From 6184287ef8d863dbf76d998de7f31afbee3f11a0 Mon Sep 17 00:00:00 2001 From: Rudolf Polzer Date: Sun, 11 Dec 2011 20:24:28 +0100 Subject: [PATCH] refactor csqcplayers code; move frame forcing clientside --- qcsrc/client/csqcmodel_hooks.qc | 404 +++++++++++++++++++------------- qcsrc/csqcmodel/settings.qh | 2 +- qcsrc/dpdefs/csprogsdefs.qc | 185 +++++++++++++++ qcsrc/server/cl_player.qc | 16 +- qcsrc/server/g_subs.qc | 18 -- 5 files changed, 433 insertions(+), 192 deletions(-) diff --git a/qcsrc/client/csqcmodel_hooks.qc b/qcsrc/client/csqcmodel_hooks.qc index 1f324807b..fc40215a6 100644 --- a/qcsrc/client/csqcmodel_hooks.qc +++ b/qcsrc/client/csqcmodel_hooks.qc @@ -1,114 +1,76 @@ -.vector glowmod; + + +// FEATURE: LOD .float lodmodelindex0; .float lodmodelindex1; .float lodmodelindex2; - -.entity tag_entity; -.float tag_index; - -void CSQCModel_Hook_PreDraw(float isplayer) +void CSQCPlayer_LOD_Apply(void) { - // auto glowmod from colormap - if(isplayer) + // LOD model loading + if(self.lodmodelindex0 != self.modelindex) { - if(self.colormap > 0) - self.glowmod = colormapPaletteColor(((self.colormap >= 1024) ? self.colormap : stof(getplayerkeyvalue(self.colormap - 1, "colors"))) & 0x0F, TRUE) * 2; - else - self.glowmod = '1 1 1'; + string modelname = self.model; + string s; - if(self.modelindex && self.model != "null") + if(!fexists(modelname)) { - if(autocvar_cl_playerdetailreduction <= 0) - { - if(autocvar_cl_playerdetailreduction <= -2) - self.modelindex = self.lodmodelindex2; - else if(autocvar_cl_playerdetailreduction <= -1) - self.modelindex = self.lodmodelindex1; - else - self.modelindex = self.lodmodelindex0; - } - else - { - float distance = vlen(self.origin - other.origin); - float f = (distance + 100.0) * autocvar_cl_playerdetailreduction; - f *= 1.0 / bound(0.01, view_quality, 1); - if(f > autocvar_cl_loddistance2) - self.modelindex = self.lodmodelindex2; - else if(f > autocvar_cl_loddistance1) - self.modelindex = self.lodmodelindex1; - else - self.modelindex = self.lodmodelindex0; - } + print(sprintf(_("Trying to use non existing model %s. "), modelname)); + modelname = cvar_defstring("_cl_playermodel"); + print(sprintf(_("Reverted to %s."), modelname)); } - } - if(!isplayer) - { - if(self.tag_entity && wasfreed(self.tag_entity)) - self.tag_entity = world; + // set modelindex + self.lodmodelindex0 = self.modelindex; + self.lodmodelindex1 = self.modelindex; + self.lodmodelindex2 = self.modelindex; - if(self.tag_networkentity) + // FIXME: this only supports 3-letter extensions + s = strcat(substring(modelname, 0, strlen(modelname)-4), "_lod1", substring(modelname, -4, 4)); + if(fexists(s)) { - // we are ATTACHED! - if(self.tag_entity.entnum == self.tag_networkentity) - { - // already good - self.drawmask = MASK_NORMAL; - } - else - { - // to something NEW NEW NEW NEW! - self.tag_entity = findfloat(world, entnum, self.tag_networkentity); - if(self.tag_entity) - { - // the best part is: IT EXISTS - self.drawmask = MASK_NORMAL; + precache_model(s); + setmodel(self, s); + if(self.modelindex) + self.lodmodelindex1 = self.modelindex; + } - if(substring(self.model, 0, 17) == "models/weapons/v_") - if(substring(self.tag_entity.model, 0, 17) == "models/weapons/h_") - { - self.tag_index = gettagindex(self.tag_entity, "weapon"); - if(!self.tag_index) - self.tag_index = gettagindex(self.tag_entity, "tag_weapon"); - if(!self.tag_index) - { - // we need to prevent this from 'appening - self.tag_entity = world; - self.drawmask = 0; - dprint("h_ model lacks weapon attachment, but v_ model is attached to it\n"); - } - } + s = strcat(substring(modelname, 0, strlen(modelname)-4), "_lod2", substring(modelname, -4, 4)); + if(fexists(s)) + { + precache_model(s); + setmodel(self, s); + if(self.modelindex) + self.lodmodelindex2 = self.modelindex; + } - if(substring(self.model, 0, 17) == "models/weapons/v_") - if(substring(self.tag_entity.model, 0, 14) == "models/player/") - { - self.tag_index = gettagindex(self.tag_entity, "tag_weapon"); - if(!self.tag_index) - self.tag_index = gettagindex(self.tag_entity, "bip01 r hand"); - } + setmodel(self, modelname); // make everything normal again + } - if(substring(self.tag_entity.model, 0, 17) == "models/weapons/v_") - { - self.tag_index = gettagindex(self.tag_entity, "shot"); - if(!self.tag_index) - self.tag_index = gettagindex(self.tag_entity, "tag_shot"); - } - } - else - { - // damn, see you next frame - self.drawmask = 0; - } - } - } + // apply LOD + if(autocvar_cl_playerdetailreduction <= 0) + { + if(autocvar_cl_playerdetailreduction <= -2) + self.modelindex = self.lodmodelindex2; + else if(autocvar_cl_playerdetailreduction <= -1) + self.modelindex = self.lodmodelindex1; else - { - // no brain no pain - self.drawmask = MASK_NORMAL; - } + self.modelindex = self.lodmodelindex0; + } + else + { + float distance = vlen(self.origin - other.origin); + float f = (distance + 100.0) * autocvar_cl_playerdetailreduction; + f *= 1.0 / bound(0.01, view_quality, 1); + if(f > autocvar_cl_loddistance2) + self.modelindex = self.lodmodelindex2; + else if(f > autocvar_cl_loddistance1) + self.modelindex = self.lodmodelindex1; + else + self.modelindex = self.lodmodelindex0; } } +// FEATURE: forcemodel string forceplayermodels_model; float forceplayermodels_modelindex; float forceplayermodels_skin; @@ -116,96 +78,208 @@ float forceplayermodels_attempted; .string forceplayermodels_savemodel; .float forceplayermodels_savemodelindex; .float forceplayermodels_saveskin; -void CSQCModel_Hook_PreUpdate(float isplayer, float islocalplayer) +void CSQCPlayer_ForceModel_PreUpdate(void) { - if(isplayer) + self.model = self.forceplayermodels_savemodel; + self.modelindex = self.forceplayermodels_savemodelindex; + self.skin = self.forceplayermodels_saveskin; +} +void CSQCPlayer_ForceModel_PostUpdate(void) +{ + self.forceplayermodels_savemodel = self.model; + self.forceplayermodels_savemodelindex = self.modelindex; + self.forceplayermodels_saveskin = self.skin; +} +void CSQCPlayer_ForceModel_Apply(float islocalplayer) +{ + // first, try finding it from the server + if(self.forceplayermodels_savemodelindex && self.forceplayermodels_savemodel != "null") { - // revert to values from server - self.model = self.forceplayermodels_savemodel; - self.modelindex = self.forceplayermodels_savemodelindex; - self.skin = self.forceplayermodels_saveskin; + if(islocalplayer) + { + // trust server's idea of "own player model" + forceplayermodels_model = self.model; + forceplayermodels_modelindex = self.modelindex; + forceplayermodels_skin = self.skin; + forceplayermodels_attempted = 1; + } + } + + // forcemodel finding + if(!forceplayermodels_attempted) + { + // only if this failed, find it out on our own + entity e; + e = spawn(); + setmodel(e, autocvar__cl_playermodel); // this is harmless, see below + forceplayermodels_model = e.model; + forceplayermodels_modelindex = e.modelindex; + forceplayermodels_skin = autocvar__cl_playerskin; + forceplayermodels_attempted = 1; + remove(e); + } + + // apply it + if(autocvar_cl_forceplayermodels && forceplayermodels_modelindex) + { + self.model = forceplayermodels_model; + self.modelindex = forceplayermodels_modelindex; + self.skin = forceplayermodels_skin; } } -void CSQCModel_Hook_PostUpdate(float isplayer, float islocalplayer) +// FEATURE: fallback frames +.float csqcmodel_saveframe; +.float csqcmodel_saveframe2; +.float csqcmodel_saveframe3; +.float csqcmodel_saveframe4; +.float csqcmodel_framecount; +void CSQCPlayer_FallbackFrame_PreUpdate(void) { - if(isplayer) + self.frame = self.csqcmodel_saveframe; + self.frame2 = self.csqcmodel_saveframe2; + self.frame3 = self.csqcmodel_saveframe3; + self.frame4 = self.csqcmodel_saveframe4; +} +void CSQCPlayer_FallbackFrame_PostUpdate(void) +{ + self.csqcmodel_saveframe = self.frame; + self.csqcmodel_saveframe2 = self.frame2; + self.csqcmodel_saveframe3 = self.frame3; + self.csqcmodel_saveframe4 = self.frame4; +} +float CSQCPlayer_FallbackFrame(float f) +{ + if(frameduration(self.modelindex, f) > 0) + return f; // goooooood + switch(f) { - // save values set by server - self.forceplayermodels_savemodel = self.model; - self.forceplayermodels_savemodelindex = self.modelindex; - self.forceplayermodels_saveskin = self.skin; + case 23: return 11; // anim_melee -> anim_shoot + case 24: return 4; // anim_duckwalkbackwards -> anim_duckwalk + case 25: return 4; // anim_duckwalkstrafeleft -> anim_duckwalk + case 26: return 4; // anim_duckwalkstraferight -> anim_duckwalk + case 27: return 4; // anim_duckwalkforwardright -> anim_duckwalk + case 28: return 4; // anim_duckwalkforwardleft -> anim_duckwalk + case 29: return 4; // anim_duckwalkbackright -> anim_duckwalk + case 30: return 4; // anim_duckwalkbackleft -> anim_duckwalk + } + print(sprintf("Frame %d missing in model %s, and we have no fallback - FAIL!\n", f, self.model)); + return f; +} +void CSQCPlayer_FallbackFrame_Apply(void) +{ + self.frame = CSQCPlayer_FallbackFrame(self.frame); + self.frame2 = CSQCPlayer_FallbackFrame(self.frame2); + self.frame3 = CSQCPlayer_FallbackFrame(self.frame3); + self.frame4 = CSQCPlayer_FallbackFrame(self.frame4); +} - if(self.modelindex && self.model != "null") - { - if(islocalplayer) - { - // trust server's idea of "own player model" - forceplayermodels_model = self.model; - forceplayermodels_modelindex = self.modelindex; - forceplayermodels_skin = self.skin; - forceplayermodels_attempted = 1; - } +// FEATURE: auto glowmod +.vector glowmod; +void CSQCPlayer_GlowMod_Apply(void) +{ + if(self.colormap > 0) + self.glowmod = colormapPaletteColor(((self.colormap >= 1024) ? self.colormap : stof(getplayerkeyvalue(self.colormap - 1, "colors"))) & 0x0F, TRUE) * 2; + else + self.glowmod = '1 1 1'; +} - if(!forceplayermodels_attempted) - { - // only if this failed, find it out on our own - entity e; - e = spawn(); - setmodel(e, autocvar__cl_playermodel); // this is harmless, see below - forceplayermodels_model = e.model; - forceplayermodels_modelindex = e.modelindex; - forceplayermodels_skin = autocvar__cl_playerskin; - forceplayermodels_attempted = 1; - remove(e); - } +// FEATURE: auto tag_index +.entity tag_entity; +.float tag_index; +void CSQCModel_AutoTagIndex_Apply(void) +{ + if(self.tag_entity && wasfreed(self.tag_entity)) + self.tag_entity = world; - if(autocvar_cl_forceplayermodels && forceplayermodels_modelindex) + if(self.tag_networkentity) + { + // we are ATTACHED! + if(self.tag_entity.entnum != self.tag_networkentity) + { + // to something NEW NEW NEW NEW! + self.tag_entity = findfloat(world, entnum, self.tag_networkentity); + if(self.tag_entity) { - self.model = forceplayermodels_model; - self.modelindex = forceplayermodels_modelindex; - self.skin = forceplayermodels_skin; - } + // the best part is: IT EXISTS + if(substring(self.model, 0, 17) == "models/weapons/v_") + if(substring(self.tag_entity.model, 0, 17) == "models/weapons/h_") + { + self.tag_index = gettagindex(self.tag_entity, "weapon"); + if(!self.tag_index) + self.tag_index = gettagindex(self.tag_entity, "tag_weapon"); + if(!self.tag_index) + { + // we need to prevent this from 'appening + self.tag_entity = world; + self.drawmask = 0; + dprint("h_ model lacks weapon attachment, but v_ model is attached to it\n"); + } + } - // LOD model loading - if(self.lodmodelindex0 != self.modelindex) - { - string modelname = self.model; - string s; + if(substring(self.model, 0, 17) == "models/weapons/v_") + if(substring(self.tag_entity.model, 0, 14) == "models/player/") + { + self.tag_index = gettagindex(self.tag_entity, "tag_weapon"); + if(!self.tag_index) + self.tag_index = gettagindex(self.tag_entity, "bip01 r hand"); + } - if(!fexists(modelname)) + if(substring(self.tag_entity.model, 0, 17) == "models/weapons/v_") { - print(sprintf(_("Trying to use non existing model %s. "), modelname)); - modelname = cvar_defstring("_cl_playermodel"); - print(sprintf(_("Reverted to %s."), modelname)); + self.tag_index = gettagindex(self.tag_entity, "shot"); + if(!self.tag_index) + self.tag_index = gettagindex(self.tag_entity, "tag_shot"); } + } + else + { + // damn, see you next frame + self.drawmask = 0; + } + } + } +} - // set modelindex - self.lodmodelindex0 = self.modelindex; - self.lodmodelindex1 = self.modelindex; - self.lodmodelindex2 = self.modelindex; +// general functions +void CSQCModel_Hook_PreDraw(float isplayer, float islocalplayer) +{ + if(!self.modelindex || self.model == "null") + { + self.drawmask = 0; + return; + } + else + self.drawmask = MASK_NORMAL; - // FIXME: this only supports 3-letter extensions - s = strcat(substring(modelname, 0, strlen(modelname)-4), "_lod1", substring(modelname, -4, 4)); - if(fexists(s)) - { - precache_model(s); - setmodel(self, s); - if(self.modelindex) - self.lodmodelindex1 = self.modelindex; - } + if(isplayer) + { + CSQCPlayer_GlowMod_Apply(); + CSQCPlayer_ForceModel_Apply(islocalplayer); + CSQCPlayer_LOD_Apply(); + CSQCPlayer_FallbackFrame_Apply(); + } - s = strcat(substring(modelname, 0, strlen(modelname)-4), "_lod2", substring(modelname, -4, 4)); - if(fexists(s)) - { - precache_model(s); - setmodel(self, s); - if(self.modelindex) - self.lodmodelindex2 = self.modelindex; - } + if(!isplayer) + CSQCModel_AutoTagIndex_Apply(); +} - setmodel(self, modelname); // make everything normal again - } - } +void CSQCModel_Hook_PreUpdate(float isplayer, float islocalplayer) +{ + if(isplayer) + { + // revert to values from server + CSQCPlayer_ForceModel_PreUpdate(); + CSQCPlayer_FallbackFrame_PreUpdate(); + } +} + +void CSQCModel_Hook_PostUpdate(float isplayer, float islocalplayer) +{ + if(isplayer) + { + // save values set by server + CSQCPlayer_ForceModel_PostUpdate(); + CSQCPlayer_FallbackFrame_PostUpdate(); } } diff --git a/qcsrc/csqcmodel/settings.qh b/qcsrc/csqcmodel/settings.qh index a5f9c8733..a91385aff 100644 --- a/qcsrc/csqcmodel/settings.qh +++ b/qcsrc/csqcmodel/settings.qh @@ -37,7 +37,7 @@ #define CSQCMODEL_HOOK_POSTUPDATE \ CSQCModel_Hook_PostUpdate(isplayer, islocalplayer); #define CSQCMODEL_HOOK_PREDRAW \ - CSQCModel_Hook_PreDraw(isplayer); + CSQCModel_Hook_PreDraw(isplayer, islocalplayer); #define CSQCPLAYER_HOOK_POSTCAMERASETUP // mod must define: diff --git a/qcsrc/dpdefs/csprogsdefs.qc b/qcsrc/dpdefs/csprogsdefs.qc index bb5f2137f..3ebb09ad3 100644 --- a/qcsrc/dpdefs/csprogsdefs.qc +++ b/qcsrc/dpdefs/csprogsdefs.qc @@ -1204,6 +1204,191 @@ float log(float f) = #532; //description: //logarithm +//FTE_CSQC_SKELETONOBJECTS +//idea: Spike, LordHavoc +//darkplaces implementation: LordHavoc +//builtin definitions: +// all skeleton numbers are 1-based (0 being no skeleton) +// all bone numbers are 1-based (0 being invalid) +float(float modlindex) skel_create = #263; // create a skeleton (be sure to assign this value into .skeletonindex for use), returns skeleton index (1 or higher) on success, returns 0 on failure (for example if the modelindex is not skeletal), it is recommended that you create a new skeleton if you change modelindex, as the skeleton uses the hierarchy from the model. +float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // blend in a percentage of standard animation, 0 replaces entirely, 1 does nothing, 0.5 blends half, etc, and this only alters the bones in the specified range for which out of bounds values like 0,100000 are safe (uses .frame, .frame2, .frame3, .frame4, .lerpfrac, .lerpfrac3, .lerpfrac4, .frame1time, .frame2time, .frame3time, .frame4time), returns skel on success, 0 on failure +float(float skel) skel_get_numbones = #265; // returns how many bones exist in the created skeleton, 0 if skeleton does not exist +string(float skel, float bonenum) skel_get_bonename = #266; // returns name of bone (as a tempstring), "" if invalid bonenum (< 1 for example) or skeleton does not exist +float(float skel, float bonenum) skel_get_boneparent = #267; // returns parent num for supplied bonenum, 0 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this) +float(float skel, string tagname) skel_find_bone = #268; // get number of bone with specified name, 0 on failure, bonenum (1-based) on success, same as using gettagindex but takes modelindex instead of entity +vector(float skel, float bonenum) skel_get_bonerel = #269; // get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone) +vector(float skel, float bonenum) skel_get_boneabs = #270; // get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity) +void(float skel, float bonenum, vector org) skel_set_bone = #271; // set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone) +void(float skel, float bonenum, vector org) skel_mul_bone = #272; // transform bone matrix (relative to its parent) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bone) +void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // transform bone matrices (relative to their parents) by the supplied matrix in v_forward, v_right, v_up, takes origin as parameter (relative to parent bones) +void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse +void(float skel) skel_delete = #275; // deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work) +float(float modlindex, string framename) frameforname = #276; // finds number of a specified frame in the animation, returns -1 if no match found +float(float modlindex, float framenum) frameduration = #277; // returns the intended play time (in seconds) of the specified framegroup, if it does not exist the result is 0, if it is a single frame it may be a small value around 0.1 or 0. +//fields: +.float skeletonindex; // active skeleton overriding standard animation on model +.float frame; // primary framegroup animation (strength = 1 - lerpfrac - lerpfrac3 - lerpfrac4) +.float frame2; // secondary framegroup animation (strength = lerpfrac) +.float frame3; // tertiary framegroup animation (strength = lerpfrac3) +.float frame4; // quaternary framegroup animation (strength = lerpfrac4) +.float lerpfrac; // strength of framegroup blend +.float lerpfrac3; // strength of framegroup blend +.float lerpfrac4; // strength of framegroup blend +.float frame1time; // start time of framegroup animation +.float frame2time; // start time of framegroup animation +.float frame3time; // start time of framegroup animation +.float frame4time; // start time of framegroup animation +//description: +//this extension provides a way to do complex skeletal animation on an entity. +// +//see also DP_SKELETONOBJECTS (this extension implemented on server as well as client) +// +//notes: +//each model contains its own skeleton, reusing a skeleton with incompatible models will yield garbage (or not render). +//each model contains its own animation data, you can use animations from other model files (for example saving out all character animations as separate model files). +//if an engine supports loading an animation-only file format such as .md5anim in FTEQW, it can be used to animate any model with a compatible skeleton. +//proper use of this extension may require understanding matrix transforms (v_forward, v_right, v_up, origin), and you must keep in mind that v_right is negative for this purpose. +// +//features include: +//multiple animations blended together. +//animating a model with animations from another model with a compatible skeleton. +//restricting animation blends to certain bones of a model - for example independent animation of legs, torso, head. +//custom bone controllers - for example making eyes track a target location. +// +// +// +//example code follows... +// +//this helper function lets you identify (by parentage) what group a bone +//belongs to - for example "torso", "leftarm", would return 1 ("torso") for +//all children of the bone named "torso", unless they are children of +//"leftarm" (which is a child of "torso") which would return 2 instead... +float(float skel, float bonenum, string g1, string g2, string g3, string g4, string g5, string g6) example_skel_findbonegroup = +{ + local string bonename; + while (bonenum >= 0) + { + bonename = skel_get_bonename(skel, bonenum); + if (bonename == g1) return 1; + if (bonename == g2) return 2; + if (bonename == g3) return 3; + if (bonename == g4) return 4; + if (bonename == g5) return 5; + if (bonename == g6) return 6; + bonenum = skel_get_boneparent(skel, bonenum); + } + return 0; +}; +// create a skeletonindex for our player using current modelindex +void() example_skel_player_setup = +{ + self.skeletonindex = skel_create(self.modelindex); +}; +// setup bones of skeleton based on an animation +// note: animmodelindex can be a different model than self.modelindex +void(float animmodelindex, float framegroup, float framegroupstarttime) example_skel_player_update_begin = +{ + // start with our standard animation + self.frame = framegroup; + self.frame2 = 0; + self.frame3 = 0; + self.frame4 = 0; + self.frame1time = framegroupstarttime; + self.frame2time = 0; + self.frame3time = 0; + self.frame4time = 0; + self.lerpfrac = 0; + self.lerpfrac3 = 0; + self.lerpfrac4 = 0; + skel_build(self.skeletonindex, self, animmodelindex, 0, 0, 100000); +}; +// apply a different framegroup animation to bones with a specified parent +void(float animmodelindex, float framegroup, float framegroupstarttime, float blendalpha, string groupbonename, string excludegroupname1, string excludegroupname2) example_skel_player_update_applyoverride = +{ + local float bonenum; + local float numbones; + self.frame = framegroup; + self.frame2 = 0; + self.frame3 = 0; + self.frame4 = 0; + self.frame1time = framegroupstarttime; + self.frame2time = 0; + self.frame3time = 0; + self.frame4time = 0; + self.lerpfrac = 0; + self.lerpfrac3 = 0; + self.lerpfrac4 = 0; + bonenum = 0; + numbones = skel_get_numbones(self.skeletonindex); + while (bonenum < numbones) + { + if (example_skel_findbonegroup(self.skeletonindex, bonenum, groupbonename, excludegroupname1, excludegroupname2, "", "", "") == 1) + skel_build(self.skeletonindex, self, animmodelindex, 1 - blendalpha, bonenum, bonenum + 1); + bonenum = bonenum + 1; + } +}; +// make eyes point at a target location, be sure v_forward, v_right, v_up are set correctly before calling +void(vector eyetarget, string bonename) example_skel_player_update_eyetarget = +{ + local float bonenum; + local vector ang; + local vector oldforward, oldright, oldup; + local vector relforward, relright, relup, relorg; + local vector boneforward, boneright, boneup, boneorg; + local vector parentforward, parentright, parentup, parentorg; + local vector u, v; + local vector modeleyetarget; + bonenum = skel_find_bone(self.skeletonindex, bonename) - 1; + if (bonenum < 0) + return; + oldforward = v_forward; + oldright = v_right; + oldup = v_up; + v = eyetarget - self.origin; + modeleyetarget_x = v * v_forward; + modeleyetarget_y = 0-v * v_right; + modeleyetarget_z = v * v_up; + // this is an eyeball, make it point at the target location + // first get all the data we can... + relorg = skel_get_bonerel(self.skeletonindex, bonenum); + relforward = v_forward; + relright = v_right; + relup = v_up; + boneorg = skel_get_boneabs(self.skeletonindex, bonenum); + boneforward = v_forward; + boneright = v_right; + boneup = v_up; + parentorg = skel_get_boneabs(self.skeletonindex, skel_get_boneparent(self.skeletonindex, bonenum)); + parentforward = v_forward; + parentright = v_right; + parentup = v_up; + // get the vector from the eyeball to the target + u = modeleyetarget - boneorg; + // now transform it inversely by the parent matrix to produce new rel vectors + v_x = u * parentforward; + v_y = u * parentright; + v_z = u * parentup; + ang = vectoangles2(v, relup); + ang_x = 0 - ang_x; + makevectors(ang); + // set the relative bone matrix + skel_set_bone(self.skeletonindex, bonenum, relorg); + // restore caller's v_ vectors + v_forward = oldforward; + v_right = oldright; + v_up = oldup; +}; +// delete skeleton when we're done with it +// note: skeleton remains valid until next frame when it is really deleted +void() example_skel_player_delete = +{ + skel_delete(self.skeletonindex); + self.skeletonindex = 0; +}; +// +// END OF EXAMPLES FOR FTE_CSQC_SKELETONOBJECTS +// + // assorted builtins const float STAT_MOVEVARS_TICRATE = 240; const float STAT_MOVEVARS_TIMESCALE = 241; diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc index 0b1576eed..8ff9fe7b2 100644 --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@ -225,14 +225,14 @@ void player_setupanimsformodel() self.anim_forwardleft = animfixfps(self, '20 1 1'); self.anim_backright = animfixfps(self, '21 1 1'); self.anim_backleft = animfixfps(self, '22 1 1'); - self.anim_melee = animfixfps2(self, '23 1 1', '11 1 5'); - self.anim_duckwalkbackwards = animfixfps2(self, '24 1 1', '4 1 1'); - self.anim_duckwalkstrafeleft = animfixfps2(self, '25 1 1', '4 1 1'); - self.anim_duckwalkstraferight = animfixfps2(self, '26 1 1', '4 1 1'); - self.anim_duckwalkforwardright = animfixfps2(self, '27 1 1', '4 1 1'); - self.anim_duckwalkforwardleft = animfixfps2(self, '28 1 1', '4 1 1'); - self.anim_duckwalkbackright = animfixfps2(self, '29 1 1', '4 1 1'); - self.anim_duckwalkbackleft = animfixfps2(self, '30 1 1', '4 1 1'); + self.anim_melee = animfixfps(self, '23 1 1'); + self.anim_duckwalkbackwards = animfixfps(self, '24 1 1'); + self.anim_duckwalkstrafeleft = animfixfps(self, '25 1 1'); + self.anim_duckwalkstraferight = animfixfps(self, '26 1 1'); + self.anim_duckwalkforwardright = animfixfps(self, '27 1 1'); + self.anim_duckwalkforwardleft = animfixfps(self, '28 1 1'); + self.anim_duckwalkbackright = animfixfps(self, '29 1 1'); + self.anim_duckwalkbackleft = animfixfps(self, '30 1 1'); // TODO introspect models for finding right "fps" value (1/duration) // reset animstate now setanim(self, self.anim_idle, TRUE, FALSE, TRUE); diff --git a/qcsrc/server/g_subs.qc b/qcsrc/server/g_subs.qc index a331da4f0..6a62d61b5 100644 --- a/qcsrc/server/g_subs.qc +++ b/qcsrc/server/g_subs.qc @@ -71,24 +71,6 @@ vector animfixfps(entity e, vector a) return a; } -vector animfixfps2(entity e, vector a, vector b) -{ - // multi-frame anim: keep as-is - float dur; - dur = frameduration(e.modelindex, a_x); - if(dur <= 0) - { - a = b; - dur = frameduration(e.modelindex, a_x); - } - if(a_y == 1) - { - if(dur > 0) - a_z = 1.0 / dur; - } - return a; -} - /* ================== SUB_Remove -- 2.39.2