]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
implemented FTE_CSQC_SKELETONOBJECTS (clientside support)
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 22 Nov 2009 21:06:10 +0000 (21:06 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 22 Nov 2009 21:06:10 +0000 (21:06 +0000)
implemented DP_SKELETONOBJECTS (serverside support)
reworked all tag queries to support animation lerp and skeletonindex
MOVE_HITMODEL / SOLID_BSP now support animation lerp and skeletonindex
server qc now has identical animation fields to that of csqc (engine
networking still only networks .frame however)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9510 d7cf8633-e32d-0410-b094-e92efae38249

27 files changed:
cl_collision.c
cl_main.c
cl_parse.c
client.h
clvm_cmds.c
collision.c
collision.h
csprogs.c
csprogs.h
gl_rmain.c
model_alias.c
model_brush.c
model_shared.h
progs.h
progsvm.h
protocol.c
prvm_cmds.c
prvm_edict.c
r_lerpanim.c
r_lerpanim.h
render.h
server.h
snd_main.c
sv_main.c
sv_phys.c
svvm_cmds.c
world.c

index 9ef311b12e7db318b6db3457d8fe72e83c418fef..f4744885794fd2a0a141d7cff21d3c0f83605766 100644 (file)
@@ -37,7 +37,7 @@ float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, ve
        if (hitent)
                *hitent = 0;
        if (cl.worldmodel && cl.worldmodel->TraceLine)
-               cl.worldmodel->TraceLine(cl.worldmodel, 0, &trace, start, end, SUPERCONTENTS_SOLID);
+               cl.worldmodel->TraceLine(cl.worldmodel, NULL, NULL, &trace, start, end, SUPERCONTENTS_SOLID);
 
        if (normal)
                VectorCopy(trace.plane.normal, normal);
@@ -78,7 +78,7 @@ float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, ve
                if (maxrealfrac < trace.realfraction)
                        continue;
 
-               ent->model->TraceLine(ent->model, ent->frameblend[0].subframe, &trace, starttransformed, endtransformed, SUPERCONTENTS_SOLID);
+               ent->model->TraceLine(ent->model, ent->frameblend, ent->skeleton, &trace, starttransformed, endtransformed, SUPERCONTENTS_SOLID);
 
                if (maxrealfrac > trace.realfraction)
                {
@@ -308,7 +308,7 @@ trace_t CL_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int
                        entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
                        if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
                                continue;
-                       Collision_ClipPointToGenericEntity(&trace, ent->model, ent->frameblend[0].subframe, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, hitsupercontentsmask);
+                       Collision_ClipPointToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, hitsupercontentsmask);
                        if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
                                *hitnetworkentity = cl.brushmodel_entities[i];
                        Collision_CombineTraces(&cliptrace, &trace, NULL, true);
@@ -354,7 +354,7 @@ trace_t CL_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int
                                continue;
                        Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
                        Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
-                       Collision_ClipPointToGenericEntity(&trace, NULL, 0, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, hitsupercontentsmask);
+                       Collision_ClipPointToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, hitsupercontentsmask);
                        if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
                                *hitnetworkentity = i;
                        Collision_CombineTraces(&cliptrace, &trace, NULL, false);
@@ -409,21 +409,16 @@ skipnetworkplayers:
                // might interact, so do an exact clip
                model = NULL;
                if ((int) touch->fields.client->solid == SOLID_BSP || type == MOVE_HITMODEL)
-               {
-                       unsigned int modelindex = (unsigned int)touch->fields.client->modelindex;
-                       // if the modelindex is 0, it shouldn't be SOLID_BSP!
-                       if (modelindex > 0 && modelindex < MAX_MODELS)
-                               model = cl.model_precache[(int)touch->fields.client->modelindex];
-               }
+                       model = CL_GetModelFromEdict(touch);
                if (model)
                        Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.client->origin[0], touch->fields.client->origin[1], touch->fields.client->origin[2], touch->fields.client->angles[0], touch->fields.client->angles[1], touch->fields.client->angles[2], 1);
                else
                        Matrix4x4_CreateTranslate(&matrix, touch->fields.client->origin[0], touch->fields.client->origin[1], touch->fields.client->origin[2]);
                Matrix4x4_Invert_Simple(&imatrix, &matrix);
                if ((int)touch->fields.client->flags & FL_MONSTER)
-                       Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.client->frame, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
                else
-                       Collision_ClipPointToGenericEntity(&trace, model, (int) touch->fields.client->frame, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
+                       Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
 
                if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
                        *hitnetworkentity = 0;
@@ -543,7 +538,7 @@ trace_t CL_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_
                        entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
                        if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
                                continue;
-                       Collision_ClipLineToGenericEntity(&trace, ent->model, ent->frameblend[0].subframe, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask);
+                       Collision_ClipLineToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, end, hitsupercontentsmask);
                        if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
                                *hitnetworkentity = cl.brushmodel_entities[i];
                        Collision_CombineTraces(&cliptrace, &trace, NULL, true);
@@ -589,7 +584,7 @@ trace_t CL_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_
                                continue;
                        Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
                        Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
-                       Collision_ClipLineToGenericEntity(&trace, NULL, 0, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, end, hitsupercontentsmask);
+                       Collision_ClipLineToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, end, hitsupercontentsmask);
                        if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
                                *hitnetworkentity = i;
                        Collision_CombineTraces(&cliptrace, &trace, NULL, false);
@@ -644,21 +639,16 @@ skipnetworkplayers:
                // might interact, so do an exact clip
                model = NULL;
                if ((int) touch->fields.client->solid == SOLID_BSP || type == MOVE_HITMODEL)
-               {
-                       unsigned int modelindex = (unsigned int)touch->fields.client->modelindex;
-                       // if the modelindex is 0, it shouldn't be SOLID_BSP!
-                       if (modelindex > 0 && modelindex < MAX_MODELS)
-                               model = cl.model_precache[(int)touch->fields.client->modelindex];
-               }
+                       model = CL_GetModelFromEdict(touch);
                if (model)
                        Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.client->origin[0], touch->fields.client->origin[1], touch->fields.client->origin[2], touch->fields.client->angles[0], touch->fields.client->angles[1], touch->fields.client->angles[2], 1);
                else
                        Matrix4x4_CreateTranslate(&matrix, touch->fields.client->origin[0], touch->fields.client->origin[1], touch->fields.client->origin[2]);
                Matrix4x4_Invert_Simple(&imatrix, &matrix);
                if (type == MOVE_MISSILE && (int)touch->fields.client->flags & FL_MONSTER)
-                       Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.client->frame, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
                else
-                       Collision_ClipLineToGenericEntity(&trace, model, (int) touch->fields.client->frame, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
+                       Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
 
                if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
                        *hitnetworkentity = 0;
@@ -809,7 +799,7 @@ trace_t CL_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, co
                        entity_render_t *ent = &cl.entities[cl.brushmodel_entities[i]].render;
                        if (!BoxesOverlap(clipboxmins, clipboxmaxs, ent->mins, ent->maxs))
                                continue;
-                       Collision_ClipToGenericEntity(&trace, ent->model, ent->frameblend[0].subframe, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, mins, maxs, end, hitsupercontentsmask);
+                       Collision_ClipToGenericEntity(&trace, ent->model, ent->frameblend, ent->skeleton, vec3_origin, vec3_origin, 0, &ent->matrix, &ent->inversematrix, start, mins, maxs, end, hitsupercontentsmask);
                        if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
                                *hitnetworkentity = cl.brushmodel_entities[i];
                        Collision_CombineTraces(&cliptrace, &trace, NULL, true);
@@ -855,7 +845,7 @@ trace_t CL_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, co
                                continue;
                        Matrix4x4_CreateTranslate(&entmatrix, origin[0], origin[1], origin[2]);
                        Matrix4x4_CreateTranslate(&entinversematrix, -origin[0], -origin[1], -origin[2]);
-                       Collision_ClipToGenericEntity(&trace, NULL, 0, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, mins, maxs, end, hitsupercontentsmask);
+                       Collision_ClipToGenericEntity(&trace, NULL, NULL, NULL, cl.playerstandmins, cl.playerstandmaxs, SUPERCONTENTS_BODY, &entmatrix, &entinversematrix, start, mins, maxs, end, hitsupercontentsmask);
                        if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
                                *hitnetworkentity = i;
                        Collision_CombineTraces(&cliptrace, &trace, NULL, false);
@@ -910,21 +900,16 @@ skipnetworkplayers:
                // might interact, so do an exact clip
                model = NULL;
                if ((int) touch->fields.client->solid == SOLID_BSP || type == MOVE_HITMODEL)
-               {
-                       unsigned int modelindex = (unsigned int)touch->fields.client->modelindex;
-                       // if the modelindex is 0, it shouldn't be SOLID_BSP!
-                       if (modelindex > 0 && modelindex < MAX_MODELS)
-                               model = cl.model_precache[(int)touch->fields.client->modelindex];
-               }
+                       model = CL_GetModelFromEdict(touch);
                if (model)
                        Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.client->origin[0], touch->fields.client->origin[1], touch->fields.client->origin[2], touch->fields.client->angles[0], touch->fields.client->angles[1], touch->fields.client->angles[2], 1);
                else
                        Matrix4x4_CreateTranslate(&matrix, touch->fields.client->origin[0], touch->fields.client->origin[1], touch->fields.client->origin[2]);
                Matrix4x4_Invert_Simple(&imatrix, &matrix);
                if ((int)touch->fields.client->flags & FL_MONSTER)
-                       Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.client->frame, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
                else
-                       Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.client->frame, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.client->mins, touch->fields.client->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
 
                if (cliptrace.realfraction > trace.realfraction && hitnetworkentity)
                        *hitnetworkentity = 0;
index 1e3eed5a5e8afe0c4e60110bbff0a412dbc93e49..ef8ab01cd55361f94e9a5a73c2af692de5bd3f19 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -481,17 +481,19 @@ List information on all models in the client modelindex
 */
 static void CL_ModelIndexList_f(void)
 {
-       int i = 1;
+       int i;
+       dp_model_t *model;
 
        // Print Header
        Con_Printf("%3s: %-30s %-8s %-8s\n", "ID", "Name", "Type", "Triangles");
 
-       while(cl.model_precache[i] && i != MAX_MODELS)
-       { // Valid Model
-               if(cl.model_precache[i]->loaded || i == 1)
-                       Con_Printf("%3i: %-30s %-8s %-10i\n", i, cl.model_precache[i]->name, cl.model_precache[i]->modeldatatypestring, cl.model_precache[i]->surfmesh.num_triangles);
+       for (i = -MAX_MODELS;i < MAX_MODELS;i++)
+       {
+               model = CL_GetModelByIndex(i);
+               if(model->loaded || i == 1)
+                       Con_Printf("%3i: %-30s %-8s %-10i\n", i, model->name, model->modeldatatypestring, model->surfmesh.num_triangles);
                else
-                       Con_Printf("%3i: %-30s %-30s\n", i, cl.model_precache[i]->name, "--no local model found--");
+                       Con_Printf("%3i: %-30s %-30s\n", i, model->name, "--no local model found--");
                i++;
        }
 }
@@ -529,7 +531,7 @@ void CL_UpdateRenderEntity(entity_render_t *ent)
        // update the inverse matrix for the renderer
        Matrix4x4_Invert_Simple(&ent->inversematrix, &ent->matrix);
        // update the animation blend state
-       R_LerpAnimation(ent);
+       VM_FrameBlendFromFrameGroupBlend(ent->frameblend, ent->framegroupblend, ent->model);
        // we need the matrix origin to center the box
        Matrix4x4_OriginFromMatrix(&ent->matrix, org);
        // update entity->render.scale because the renderer needs it
@@ -854,7 +856,7 @@ void CL_AddQWCTFFlagModel(entity_t *player, int skin)
        if (!flagrender)
                return;
 
-       flagrender->model = cl.model_precache[cl.qw_modelindex_flag];
+       flagrender->model = CL_GetModelByIndex(cl.qw_modelindex_flag);
        flagrender->skinnum = skin;
        flagrender->alpha = 1;
        VectorSet(flagrender->colormod, 1, 1, 1);
@@ -943,7 +945,7 @@ void CL_UpdateNetworkEntity(entity_t *e, int recursionlimit, qboolean interpolat
                // FIXME: use a model function to get tag info (need to handle skeletal)
                if (e->state_current.tagentity && e->state_current.tagindex >= 1 && t->render.model)
                {
-                       if(!CL_BlendTagMatrix(&t->render, e->state_current.tagindex - 1, &blendmatrix)) // i.e. no error
+                       if(!Mod_Alias_GetTagMatrix(t->render.model, t->render.frameblend, t->render.skeleton, e->state_current.tagindex - 1, &blendmatrix)) // i.e. no error
                        {
                                // concat the tag matrices onto the entity matrix
                                Matrix4x4_Concat(&tempmatrix, &t->render.matrix, &blendmatrix);
@@ -1011,10 +1013,7 @@ void CL_UpdateNetworkEntity(entity_t *e, int recursionlimit, qboolean interpolat
 
        // model setup and some modelflags
        frame = e->state_current.frame;
-       if (e->state_current.modelindex < MAX_MODELS)
-               e->render.model = cl.model_precache[e->state_current.modelindex];
-       else
-               e->render.model = NULL;
+       e->render.model = CL_GetModelByIndex(e->state_current.modelindex);
        if (e->render.model)
        {
                if (e->render.skinnum >= e->render.model->numskins)
@@ -1523,7 +1522,7 @@ static void CL_RelinkStaticEntities(void)
                e->render.flags = 0;
                // if the model was not loaded when the static entity was created we
                // need to re-fetch the model pointer
-               e->render.model = cl.model_precache[e->state_baseline.modelindex];
+               e->render.model = CL_GetModelByIndex(e->state_baseline.modelindex);
                // either fullbright or lit
                if(!r_fullbright.integer)
                {
@@ -1537,7 +1536,7 @@ static void CL_RelinkStaticEntities(void)
                        e->render.flags |= RENDER_SHADOW;
                VectorSet(e->render.colormod, 1, 1, 1);
                VectorSet(e->render.glowmod, 1, 1, 1);
-               R_LerpAnimation(&e->render);
+               VM_FrameBlendFromFrameGroupBlend(e->render.frameblend, e->render.framegroupblend, e->render.model);
                e->render.allowdecals = true;
                CL_UpdateRenderEntity(&e->render);
                r_refdef.scene.entities[r_refdef.scene.numentities++] = &e->render;
@@ -1618,10 +1617,7 @@ static void CL_RelinkEffects(void)
                                }
 
                                // normal stuff
-                               if(e->modelindex < MAX_MODELS)
-                                       entrender->model = cl.model_precache[e->modelindex];
-                               else
-                                       entrender->model = cl.csqc_model_precache[-(e->modelindex+1)];
+                               entrender->model = CL_GetModelByIndex(e->modelindex);
                                entrender->alpha = 1;
                                VectorSet(entrender->colormod, 1, 1, 1);
                                VectorSet(entrender->glowmod, 1, 1, 1);
@@ -1768,7 +1764,7 @@ static void CL_RelinkQWNails(void)
                        continue;
 
                // normal stuff
-               entrender->model = cl.model_precache[cl.qw_modelindex_spike];
+               entrender->model = CL_GetModelByIndex(cl.qw_modelindex_spike);
                entrender->alpha = 1;
                VectorSet(entrender->colormod, 1, 1, 1);
                VectorSet(entrender->glowmod, 1, 1, 1);
index f9039d03cf016842f7fe7d3b926f14f4bbdb2cb3..44991042fc33fc9fbdbe6db876cc5b91db0f3ded 100644 (file)
@@ -426,7 +426,7 @@ static const vec3_t defaultmaxs = {4096, 4096, 4096};
 static void CL_SetupWorldModel(void)
 {
        // update the world model
-       cl.entities[0].render.model = cl.worldmodel = cl.model_precache[1];
+       cl.entities[0].render.model = cl.worldmodel = CL_GetModelByIndex(1);
        CL_UpdateRenderEntity(&cl.entities[0].render);
 
        // set up csqc world for collision culling
@@ -1846,7 +1846,7 @@ void CL_ValidateState(entity_state_t *s)
        if (!(s->flags & RENDER_COLORMAPPED) && s->colormap > cl.maxclients)
                Con_DPrintf("CL_ValidateState: colormap (%i) > cl.maxclients (%i)\n", s->colormap, cl.maxclients);
 
-       model = cl.model_precache[s->modelindex];
+       model = CL_GetModelByIndex(s->modelindex);
        if (model && model->type && s->frame >= model->numframes)
                Con_DPrintf("CL_ValidateState: no such frame %i in \"%s\" (which has %i frames)\n", s->frame, model->name, model->numframes);
        if (model && model->type && s->skin > 0 && s->skin >= model->numskins && !(s->lightpflags & PFLAGS_FULLDYNAMIC))
@@ -2131,7 +2131,7 @@ void CL_ParseStatic (int large)
        }
 
 // copy it to the current state
-       ent->render.model = cl.model_precache[ent->state_baseline.modelindex];
+       ent->render.model = CL_GetModelByIndex(ent->state_baseline.modelindex);
        ent->render.framegroupblend[0].frame = ent->state_baseline.frame;
        ent->render.framegroupblend[0].lerp = 1;
        // make torchs play out of sync
index ceb699a5c598d39e270a124e7d82f3654b580250..e44d9b7dae39690be34e084feb5463ce897d96d9 100644 (file)
--- a/client.h
+++ b/client.h
@@ -336,6 +336,8 @@ typedef struct entity_render_s
        vec3_t mins, maxs;
        // subframe numbers (-1 if not used) and their blending scalers (0-1), if interpolation is not desired, use subframeblend[0].subframe
        frameblend_t frameblend[MAX_FRAMEBLENDS];
+       // skeletal animation data (if skeleton.relativetransforms is not NULL, it overrides frameblend)
+       skeleton_t *skeleton;
 
        // animation cache index
        int animcacheindex;
index 89bea834150ca5f2cf7e8d056e8863722a239931..e10ab54dc89a9ef8d62b7d7d88b842136ba08ea7 100644 (file)
@@ -2220,7 +2220,6 @@ int CL_GetExtendedTagInfo (prvm_edict_t *e, int tagindex, int *parentindex, cons
 {
        int r;
        dp_model_t *model;
-       int frame;
 
        *tagname = NULL;
        *parentindex = 0;
@@ -2230,11 +2229,7 @@ int CL_GetExtendedTagInfo (prvm_edict_t *e, int tagindex, int *parentindex, cons
         && (model = CL_GetModelFromEdict(e))
         && model->animscenes)
        {
-               frame = (int)e->fields.client->frame;
-               if (frame < 0 || frame >= model->numframes)
-                       frame = 0;
-
-               r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)e->fields.client->skin, model->animscenes[frame].firstframe, tagindex - 1, parentindex, tagname, tag_localmatrix);
+               r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)e->fields.client->skin, e->priv.server->frameblend, &e->priv.server->skeleton, tagindex - 1, parentindex, tagname, tag_localmatrix);
 
                if(!r) // success?
                        *parentindex += 1;
@@ -2276,27 +2271,15 @@ void CL_GetEntityMatrix (prvm_edict_t *ent, matrix4x4_t *out, qboolean viewmatri
 
 int CL_GetEntityLocalTagMatrix(prvm_edict_t *ent, int tagindex, matrix4x4_t *out)
 {
-       int frame;
-       int ret;
        dp_model_t *model;
-       entity_render_t cheatentity;
        if (tagindex >= 0
         && (model = CL_GetModelFromEdict(ent))
         && model->animscenes)
        {
-               // if model has wrong frame, engine automatically switches to model first frame
-               frame = (int)ent->fields.client->frame;
-               if (frame < 0 || frame >= model->numframes)
-                       frame = 0;
-               // now we'll do some CHEATING
-               memset(&cheatentity, 0, sizeof(cheatentity));
-               cheatentity.model = model;
-               CL_LoadFrameGroupBlend(ent, &cheatentity);
-               R_LerpAnimation(&cheatentity);
-               ret = CL_BlendTagMatrix(&cheatentity, tagindex, out);
-               if(ret)
-                       *out = identitymatrix;
-               return ret;
+               VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
+               VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
+               VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
+               return Mod_Alias_GetTagMatrix(model, ent->priv.server->frameblend, &ent->priv.server->skeleton, tagindex, out);
        }
        *out = identitymatrix;
        return 0;
@@ -2395,7 +2378,7 @@ void VM_CL_gettagindex (void)
 {
        prvm_edict_t *ent;
        const char *tag_name;
-       int modelindex, tag_index;
+       int tag_index;
 
        VM_SAFEPARMCOUNT(2, VM_CL_gettagindex);
 
@@ -2403,24 +2386,23 @@ void VM_CL_gettagindex (void)
        tag_name = PRVM_G_STRING(OFS_PARM1);
        if (ent == prog->edicts)
        {
-               VM_Warning("gettagindex: can't affect world entity\n");
+               VM_Warning("VM_CL_gettagindex(entity #%i): can't affect world entity\n", PRVM_NUM_FOR_EDICT(ent));
                return;
        }
        if (ent->priv.server->free)
        {
-               VM_Warning("gettagindex: can't affect free entity\n");
+               VM_Warning("VM_CL_gettagindex(entity #%i): can't affect free entity\n", PRVM_NUM_FOR_EDICT(ent));
                return;
        }
 
-       modelindex = (int)ent->fields.client->modelindex;
        tag_index = 0;
-       if (modelindex >= MAX_MODELS || (modelindex <= -MAX_MODELS /* client models */))
-               Con_DPrintf("gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
+       if (!CL_GetModelFromEdict(ent))
+               Con_DPrintf("VM_CL_gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
        else
        {
                tag_index = CL_GetTagIndex(ent, tag_name);
                if (tag_index == 0)
-                       Con_DPrintf("gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
+                       Con_Printf("VM_CL_gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
        }
        PRVM_G_FLOAT(OFS_RETURN) = tag_index;
 }
@@ -2437,6 +2419,7 @@ void VM_CL_gettaginfo (void)
        int returncode;
        prvm_eval_t *val;
        vec3_t fo, le, up, trans;
+       const dp_model_t *model;
 
        VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo);
 
@@ -2445,6 +2428,10 @@ void VM_CL_gettaginfo (void)
        returncode = CL_GetTagMatrix(&tag_matrix, e, tagindex);
        Matrix4x4_ToVectors(&tag_matrix, prog->globals.client->v_forward, le, prog->globals.client->v_up, PRVM_G_VECTOR(OFS_RETURN));
        VectorScale(le, -1, prog->globals.client->v_right);
+       model = CL_GetModelFromEdict(e);
+       VM_GenerateFrameGroupBlend(e->priv.server->framegroupblend, e);
+       VM_FrameBlendFromFrameGroupBlend(e->priv.server->frameblend, e->priv.server->framegroupblend, model);
+       VM_UpdateEdictSkeleton(e, model, e->priv.server->frameblend);
        CL_GetExtendedTagInfo(e, tagindex, &parentindex, &tagname, &tag_localmatrix);
        Matrix4x4_ToVectors(&tag_localmatrix, fo, le, up, trans);
 
@@ -3645,6 +3632,315 @@ static void VM_CL_checkpvs (void)
        PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, fatpvs, mi, ma);
 #endif
 }
+
+// #263 float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) 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.
+static void VM_CL_skel_create(void)
+{
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
+       dp_model_t *model = CL_GetModelByIndex(modelindex);
+       skeleton_t *skeleton;
+       int i;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (!model || !model->num_bones)
+               return;
+       for (i = 0;i < MAX_EDICTS;i++)
+               if (!prog->skeletons[i])
+                       break;
+       if (i == MAX_EDICTS)
+               return;
+       prog->skeletons[i] = skeleton = Mem_Alloc(cls.levelmempool, sizeof(skeleton_t) + model->num_bones * sizeof(matrix4x4_t));
+       skeleton->model = model;
+       skeleton->relativetransforms = (matrix4x4_t *)(skeleton+1);
+       // initialize to identity matrices
+       for (i = 0;i < skeleton->model->num_bones;i++)
+               skeleton->relativetransforms[i] = identitymatrix;
+       PRVM_G_FLOAT(OFS_RETURN) = i + 1;
+}
+
+// #264 float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) 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
+static void VM_CL_skel_build(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       skeleton_t *skeleton;
+       prvm_edict_t *ed = PRVM_G_EDICT(OFS_PARM1);
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM2);
+       float retainfrac = PRVM_G_FLOAT(OFS_PARM3);
+       int firstbone = PRVM_G_FLOAT(OFS_PARM4);
+       int lastbone = PRVM_G_FLOAT(OFS_PARM5);
+       dp_model_t *model = CL_GetModelByIndex(modelindex);
+       float blendfrac;
+       int numblends;
+       int bonenum;
+       int blendindex;
+       framegroupblend_t framegroupblend[MAX_FRAMEGROUPBLENDS];
+       frameblend_t frameblend[MAX_FRAMEBLENDS];
+       matrix4x4_t blendedmatrix;
+       matrix4x4_t matrix;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       firstbone = max(0, firstbone);
+       lastbone = min(lastbone, model->num_bones - 1);
+       lastbone = min(lastbone, skeleton->model->num_bones - 1);
+       VM_GenerateFrameGroupBlend(framegroupblend, ed);
+       VM_FrameBlendFromFrameGroupBlend(frameblend, framegroupblend, model);
+       blendfrac = 1.0f - retainfrac;
+       for (numblends = 0;numblends < MAX_FRAMEBLENDS && frameblend[numblends].lerp;numblends++)
+               frameblend[numblends].lerp *= blendfrac;
+       for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
+       {
+               memset(&blendedmatrix, 0, sizeof(blendedmatrix));
+               Matrix4x4_Accumulate(&blendedmatrix, &skeleton->relativetransforms[bonenum], retainfrac);
+               for (blendindex = 0;blendindex < numblends;blendindex++)
+               {
+                       Matrix4x4_FromArray12FloatD3D(&matrix, model->data_poses + 12 * (frameblend[blendindex].subframe * model->num_bones + bonenum));
+                       Matrix4x4_Accumulate(&blendedmatrix, &matrix, frameblend[blendindex].lerp);
+               }
+               skeleton->relativetransforms[bonenum] = blendedmatrix;
+       }
+       PRVM_G_FLOAT(OFS_RETURN) = skeletonindex;
+}
+
+// #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
+static void VM_CL_skel_get_numbones(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       skeleton_t *skeleton;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->num_bones;
+}
+
+// #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
+static void VM_CL_skel_get_bonename(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       PRVM_G_INT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(skeleton->model->data_bones[bonenum].name);
+}
+
+// #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) 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)
+static void VM_CL_skel_get_boneparent(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->data_bones[bonenum].parent + 1;
+}
+
+// #268 float(float skel, string tagname) skel_find_bone = #268; // (FTE_CSQC_SKELETONOBJECTS) get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex
+static void VM_CL_skel_find_bone(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       const char *tagname = PRVM_G_STRING(OFS_PARM1);
+       skeleton_t *skeleton;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       PRVM_G_FLOAT(OFS_RETURN) = Mod_Alias_GetTagIndexForName(skeleton->model, 0, tagname) + 1;
+}
+
+// #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
+static void VM_CL_skel_get_bonerel(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       vec3_t forward, left, up, origin;
+       VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+       VectorClear(prog->globals.client->v_forward);
+       VectorClear(prog->globals.client->v_right);
+       VectorClear(prog->globals.client->v_up);
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       matrix = skeleton->relativetransforms[bonenum];
+       Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
+       VectorCopy(forward, prog->globals.client->v_forward);
+       VectorNegate(left, prog->globals.client->v_right);
+       VectorCopy(up, prog->globals.client->v_up);
+       VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+// #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
+static void VM_CL_skel_get_boneabs(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       matrix4x4_t temp;
+       vec3_t forward, left, up, origin;
+       VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+       VectorClear(prog->globals.client->v_forward);
+       VectorClear(prog->globals.client->v_right);
+       VectorClear(prog->globals.client->v_up);
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       matrix = skeleton->relativetransforms[bonenum];
+       // convert to absolute
+       while ((bonenum = skeleton->model->data_bones[bonenum].parent) >= 0)
+       {
+               temp = matrix;
+               Matrix4x4_Concat(&matrix, &skeleton->relativetransforms[bonenum], &temp);
+       }
+       Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
+       VectorCopy(forward, prog->globals.client->v_forward);
+       VectorNegate(left, prog->globals.client->v_right);
+       VectorCopy(up, prog->globals.client->v_up);
+       VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+// #271 void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
+static void VM_CL_skel_set_bone(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       vec3_t forward, left, up, origin;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       VectorCopy(prog->globals.client->v_forward, forward);
+       VectorNegate(prog->globals.client->v_right, left);
+       VectorCopy(prog->globals.client->v_up, up);
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
+       Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
+       skeleton->relativetransforms[bonenum] = matrix;
+}
+
+// #272 void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) 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)
+static void VM_CL_skel_mul_bone(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       vec3_t forward, left, up, origin;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       matrix4x4_t temp;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
+       VectorCopy(prog->globals.client->v_forward, forward);
+       VectorNegate(prog->globals.client->v_right, left);
+       VectorCopy(prog->globals.client->v_up, up);
+       Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
+       temp = skeleton->relativetransforms[bonenum];
+       Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
+}
+
+// #273 void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // (FTE_CSQC_SKELETONOBJECTS) 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)
+static void VM_CL_skel_mul_bones(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int firstbone = PRVM_G_FLOAT(OFS_PARM1) - 1;
+       int lastbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
+       int bonenum;
+       vec3_t forward, left, up, origin;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       matrix4x4_t temp;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM3), origin);
+       VectorCopy(prog->globals.client->v_forward, forward);
+       VectorNegate(prog->globals.client->v_right, left);
+       VectorCopy(prog->globals.client->v_up, up);
+       Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
+       firstbone = max(0, firstbone);
+       lastbone = min(lastbone, skeleton->model->num_bones - 1);
+       for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
+       {
+               temp = skeleton->relativetransforms[bonenum];
+               Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
+       }
+}
+
+// #274 void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS) copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse
+static void VM_CL_skel_copybones(void)
+{
+       int skeletonindexdst = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int skeletonindexsrc = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       int firstbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
+       int lastbone = PRVM_G_FLOAT(OFS_PARM3) - 1;
+       int bonenum;
+       skeleton_t *skeletondst;
+       skeleton_t *skeletonsrc;
+       if (skeletonindexdst < 0 || skeletonindexdst >= MAX_EDICTS || !(skeletondst = prog->skeletons[skeletonindexdst]))
+               return;
+       if (skeletonindexsrc < 0 || skeletonindexsrc >= MAX_EDICTS || !(skeletonsrc = prog->skeletons[skeletonindexsrc]))
+               return;
+       firstbone = max(0, firstbone);
+       lastbone = min(lastbone, skeletondst->model->num_bones - 1);
+       lastbone = min(lastbone, skeletonsrc->model->num_bones - 1);
+       for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
+               skeletondst->relativetransforms[bonenum] = skeletonsrc->relativetransforms[bonenum];
+}
+
+// #275 void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS) deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)
+static void VM_CL_skel_delete(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       skeleton_t *skeleton;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       Mem_Free(skeleton);
+       prog->skeletons[skeletonindex] = NULL;
+}
+
+// #276 float(float modlindex, string framename) frameforname = #276; // (FTE_CSQC_SKELETONOBJECTS) finds number of a specified frame in the animation, returns -1 if no match found
+static void VM_CL_frameforname(void)
+{
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
+       dp_model_t *model = CL_GetModelByIndex(modelindex);
+       const char *name = PRVM_G_STRING(OFS_PARM1);
+       int i;
+       PRVM_G_FLOAT(OFS_RETURN) = -1;
+       if (!model || !model->animscenes)
+               return;
+       for (i = 0;i < model->numframes;i++)
+       {
+               if (!strcasecmp(model->animscenes[i].name, name))
+               {
+                       PRVM_G_FLOAT(OFS_RETURN) = i;
+                       break;
+               }
+       }
+}
+
+// #277 float(float modlindex, float framenum) frameduration = #277; // (FTE_CSQC_SKELETONOBJECTS) 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.
+static void VM_CL_frameduration(void)
+{
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
+       dp_model_t *model = CL_GetModelByIndex(modelindex);
+       int framenum = (int)PRVM_G_FLOAT(OFS_PARM1);
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (!model || !model->animscenes || framenum < 0 || framenum >= model->numframes)
+               return;
+       if (model->animscenes[framenum].framerate)
+               PRVM_G_FLOAT(OFS_RETURN) = model->animscenes[framenum].framecount / model->animscenes[framenum].framerate;
+}
+
 //============================================================================
 
 // To create a almost working builtin file from this replace:
@@ -3920,21 +4216,21 @@ NULL,                                                   // #259
 NULL,                                                  // #260
 NULL,                                                  // #261
 NULL,                                                  // #262
-NULL,                                                  // #263
-NULL,                                                  // #264
-NULL,                                                  // #265
-NULL,                                                  // #266
-NULL,                                                  // #267
-NULL,                                                  // #268
-NULL,                                                  // #269
-NULL,                                                  // #270
-NULL,                                                  // #271
-NULL,                                                  // #272
-NULL,                                                  // #273
-NULL,                                                  // #274
-NULL,                                                  // #275
-NULL,                                                  // #276
-NULL,                                                  // #277
+VM_CL_skel_create,                             // #263 float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) 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.
+VM_CL_skel_build,                              // #264 float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) 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
+VM_CL_skel_get_numbones,               // #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
+VM_CL_skel_get_bonename,               // #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
+VM_CL_skel_get_boneparent,             // #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) returns parent num for supplied bonenum, -1 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
+VM_CL_skel_find_bone,                  // #268 float(float skel, string tagname) skel_find_bone = #268; // (FTE_CSQC_SKELETONOBJECTS) get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex
+VM_CL_skel_get_bonerel,                        // #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
+VM_CL_skel_get_boneabs,                        // #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
+VM_CL_skel_set_bone,                   // #271 void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
+VM_CL_skel_mul_bone,                   // #272 void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) 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)
+VM_CL_skel_mul_bones,                  // #273 void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // (FTE_CSQC_SKELETONOBJECTS) 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)
+VM_CL_skel_copybones,                  // #274 void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS) copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse
+VM_CL_skel_delete,                             // #275 void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS) deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)
+VM_CL_frameforname,                            // #276 float(float modlindex, string framename) frameforname = #276; // (FTE_CSQC_SKELETONOBJECTS) finds number of a specified frame in the animation, returns -1 if no match found
+VM_CL_frameduration,                   // #277 float(float modlindex, float framenum) frameduration = #277; // (FTE_CSQC_SKELETONOBJECTS) 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.
 NULL,                                                  // #278
 NULL,                                                  // #279
 NULL,                                                  // #280
index 49efcd72b057cd5efe6ce0e9602d48369bfcc597..e5b07548788bbd28ab5c1f0ca275c766accb1d65 100644 (file)
@@ -1558,7 +1558,7 @@ void Collision_BoundingBoxOfBrushTraceSegment(const colbrushf_t *start, const co
 
 //===========================================
 
-void Collision_ClipToGenericEntity(trace_t *trace, dp_model_t *model, int frame, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask)
+void Collision_ClipToGenericEntity(trace_t *trace, dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask)
 {
        float starttransformed[3], endtransformed[3];
 
@@ -1572,7 +1572,7 @@ void Collision_ClipToGenericEntity(trace_t *trace, dp_model_t *model, int frame,
 #endif
 
        if (model && model->TraceBox)
-               model->TraceBox(model, bound(0, frame, (model->numframes - 1)), trace, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask);
+               model->TraceBox(model, frameblend, skeleton, trace, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask);
        else
                Collision_ClipTrace_Box(trace, bodymins, bodymaxs, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
        trace->fraction = bound(0, trace->fraction, 1);
@@ -1589,13 +1589,13 @@ void Collision_ClipToWorld(trace_t *trace, dp_model_t *model, const vec3_t start
        memset(trace, 0, sizeof(*trace));
        trace->fraction = trace->realfraction = 1;
        if (model && model->TraceBox)
-               model->TraceBox(model, 0, trace, start, mins, maxs, end, hitsupercontents);
+               model->TraceBox(model, NULL, NULL, trace, start, mins, maxs, end, hitsupercontents);
        trace->fraction = bound(0, trace->fraction, 1);
        trace->realfraction = bound(0, trace->realfraction, 1);
        VectorLerp(start, trace->fraction, end, trace->endpos);
 }
 
-void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, int frame, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
+void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
 {
        float starttransformed[3], endtransformed[3];
 
@@ -1609,7 +1609,7 @@ void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, int fr
 #endif
 
        if (model && model->TraceLine)
-               model->TraceLine(model, bound(0, frame, (model->numframes - 1)), trace, starttransformed, endtransformed, hitsupercontentsmask);
+               model->TraceLine(model, frameblend, skeleton, trace, starttransformed, endtransformed, hitsupercontentsmask);
        else
                Collision_ClipTrace_Box(trace, bodymins, bodymaxs, starttransformed, vec3_origin, vec3_origin, endtransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
        trace->fraction = bound(0, trace->fraction, 1);
@@ -1626,13 +1626,13 @@ void Collision_ClipLineToWorld(trace_t *trace, dp_model_t *model, const vec3_t s
        memset(trace, 0, sizeof(*trace));
        trace->fraction = trace->realfraction = 1;
        if (model && model->TraceLine)
-               model->TraceLine(model, 0, trace, start, end, hitsupercontents);
+               model->TraceLine(model, NULL, NULL, trace, start, end, hitsupercontents);
        trace->fraction = bound(0, trace->fraction, 1);
        trace->realfraction = bound(0, trace->realfraction, 1);
        VectorLerp(start, trace->fraction, end, trace->endpos);
 }
 
-void Collision_ClipPointToGenericEntity(trace_t *trace, dp_model_t *model, int frame, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, int hitsupercontentsmask)
+void Collision_ClipPointToGenericEntity(trace_t *trace, dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, int hitsupercontentsmask)
 {
        float starttransformed[3];
 
@@ -1645,7 +1645,7 @@ void Collision_ClipPointToGenericEntity(trace_t *trace, dp_model_t *model, int f
 #endif
 
        if (model && model->TracePoint)
-               model->TracePoint(model, bound(0, frame, (model->numframes - 1)), trace, starttransformed, hitsupercontentsmask);
+               model->TracePoint(model, NULL, NULL, trace, starttransformed, hitsupercontentsmask);
        else
                Collision_ClipTrace_Point(trace, bodymins, bodymaxs, starttransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
 
@@ -1660,7 +1660,7 @@ void Collision_ClipPointToWorld(trace_t *trace, dp_model_t *model, const vec3_t
        memset(trace, 0, sizeof(*trace));
        trace->fraction = trace->realfraction = 1;
        if (model && model->TracePoint)
-               model->TracePoint(model, 0, trace, start, hitsupercontents);
+               model->TracePoint(model, NULL, NULL, trace, start, hitsupercontents);
        VectorCopy(start, trace->endpos);
 }
 
index 58973b8db1466d235d9e43051ab7320cc280dfc6..3b36e220d1fef2959e9512a7be6dd628f7a0e00d 100644 (file)
@@ -146,9 +146,11 @@ void Collision_TraceLineTriangleFloat(trace_t *trace, const vec3_t linestart, co
 // entities, only colliding with SOLID_BSP entities (doors, lifts)
 //
 // passedict is excluded from clipping checks
-void Collision_ClipToGenericEntity(trace_t *trace, dp_model_t *model, int frame, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask);
-void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, int frame, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t end, int hitsupercontentsmask);
-void Collision_ClipPointToGenericEntity(trace_t *trace, dp_model_t *model, int frame, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, int hitsupercontentsmask);
+struct frameblend_s;
+struct skeleton_s;
+void Collision_ClipToGenericEntity(trace_t *trace, dp_model_t *model, const struct frameblend_s *frameblend, const struct skeleton_s *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask);
+void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, const struct frameblend_s *frameblend, const struct skeleton_s *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t end, int hitsupercontentsmask);
+void Collision_ClipPointToGenericEntity(trace_t *trace, dp_model_t *model, const struct frameblend_s *frameblend, const struct skeleton_s *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, int hitsupercontentsmask);
 // like above but does not do a transform and does nothing if model is NULL
 void Collision_ClipToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontents);
 void Collision_ClipLineToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, const vec3_t end, int hitsupercontents);
index fc7f3d5a12555316381dfa76f5e7946e8cfb6406..0492d10c574f80383ec32ff3d6d3a53db9cd6eb9 100644 (file)
--- a/csprogs.c
+++ b/csprogs.c
@@ -236,7 +236,10 @@ qboolean CSQC_AddRenderEdict(prvm_edict_t *ed, int edictnum)
        }
 
        // set up the animation data
-       CL_LoadFrameGroupBlend(ed, entrender);
+       VM_GenerateFrameGroupBlend(ed->priv.server->framegroupblend, ed);
+       VM_FrameBlendFromFrameGroupBlend(ed->priv.server->frameblend, ed->priv.server->framegroupblend, model);
+       VM_UpdateEdictSkeleton(ed, model, ed->priv.server->frameblend);
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.shadertime))) entrender->shadertime = val->_float;
 
        // concat the matrices to make the entity relative to its tag
        Matrix4x4_Concat(&entrender->matrix, &tagmatrix, &matrix2);
@@ -290,7 +293,14 @@ qboolean CSQC_AddRenderEdict(prvm_edict_t *ed, int edictnum)
                entrender->flags |= RENDER_DOUBLESIDED;
 
        // make the other useful stuff
+       memcpy(entrender->framegroupblend, ed->priv.server->framegroupblend, sizeof(ed->priv.server->framegroupblend));
        CL_UpdateRenderEntity(entrender);
+       // override animation data with full control
+       memcpy(entrender->frameblend, ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend));
+       if (ed->priv.server->skeleton.relativetransforms)
+               entrender->skeleton = &ed->priv.server->skeleton;
+       else
+               entrender->skeleton = NULL;
 
        return true;
 }
@@ -750,6 +760,7 @@ void CL_VM_CB_FreeEdict(prvm_edict_t *ed)
        memset(entrender, 0, sizeof(*entrender));
        World_UnlinkEdict(ed);
        memset(ed->fields.client, 0, sizeof(*ed->fields.client));
+       VM_RemoveEdictSkeleton(ed);
        World_Physics_RemoveFromEntity(&cl.world, ed);
        World_Physics_RemoveJointFromEntity(&cl.world, ed);
 }
@@ -1031,53 +1042,3 @@ qboolean CL_VM_GetEntitySoundOrigin(int entnum, vec3_t out)
 
        return r;
 }
-
-void CL_LoadFrameGroupBlend(prvm_edict_t *ed, entity_render_t *entrender)
-{
-       prvm_eval_t *val;
-       // self.frame is the interpolation target (new frame)
-       // self.frame1time is the animation base time for the interpolation target
-       // self.frame2 is the interpolation start (previous frame)
-       // self.frame2time is the animation base time for the interpolation start
-       // self.lerpfrac is the interpolation strength for self.frame2
-       // self.lerpfrac3 is the interpolation strength for self.frame3
-       // self.lerpfrac4 is the interpolation strength for self.frame4
-       // pitch angle on a player model where the animator set up 5 sets of
-       // animations and the csqc simply lerps between sets)
-       entrender->framegroupblend[0].frame = entrender->framegroupblend[1].frame = (int) ed->fields.client->frame;
-       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2))) entrender->framegroupblend[1].frame = (int) val->_float;
-       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame3))) entrender->framegroupblend[2].frame = (int) val->_float;
-       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame4))) entrender->framegroupblend[3].frame = (int) val->_float;
-       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame1time))) entrender->framegroupblend[0].start = val->_float;
-       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2time))) entrender->framegroupblend[1].start = val->_float;
-       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame3time))) entrender->framegroupblend[2].start = val->_float;
-       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame4time))) entrender->framegroupblend[3].start = val->_float;
-       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac))) entrender->framegroupblend[1].lerp = val->_float;
-       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac3))) entrender->framegroupblend[2].lerp = val->_float;
-       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac4))) entrender->framegroupblend[3].lerp = val->_float;
-       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.shadertime))) entrender->shadertime = val->_float;
-       // assume that the (missing) lerpfrac1 is whatever remains after lerpfrac2+lerpfrac3+lerpfrac4 are summed
-       entrender->framegroupblend[0].lerp = 1 - entrender->framegroupblend[1].lerp - entrender->framegroupblend[2].lerp - entrender->framegroupblend[3].lerp;
-}
-
-int CL_BlendTagMatrix(entity_render_t *entrender, int tagindex, matrix4x4_t *blendmatrix)
-{
-       int j, l, k;
-       int ret;
-       float d;
-       dp_model_t *model = entrender->model;
-       // blend the matrices
-       memset(blendmatrix, 0, sizeof(*blendmatrix));
-       for (j = 0;j < MAX_FRAMEBLENDS && entrender->frameblend[j].lerp > 0;j++)
-       {
-               matrix4x4_t tagmatrix;
-               ret = Mod_Alias_GetTagMatrix(model, entrender->frameblend[j].subframe, tagindex, &tagmatrix);
-               if(ret)
-                       return ret;
-               d = entrender->frameblend[j].lerp;
-               for (l = 0;l < 4;l++)
-                       for (k = 0;k < 4;k++)
-                               blendmatrix->m[l][k] += d * tagmatrix.m[l][k];
-       }
-       return 0;
-}
index 38e1cba0f2223f0f47076a1b9cf267eb64458486..e4a3e897642b67d7d5f5328aa7a837ca2311905b 100644 (file)
--- a/csprogs.h
+++ b/csprogs.h
@@ -61,7 +61,4 @@ qboolean MakeDownloadPacket(const char *filename, unsigned char *data, unsigned
 
 qboolean CL_VM_GetEntitySoundOrigin(int entnum, vec3_t out);
 
-void CL_LoadFrameGroupBlend(prvm_edict_t *ed, entity_render_t *entrender);
-int CL_BlendTagMatrix(entity_render_t *entrender, int tagindex, matrix4x4_t *blendmatrix); // returns 0 or an error code
-
 #endif
index 357bd848b4888d5d4ef38960e9c2a851b2fa4919..d6c7929edba7314eff438b123e45542576e40476 100644 (file)
@@ -3393,7 +3393,7 @@ qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qbool
                if (c->wanttangents)
                        wanttangents = false;
                if (wantnormals || wanttangents)
-                       model->AnimateVertices(model, ent->frameblend, NULL, wantnormals ? c->normal3f : NULL, wanttangents ? c->svector3f : NULL, wanttangents ? c->tvector3f : NULL);
+                       model->AnimateVertices(model, ent->frameblend, ent->skeleton, NULL, wantnormals ? c->normal3f : NULL, wanttangents ? c->svector3f : NULL, wanttangents ? c->tvector3f : NULL);
        }
        else
        {
@@ -3408,7 +3408,7 @@ qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qbool
                c = r_animcachestate.entity + ent->animcacheindex;
                c->wantnormals = wantnormals;
                c->wanttangents = wanttangents;
-               model->AnimateVertices(model, ent->frameblend, c->vertex3f, wantnormals ? c->normal3f : NULL, wanttangents ? c->svector3f : NULL, wanttangents ? c->tvector3f : NULL);
+               model->AnimateVertices(model, ent->frameblend, ent->skeleton, c->vertex3f, wantnormals ? c->normal3f : NULL, wanttangents ? c->svector3f : NULL, wanttangents ? c->tvector3f : NULL);
        }
        return true;
 }
@@ -5982,6 +5982,7 @@ void RSurf_ActiveWorldEntity(void)
        //if (rsurface.entity == r_refdef.scene.worldentity)
        //      return;
        rsurface.entity = r_refdef.scene.worldentity;
+       rsurface.skeleton = NULL;
        rsurface.ent_skinnum = 0;
        rsurface.ent_qwskin = -1;
        rsurface.ent_shadertime = 0;
@@ -6062,6 +6063,7 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
        //if (rsurface.entity == ent && (!model->surfmesh.isanimated || (!wantnormals && !wanttangents)))
        //      return;
        rsurface.entity = (entity_render_t *)ent;
+       rsurface.skeleton = ent->skeleton;
        rsurface.ent_skinnum = ent->skinnum;
        rsurface.ent_qwskin = (ent->entitynumber <= cl.maxclients && ent->entitynumber >= 1 && cls.protocol == PROTOCOL_QUAKEWORLD && cl.scores[ent->entitynumber - 1].qw_skin[0] && !strcmp(ent->model->name, "progs/player.mdl")) ? (ent->entitynumber - 1) : -1;
        rsurface.ent_shadertime = ent->shadertime;
@@ -6110,7 +6112,7 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
                        rsurface.modelsvector3f = rsurface.array_modelsvector3f;
                        rsurface.modeltvector3f = rsurface.array_modeltvector3f;
                        rsurface.modelnormal3f = rsurface.array_modelnormal3f;
-                       model->AnimateVertices(model, rsurface.frameblend, rsurface.array_modelvertex3f, rsurface.array_modelnormal3f, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f);
+                       model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.array_modelvertex3f, rsurface.array_modelnormal3f, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f);
                }
                else if (wantnormals)
                {
@@ -6118,7 +6120,7 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
                        rsurface.modelsvector3f = NULL;
                        rsurface.modeltvector3f = NULL;
                        rsurface.modelnormal3f = rsurface.array_modelnormal3f;
-                       model->AnimateVertices(model, rsurface.frameblend, rsurface.array_modelvertex3f, rsurface.array_modelnormal3f, NULL, NULL);
+                       model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.array_modelvertex3f, rsurface.array_modelnormal3f, NULL, NULL);
                }
                else
                {
@@ -6126,7 +6128,7 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
                        rsurface.modelsvector3f = NULL;
                        rsurface.modeltvector3f = NULL;
                        rsurface.modelnormal3f = NULL;
-                       model->AnimateVertices(model, rsurface.frameblend, rsurface.array_modelvertex3f, NULL, NULL, NULL);
+                       model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.array_modelvertex3f, NULL, NULL, NULL);
                }
                rsurface.modelvertex3f_bufferobject = 0;
                rsurface.modelvertex3f_bufferoffset = 0;
@@ -6189,6 +6191,7 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
 void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inversematrix, int entflags, double shadertime, float r, float g, float b, float a, int numvertices, const float *vertex3f, const float *texcoord2f, const float *normal3f, const float *svector3f, const float *tvector3f, const float *color4f, int numtriangles, const int *element3i, const unsigned short *element3s, qboolean wantnormals, qboolean wanttangents)
 {
        rsurface.entity = r_refdef.scene.worldentity;
+       rsurface.skeleton = NULL;
        rsurface.ent_skinnum = 0;
        rsurface.ent_qwskin = -1;
        rsurface.ent_shadertime = shadertime;
index b1a5bacaa73035e9e06ae5dc14daa09412992c08..e8feab96e5dc3030bc57090f9eaca000075b4ede 100644 (file)
@@ -46,7 +46,7 @@ void Mod_AliasInit (void)
                mod_md3_sin[i] = sin(i * M_PI * 2.0f / 256.0);
 }
 
-void Mod_Skeletal_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+void Mod_Skeletal_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
 {
 #define MAX_BONES 256
        // vertex weighted skeletal
@@ -56,41 +56,64 @@ void Mod_Skeletal_AnimateVertices(const dp_model_t *model, const frameblend_t *f
        float boneposerelative[MAX_BONES][12];
        float *matrix, m[12], bonepose[MAX_BONES][12];
 
-       // interpolate matrices and concatenate them to their parents
-       for (i = 0;i < model->num_bones;i++)
+       if (skeleton && !skeleton->relativetransforms)
+               skeleton = NULL;
+
+       // interpolate matrices
+       if (skeleton)
        {
-               for (k = 0;k < 12;k++)
-                       m[k] = 0;
-               VectorClear(desiredscale);
-               for (blends = 0;blends < MAX_FRAMEBLENDS && frameblend[blends].lerp > 0;blends++)
+               for (i = 0;i < model->num_bones;i++)
                {
-                       matrix = model->data_poses + (frameblend[blends].subframe * model->num_bones + i) * 12;
-                       for (k = 0;k < 12;k++)
-                               m[k] += matrix[k] * frameblend[blends].lerp;
-                       desiredscale[0] += frameblend[blends].lerp * VectorLength(matrix    );
-                       desiredscale[1] += frameblend[blends].lerp * VectorLength(matrix + 4);
-                       desiredscale[2] += frameblend[blends].lerp * VectorLength(matrix + 8);
+                       Matrix4x4_ToArray12FloatD3D(&skeleton->relativetransforms[i], m);
+                       if (model->data_bones[i].parent >= 0)
+                               R_ConcatTransforms(bonepose[model->data_bones[i].parent], m, bonepose[i]);
+                       else
+                               for (k = 0;k < 12;k++)
+                                       bonepose[i][k] = m[k];
+       
+                       // create a relative deformation matrix to describe displacement
+                       // from the base mesh, which is used by the actual weighting
+                       R_ConcatTransforms(bonepose[i], model->data_baseboneposeinverse + i * 12, boneposerelative[i]);
                }
-               VectorNormalize(m    );
-               VectorNormalize(m + 4);
-               VectorNormalize(m + 8);
-               VectorScale(m    , desiredscale[0], m    );
-               VectorScale(m + 4, desiredscale[1], m + 4);
-               VectorScale(m + 8, desiredscale[2], m + 8);
-               if (i == r_skeletal_debugbone.integer)
-                       m[r_skeletal_debugbonecomponent.integer % 12] += r_skeletal_debugbonevalue.value;
-               m[3] *= r_skeletal_debugtranslatex.value;
-               m[7] *= r_skeletal_debugtranslatey.value;
-               m[11] *= r_skeletal_debugtranslatez.value;
-               if (model->data_bones[i].parent >= 0)
-                       R_ConcatTransforms(bonepose[model->data_bones[i].parent], m, bonepose[i]);
-               else
+       }
+       else
+       {
+               for (i = 0;i < model->num_bones;i++)
+               {
                        for (k = 0;k < 12;k++)
-                               bonepose[i][k] = m[k];
-               // create a relative deformation matrix to describe displacement
-               // from the base mesh, which is used by the actual weighting
-               R_ConcatTransforms(bonepose[i], model->data_baseboneposeinverse + i * 12, boneposerelative[i]);
+                               m[k] = 0;
+                       VectorClear(desiredscale);
+                       for (blends = 0;blends < MAX_FRAMEBLENDS && frameblend[blends].lerp > 0;blends++)
+                       {
+                               matrix = model->data_poses + (frameblend[blends].subframe * model->num_bones + i) * 12;
+                               for (k = 0;k < 12;k++)
+                                       m[k] += matrix[k] * frameblend[blends].lerp;
+                               desiredscale[0] += frameblend[blends].lerp * VectorLength(matrix    );
+                               desiredscale[1] += frameblend[blends].lerp * VectorLength(matrix + 4);
+                               desiredscale[2] += frameblend[blends].lerp * VectorLength(matrix + 8);
+                       }
+                       VectorNormalize(m    );
+                       VectorNormalize(m + 4);
+                       VectorNormalize(m + 8);
+                       VectorScale(m    , desiredscale[0], m    );
+                       VectorScale(m + 4, desiredscale[1], m + 4);
+                       VectorScale(m + 8, desiredscale[2], m + 8);
+                       if (i == r_skeletal_debugbone.integer)
+                               m[r_skeletal_debugbonecomponent.integer % 12] += r_skeletal_debugbonevalue.value;
+                       m[3] *= r_skeletal_debugtranslatex.value;
+                       m[7] *= r_skeletal_debugtranslatey.value;
+                       m[11] *= r_skeletal_debugtranslatez.value;
+                       if (model->data_bones[i].parent >= 0)
+                               R_ConcatTransforms(bonepose[model->data_bones[i].parent], m, bonepose[i]);
+                       else
+                               for (k = 0;k < 12;k++)
+                                       bonepose[i][k] = m[k];
+                       // create a relative deformation matrix to describe displacement
+                       // from the base mesh, which is used by the actual weighting
+                       R_ConcatTransforms(bonepose[i], model->data_baseboneposeinverse + i * 12, boneposerelative[i]);
+               }
        }
+
        // blend the vertex bone weights
        // special case for the extremely common wf[0] == 1 because it saves 3 multiplies per array when compared to the other case (w[0] is always 1 if only one bone controls this vertex, artists only use multiple bones for certain special cases)
        // special case for the first bone because it avoids the need to memset the arrays before filling
@@ -228,7 +251,7 @@ void Mod_Skeletal_AnimateVertices(const dp_model_t *model, const frameblend_t *f
        }
 }
 
-void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
 {
        // vertex morph
        int i, numblends, blendnum;
@@ -317,7 +340,7 @@ void Mod_MD3_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        }
 }
 
-void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
+void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f)
 {
        // vertex morph
        int i, numblends, blendnum;
@@ -414,34 +437,68 @@ void Mod_MDL_AnimateVertices(const dp_model_t *model, const frameblend_t *frameb
        }
 }
 
-int Mod_Alias_GetTagMatrix(const dp_model_t *model, int poseframe, int tagindex, matrix4x4_t *outmatrix)
+int Mod_Alias_GetTagMatrix(const dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, int tagindex, matrix4x4_t *outmatrix)
 {
+       matrix4x4_t temp;
        const float *boneframe;
-       float tempbonematrix[12], bonematrix[12];
+       const float *input;
+       int blendindex;
+       int parenttagindex;
+       int k;
+       float lerp;
+       float tempbonematrix[12], bonematrix[12], blendmatrix[12];
        *outmatrix = identitymatrix;
-       if (model->num_bones)
+       if (skeleton && skeleton->relativetransforms)
+       {
+               if (tagindex < 0 || tagindex >= skeleton->model->num_bones)
+                       return 4;
+               *outmatrix = skeleton->relativetransforms[tagindex];
+               while ((tagindex = model->data_bones[tagindex].parent) >= 0)
+               {
+                       temp = *outmatrix;
+                       Matrix4x4_Concat(outmatrix, &skeleton->relativetransforms[tagindex], &temp);
+               }
+       }
+       else if (model->num_bones)
        {
                if (tagindex < 0 || tagindex >= model->num_bones)
                        return 4;
-               if (poseframe >= model->num_poses)
-                       return 6;
-               boneframe = model->data_poses + poseframe * model->num_bones * 12;
-               memcpy(bonematrix, boneframe + tagindex * 12, sizeof(float[12]));
-               while (model->data_bones[tagindex].parent >= 0)
+               for (k = 0;k < 12;k++)
+                       blendmatrix[k] = 0;
+               for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
                {
-                       memcpy(tempbonematrix, bonematrix, sizeof(float[12]));
-                       R_ConcatTransforms(boneframe + model->data_bones[tagindex].parent * 12, tempbonematrix, bonematrix);
-                       tagindex = model->data_bones[tagindex].parent;
+                       lerp = frameblend[blendindex].lerp;
+                       boneframe = model->data_poses + frameblend[blendindex].subframe * model->num_bones * 12;
+                       input = boneframe + tagindex * 12;
+                       for (k = 0;k < 12;k++)
+                               bonematrix[k] = input[k];
+                       parenttagindex = tagindex;
+                       while ((parenttagindex = model->data_bones[parenttagindex].parent) >= 0)
+                       {
+                               for (k = 0;k < 12;k++)
+                                       tempbonematrix[k] = bonematrix[k];
+                               input = boneframe + parenttagindex * 12;
+                               R_ConcatTransforms(input, tempbonematrix, bonematrix);
+                       }
+                       for (k = 0;k < 12;k++)
+                               blendmatrix[k] += bonematrix[k] * lerp;
                }
-               Matrix4x4_FromArray12FloatD3D(outmatrix, bonematrix);
+               Matrix4x4_FromArray12FloatD3D(outmatrix, blendmatrix);
        }
        else if (model->num_tags)
        {
                if (tagindex < 0 || tagindex >= model->num_tags)
                        return 4;
-               if (poseframe >= model->num_tagframes)
-                       return 6;
-               Matrix4x4_FromArray12FloatGL(outmatrix, model->data_tags[poseframe * model->num_tags + tagindex].matrixgl);
+               for (k = 0;k < 12;k++)
+                       blendmatrix[k] = 0;
+               for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
+               {
+                       lerp = frameblend[blendindex].lerp;
+                       input = model->data_tags[frameblend[blendindex].subframe * model->num_tags + tagindex].matrixgl;
+                       for (k = 0;k < 12;k++)
+                               blendmatrix[k] += input[k] * lerp;
+               }
+               Matrix4x4_FromArray12FloatGL(outmatrix, blendmatrix);
        }
 
        if(!mod_alias_supporttagscale.integer)
@@ -450,35 +507,55 @@ int Mod_Alias_GetTagMatrix(const dp_model_t *model, int poseframe, int tagindex,
        return 0;
 }
 
-int Mod_Alias_GetExtendedTagInfoForIndex(const dp_model_t *model, unsigned int skin, int poseframe, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
+int Mod_Alias_GetExtendedTagInfoForIndex(const dp_model_t *model, unsigned int skin, const frameblend_t *frameblend, const skeleton_t *skeleton, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
 {
        const float *boneframe;
+       const float *input;
+       int blendindex;
+       int k;
+       float lerp;
+       float blendmatrix[12];
 
-       if(skin >= (unsigned int)model->numskins)
-               skin = 0;
-
-       if (model->num_bones)
+       if (skeleton && skeleton->relativetransforms)
+       {
+               if (tagindex < 0 || tagindex >= skeleton->model->num_bones)
+                       return 1;
+               *parentindex = skeleton->model->data_bones[tagindex].parent;
+               *tagname = skeleton->model->data_bones[tagindex].name;
+               *tag_localmatrix = skeleton->relativetransforms[tagindex];
+               return 0;
+       }
+       else if (model->num_bones)
        {
                if(tagindex >= model->num_bones || tagindex < 0)
                        return 1;
-               if (poseframe >= model->num_poses)
-                       return 2;
-
-               boneframe = model->data_poses + poseframe * model->num_bones * 12;
                *parentindex = model->data_bones[tagindex].parent;
                *tagname = model->data_bones[tagindex].name;
-               Matrix4x4_FromArray12FloatD3D(tag_localmatrix, boneframe + tagindex * 12);
+               for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
+               {
+                       lerp = frameblend[blendindex].lerp;
+                       boneframe = model->data_poses + frameblend[blendindex].subframe * model->num_bones * 12;
+                       input = boneframe + tagindex * 12;
+                       for (k = 0;k < 12;k++)
+                               blendmatrix[k] += input[k] * lerp;
+               }
+               Matrix4x4_FromArray12FloatD3D(tag_localmatrix, blendmatrix);
                return 0;
        }
-
-       if (model->num_tags)
+       else if (model->num_tags)
        {
                if(tagindex >= model->num_tags || tagindex < 0)
                        return 1;
-               if (poseframe >= model->num_tagframes)
-                       return 2;
+               *parentindex = -1;
                *tagname = model->data_tags[tagindex].name;
-               Matrix4x4_FromArray12FloatGL(tag_localmatrix, model->data_tags[poseframe * model->num_tags + tagindex].matrixgl);
+               for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
+               {
+                       lerp = frameblend[blendindex].lerp;
+                       input = model->data_tags[frameblend[blendindex].subframe * model->num_tags + tagindex].matrixgl;
+                       for (k = 0;k < 12;k++)
+                               blendmatrix[k] += input[k] * lerp;
+               }
+               Matrix4x4_FromArray12FloatGL(tag_localmatrix, blendmatrix);
                return 0;
        }
 
@@ -565,7 +642,7 @@ static void Mod_Alias_CalculateBoundingBox(void)
        radius = 0;
        for (frameblend[0].subframe = 0;frameblend[0].subframe < loadmodel->num_poses;frameblend[0].subframe++)
        {
-               loadmodel->AnimateVertices(loadmodel, frameblend, vertex3f, NULL, NULL, NULL);
+               loadmodel->AnimateVertices(loadmodel, frameblend, NULL, vertex3f, NULL, NULL, NULL);
                for (vnum = 0, v = vertex3f;vnum < loadmodel->surfmesh.num_vertices;vnum++, v += 3)
                {
                        if (firstvertex)
@@ -622,7 +699,7 @@ static void Mod_Alias_MorphMesh_CompileFrames(void)
        for (i = loadmodel->surfmesh.num_morphframes-1;i >= 0;i--)
        {
                frameblend[0].subframe = i;
-               loadmodel->AnimateVertices(loadmodel, frameblend, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_normal3f, NULL, NULL);
+               loadmodel->AnimateVertices(loadmodel, frameblend, NULL, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_normal3f, NULL, NULL);
                Mod_BuildTextureVectorsFromNormals(0, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->surfmesh.data_vertex3f, loadmodel->surfmesh.data_texcoordtexture2f, loadmodel->surfmesh.data_normal3f, loadmodel->surfmesh.data_element3i, loadmodel->surfmesh.data_svector3f, loadmodel->surfmesh.data_tvector3f, r_smoothnormals_areaweighting.integer != 0);
                // encode the svector and tvector in 3 byte format for permanent storage
                for (j = 0;j < loadmodel->surfmesh.num_vertices;j++)
@@ -633,11 +710,10 @@ static void Mod_Alias_MorphMesh_CompileFrames(void)
        }
 }
 
-static void Mod_MDLMD2MD3_TraceLine(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
+static void Mod_MDLMD2MD3_TraceLine(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
 {
        int i;
        float segmentmins[3], segmentmaxs[3];
-       frameblend_t frameblend[MAX_FRAMEBLENDS];
        msurface_t *surface;
        static int maxvertices = 0;
        static float *vertex3f = NULL;
@@ -645,9 +721,6 @@ static void Mod_MDLMD2MD3_TraceLine(dp_model_t *model, int frame, trace_t *trace
        trace->fraction = 1;
        trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
-       memset(frameblend, 0, sizeof(frameblend));
-       frameblend[0].subframe = frame;
-       frameblend[0].lerp = 1;
        if (maxvertices < model->surfmesh.num_vertices)
        {
                if (vertex3f)
@@ -661,22 +734,20 @@ static void Mod_MDLMD2MD3_TraceLine(dp_model_t *model, int frame, trace_t *trace
        segmentmaxs[0] = max(start[0], end[0]) + 1;
        segmentmaxs[1] = max(start[1], end[1]) + 1;
        segmentmaxs[2] = max(start[2], end[2]) + 1;
+       model->AnimateVertices(model, frameblend, skeleton, vertex3f, NULL, NULL, NULL);
        for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
-       {
-               model->AnimateVertices(model, frameblend, vertex3f, NULL, NULL, NULL);
                Collision_TraceLineTriangleMeshFloat(trace, start, end, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, 0, NULL, SUPERCONTENTS_SOLID | (surface->texture->basematerialflags & MATERIALFLAGMASK_TRANSLUCENT ? 0 : SUPERCONTENTS_OPAQUE), 0, surface->texture, segmentmins, segmentmaxs);
-       }
 }
 
-static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
+static int maxvertices = 0;
+static float *vertex3f = NULL;
+
+static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
 {
        int i;
        vec3_t shiftstart, shiftend;
        float segmentmins[3], segmentmaxs[3];
-       frameblend_t frameblend[MAX_FRAMEBLENDS];
        msurface_t *surface;
-       static int maxvertices = 0;
-       static float *vertex3f = NULL;
        colboxbrushf_t thisbrush_start, thisbrush_end;
        vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
 
@@ -684,7 +755,7 @@ static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, int frame, trace_t *trace,
        {
                VectorAdd(start, boxmins, shiftstart);
                VectorAdd(end, boxmins, shiftend);
-               Mod_MDLMD2MD3_TraceLine(model, frame, trace, start, end, hitsupercontentsmask);
+               Mod_MDLMD2MD3_TraceLine(model, frameblend, skeleton, trace, start, end, hitsupercontentsmask);
                VectorSubtract(trace->endpos, boxmins, trace->endpos);
                return;
        }
@@ -694,9 +765,6 @@ static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, int frame, trace_t *trace,
        trace->fraction = 1;
        trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
-       memset(frameblend, 0, sizeof(frameblend));
-       frameblend[0].subframe = frame;
-       frameblend[0].lerp = 1;
        if (maxvertices < model->surfmesh.num_vertices)
        {
                if (vertex3f)
@@ -716,18 +784,16 @@ static void Mod_MDLMD2MD3_TraceBox(dp_model_t *model, int frame, trace_t *trace,
        VectorAdd(end, boxmaxs, boxendmaxs);
        Collision_BrushForBox(&thisbrush_start, boxstartmins, boxstartmaxs, 0, 0, NULL);
        Collision_BrushForBox(&thisbrush_end, boxendmins, boxendmaxs, 0, 0, NULL);
-       for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
+       if (maxvertices < model->surfmesh.num_vertices)
        {
-               if (maxvertices < model->surfmesh.num_vertices)
-               {
-                       if (vertex3f)
-                               Z_Free(vertex3f);
-                       maxvertices = (model->surfmesh.num_vertices + 255) & ~255;
-                       vertex3f = (float *)Z_Malloc(maxvertices * sizeof(float[3]));
-               }
-               model->AnimateVertices(model, frameblend, vertex3f, NULL, NULL, NULL);
-               Collision_TraceBrushTriangleMeshFloat(trace, &thisbrush_start.brush, &thisbrush_end.brush, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, 0, NULL, SUPERCONTENTS_SOLID | (surface->texture->basematerialflags & MATERIALFLAGMASK_TRANSLUCENT ? 0 : SUPERCONTENTS_OPAQUE), 0, surface->texture, segmentmins, segmentmaxs);
+               if (vertex3f)
+                       Z_Free(vertex3f);
+               maxvertices = (model->surfmesh.num_vertices + 255) & ~255;
+               vertex3f = (float *)Z_Malloc(maxvertices * sizeof(float[3]));
        }
+       model->AnimateVertices(model, frameblend, skeleton, vertex3f, NULL, NULL, NULL);
+       for (i = 0, surface = model->data_surfaces;i < model->num_surfaces;i++, surface++)
+               Collision_TraceBrushTriangleMeshFloat(trace, &thisbrush_start.brush, &thisbrush_end.brush, model->surfmesh.num_triangles, model->surfmesh.data_element3i, vertex3f, 0, NULL, SUPERCONTENTS_SOLID | (surface->texture->basematerialflags & MATERIALFLAGMASK_TRANSLUCENT ? 0 : SUPERCONTENTS_OPAQUE), 0, surface->texture, segmentmins, segmentmaxs);
 }
 
 static void Mod_ConvertAliasVerts (int inverts, trivertx_t *v, trivertx_t *out, int *vertremap)
index d8fe5ae41d87763160ae2cb8cb246b24e1fe9a6a..b20992d6e12c5f09d7b4746c101eb06cdea73594 100644 (file)
@@ -853,7 +853,7 @@ static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, i
 }
 //#endif
 
-static void Mod_Q1BSP_TracePoint(struct model_s *model, int frame, trace_t *trace, const vec3_t start, int hitsupercontentsmask)
+static void Mod_Q1BSP_TracePoint(struct model_s *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, int hitsupercontentsmask)
 {
        RecursiveHullCheckTraceInfo_t rhc;
 
@@ -869,13 +869,13 @@ static void Mod_Q1BSP_TracePoint(struct model_s *model, int frame, trace_t *trac
        Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
 }
 
-static void Mod_Q1BSP_TraceLine(struct model_s *model, int frame, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
+static void Mod_Q1BSP_TraceLine(struct model_s *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
 {
        RecursiveHullCheckTraceInfo_t rhc;
 
        if (VectorCompare(start, end))
        {
-               Mod_Q1BSP_TracePoint(model, frame, trace, start, hitsupercontentsmask);
+               Mod_Q1BSP_TracePoint(model, frameblend, skeleton, trace, start, hitsupercontentsmask);
                return;
        }
 
@@ -921,7 +921,7 @@ static void Mod_Q1BSP_TraceLine(struct model_s *model, int frame, trace_t *trace
 #endif
 }
 
-static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
+static void Mod_Q1BSP_TraceBox(struct model_s *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
 {
        // this function currently only supports same size start and end
        double boxsize[3];
@@ -930,9 +930,9 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace,
        if (VectorCompare(boxmins, boxmaxs))
        {
                if (VectorCompare(start, end))
-                       Mod_Q1BSP_TracePoint(model, frame, trace, start, hitsupercontentsmask);
+                       Mod_Q1BSP_TracePoint(model, frameblend, skeleton, trace, start, hitsupercontentsmask);
                else
-                       Mod_Q1BSP_TraceLine(model, frame, trace, start, end, hitsupercontentsmask);
+                       Mod_Q1BSP_TraceLine(model, frameblend, skeleton, trace, start, end, hitsupercontentsmask);
                return;
        }
 
@@ -1142,7 +1142,7 @@ void Collision_ClipTrace_Point(trace_t *trace, const vec3_t cmins, const vec3_t
 static qboolean Mod_Q1BSP_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end)
 {
        trace_t trace;
-       model->TraceLine(model, 0, &trace, start, end, SUPERCONTENTS_VISBLOCKERMASK);
+       model->TraceLine(model, NULL, NULL, &trace, start, end, SUPERCONTENTS_VISBLOCKERMASK);
        return trace.fraction == 1;
 }
 
@@ -5656,7 +5656,7 @@ static qboolean Mod_Q3BSP_TraceLineOfSight(struct model_s *model, const vec3_t s
        if (model->brush.submodel || mod_q3bsp_tracelineofsight_brushes.integer)
        {
                trace_t trace;
-               model->TraceLine(model, 0, &trace, start, end, SUPERCONTENTS_VISBLOCKERMASK);
+               model->TraceLine(model, NULL, NULL, &trace, start, end, SUPERCONTENTS_VISBLOCKERMASK);
                return trace.fraction == 1;
        }
        else
@@ -5860,7 +5860,7 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, dp_model_t *mo
 
 static int markframe = 0;
 
-static void Mod_Q3BSP_TracePoint(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, int hitsupercontentsmask)
+static void Mod_Q3BSP_TracePoint(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, int hitsupercontentsmask)
 {
        int i;
        q3mbrush_t *brush;
@@ -5878,7 +5878,7 @@ static void Mod_Q3BSP_TracePoint(dp_model_t *model, int frame, trace_t *trace, c
                Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, ++markframe);
 }
 
-static void Mod_Q3BSP_TraceLine(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
+static void Mod_Q3BSP_TraceLine(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
 {
        int i;
        float segmentmins[3], segmentmaxs[3];
@@ -5887,7 +5887,7 @@ static void Mod_Q3BSP_TraceLine(dp_model_t *model, int frame, trace_t *trace, co
 
        if (VectorCompare(start, end))
        {
-               Mod_Q3BSP_TracePoint(model, frame, trace, start, hitsupercontentsmask);
+               Mod_Q3BSP_TracePoint(model, frameblend, skeleton, trace, start, hitsupercontentsmask);
                return;
        }
 
@@ -5915,7 +5915,7 @@ static void Mod_Q3BSP_TraceLine(dp_model_t *model, int frame, trace_t *trace, co
                Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, end, 0, 1, start, end, ++markframe, segmentmins, segmentmaxs);
 }
 
-static void Mod_Q3BSP_TraceBox(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
+static void Mod_Q3BSP_TraceBox(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
 {
        int i;
        float segmentmins[3], segmentmaxs[3];
@@ -5930,10 +5930,10 @@ static void Mod_Q3BSP_TraceBox(dp_model_t *model, int frame, trace_t *trace, con
                VectorAdd(start, boxmins, shiftstart);
                VectorAdd(end, boxmins, shiftend);
                if (VectorCompare(start, end))
-                       Mod_Q3BSP_TracePoint(model, frame, trace, shiftstart, hitsupercontentsmask);
+                       Mod_Q3BSP_TracePoint(model, frameblend, skeleton, trace, shiftstart, hitsupercontentsmask);
                else
                {
-                       Mod_Q3BSP_TraceLine(model, frame, trace, shiftstart, shiftend, hitsupercontentsmask);
+                       Mod_Q3BSP_TraceLine(model, frameblend, skeleton, trace, shiftstart, shiftend, hitsupercontentsmask);
                        VectorSubtract(trace->endpos, boxmins, trace->endpos);
                }
                return;
index d7de945620fe609d4671adb2b0f6be3c2928bc3c..fda4957823b80114427cde2b0321bdcfe2dcf89f 100644 (file)
@@ -808,6 +808,7 @@ typedef struct model_brushq3_s
 model_brushq3_t;
 
 struct frameblend_s;
+struct skeleton_s;
 
 typedef struct model_s
 {
@@ -878,7 +879,7 @@ typedef struct model_s
        // data type of model
        const char              *modeldatatypestring;
        // generates vertex data for a given frameblend
-       void(*AnimateVertices)(const struct model_s *model, const struct frameblend_s *frameblend, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f);
+       void(*AnimateVertices)(const struct model_s *model, const struct frameblend_s *frameblend, const struct skeleton_s *skeleton, float *vertex3f, float *normal3f, float *svector3f, float *tvector3f);
        // draw the model's sky polygons (only used by brush models)
        void(*DrawSky)(struct entity_render_s *ent);
        // draw refraction/reflection textures for the model's water polygons (only used by brush models)
@@ -902,11 +903,11 @@ typedef struct model_s
        // draw the lighting on a model (through stencil)
        void(*DrawLight)(struct entity_render_s *ent, int numsurfaces, const int *surfacelist, const unsigned char *trispvs);
        // trace a box against this model
-       void (*TraceBox)(struct model_s *model, int frame, struct trace_s *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask);
+       void (*TraceBox)(struct model_s *model, const struct frameblend_s *frameblend, const struct skeleton_s *skeleton, struct trace_s *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask);
        // trace a box against this model
-       void (*TraceLine)(struct model_s *model, int frame, struct trace_s *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask);
+       void (*TraceLine)(struct model_s *model, const struct frameblend_s *frameblend, const struct skeleton_s *skeleton, struct trace_s *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask);
        // trace a point against this model (like PointSuperContents)
-       void (*TracePoint)(struct model_s *model, int frame, struct trace_s *trace, const vec3_t start, int hitsupercontentsmask);
+       void (*TracePoint)(struct model_s *model, const struct frameblend_s *frameblend, const struct skeleton_s *skeleton, struct trace_s *trace, const vec3_t start, int hitsupercontentsmask);
        // find the supercontents value at a point in this model
        int (*PointSuperContents)(struct model_s *model, int frame, const vec3_t point);
        // fields belonging to some types of model
@@ -974,6 +975,13 @@ qboolean Mod_LoadTextureFromQ3Shader(texture_t *texture, const char *name, qbool
 
 extern cvar_t r_mipskins;
 
+typedef struct skeleton_s
+{
+       const dp_model_t *model;
+       matrix4x4_t *relativetransforms;
+}
+skeleton_t;
+
 typedef struct skinfileitem_s
 {
        struct skinfileitem_s *next;
@@ -1019,10 +1027,11 @@ void R_Q1BSP_DrawLight(struct entity_render_s *ent, int numsurfaces, const int *
 
 // alias models
 struct frameblend_s;
+struct skeleton_s;
 void Mod_AliasInit(void);
-int Mod_Alias_GetTagMatrix(const dp_model_t *model, int poseframe, int tagindex, matrix4x4_t *outmatrix);
+int Mod_Alias_GetTagMatrix(const dp_model_t *model, const struct frameblend_s *frameblend, const struct skeleton_s *skeleton, int tagindex, matrix4x4_t *outmatrix);
 int Mod_Alias_GetTagIndexForName(const dp_model_t *model, unsigned int skin, const char *tagname);
-int Mod_Alias_GetExtendedTagInfoForIndex(const dp_model_t *model, unsigned int skin, int poseframe, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix);
+int Mod_Alias_GetExtendedTagInfoForIndex(const dp_model_t *model, unsigned int skin, const struct frameblend_s *frameblend, const struct skeleton_s *skeleton, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix);
 
 // sprite models
 void Mod_SpriteInit(void);
diff --git a/progs.h b/progs.h
index 1df57c2f59ad54ac327781e796bb59a384d2ee19..fedd19e554cd66501557b61766a0b29b1c8bf5d2 100644 (file)
--- a/progs.h
+++ b/progs.h
@@ -76,6 +76,10 @@ typedef struct edict_engineprivate_s
        vec3_t moved_from;
        vec3_t moved_fromangles;
 
+       framegroupblend_t framegroupblend[MAX_FRAMEGROUPBLENDS];
+       frameblend_t frameblend[MAX_FRAMEBLENDS];
+       skeleton_t skeleton;
+
        // physics parameters
        qboolean ode_physics;
        void *ode_body;
index 72cfa0bf13dabfc7defb5a7ee3b1c17a7d7c4aef..eb8306fb47f4d1e424292071f873d780cbe35b34 100644 (file)
--- a/progsvm.h
+++ b/progsvm.h
@@ -222,6 +222,7 @@ typedef struct prvm_prog_fieldoffsets_s
        int rendermode; // ssqc - HalfLife support
        int scale; // ssqc / csqc
        int shadertime; // csqc
+       int skeletonindex; // csqc / ssqc FTE_CSQC_SKELETONOBJECTS / DP_SKELETONOBJECTS
        int style; // ssqc
        int tag_entity; // ssqc / csqc
        int tag_index; // ssqc / csqc
@@ -422,6 +423,7 @@ typedef struct prvm_prog_s
        fssearch_t                      *opensearches[PRVM_MAX_OPENSEARCHES];
        const char *         opensearches_origin[PRVM_MAX_OPENSEARCHES];
        struct clgecko_s                *opengeckoinstances[PRVM_MAX_GECKOINSTANCES];
+       skeleton_t                      *skeletons[MAX_EDICTS];
 
        // copies of some vars that were former read from sv
        int                                     num_edicts;
@@ -679,4 +681,9 @@ void VM_Warning(const char *fmt, ...) DP_FUNC_PRINTF(1);
 // TODO: fill in the params
 //void PRVM_Create();
 
+void VM_GenerateFrameGroupBlend(framegroupblend_t *framegroupblend, const prvm_edict_t *ed);
+void VM_FrameBlendFromFrameGroupBlend(frameblend_t *frameblend, const framegroupblend_t *framegroupblend, const dp_model_t *model);
+void VM_UpdateEdictSkeleton(prvm_edict_t *ed, const dp_model_t *edmodel, const frameblend_t *frameblend);
+void VM_RemoveEdictSkeleton(prvm_edict_t *ed);
+
 #endif
index f7caf5773b670a16b3cd05e8833df94db70047df..da7085d71e9cd5c51a3a8ec428bd0030d88f35bf 100644 (file)
@@ -2033,7 +2033,7 @@ void EntityState5_WriteUpdate(int number, const entity_state_t *s, int changedbi
        {
                bits = changedbits;
                if ((bits & E5_ORIGIN) && (!(s->flags & RENDER_LOWPRECISION) || s->exteriormodelforclient || s->tagentity || s->viewmodelforclient || (s->number >= 1 && s->number <= svs.maxclients) || s->origin[0] <= -4096.0625 || s->origin[0] >= 4095.9375 || s->origin[1] <= -4096.0625 || s->origin[1] >= 4095.9375 || s->origin[2] <= -4096.0625 || s->origin[2] >= 4095.9375))
-               // maybe also add: ((model = sv.models[s->modelindex]) != NULL && model->name[0] == '*')
+               // maybe also add: ((model = SV_GetModelByIndex(s->modelindex)) != NULL && model->name[0] == '*')
                        bits |= E5_ORIGIN32;
                        // possible values:
                        //   negative origin:
index fedb47690e578b075dabbab5127b14a847171029..23133207bfd7e5de4238497465a2fef35bdb94dc 100644 (file)
@@ -49,6 +49,189 @@ void VM_CheckEmptyString (const char *s)
                PRVM_ERROR ("%s: Bad string", PRVM_NAME);
 }
 
+void VM_GenerateFrameGroupBlend(framegroupblend_t *framegroupblend, const prvm_edict_t *ed)
+{
+       prvm_eval_t *val;
+       // self.frame is the interpolation target (new frame)
+       // self.frame1time is the animation base time for the interpolation target
+       // self.frame2 is the interpolation start (previous frame)
+       // self.frame2time is the animation base time for the interpolation start
+       // self.lerpfrac is the interpolation strength for self.frame2
+       // self.lerpfrac3 is the interpolation strength for self.frame3
+       // self.lerpfrac4 is the interpolation strength for self.frame4
+       // pitch angle on a player model where the animator set up 5 sets of
+       // animations and the csqc simply lerps between sets)
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame))) framegroupblend[0].frame = (int) val->_float;
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2))) framegroupblend[1].frame = (int) val->_float;
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame3))) framegroupblend[2].frame = (int) val->_float;
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame4))) framegroupblend[3].frame = (int) val->_float;
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame1time))) framegroupblend[0].start = val->_float;
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2time))) framegroupblend[1].start = val->_float;
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame3time))) framegroupblend[2].start = val->_float;
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame4time))) framegroupblend[3].start = val->_float;
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac))) framegroupblend[1].lerp = val->_float;
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac3))) framegroupblend[2].lerp = val->_float;
+       if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac4))) framegroupblend[3].lerp = val->_float;
+       // assume that the (missing) lerpfrac1 is whatever remains after lerpfrac2+lerpfrac3+lerpfrac4 are summed
+       framegroupblend[0].lerp = 1 - framegroupblend[1].lerp - framegroupblend[2].lerp - framegroupblend[3].lerp;
+}
+
+// LordHavoc: quite tempting to break apart this function to reuse the
+//            duplicated code, but I suspect it is better for performance
+//            this way
+void VM_FrameBlendFromFrameGroupBlend(frameblend_t *frameblend, const framegroupblend_t *framegroupblend, const dp_model_t *model)
+{
+       int sub2, numframes, f, i, k;
+       int isfirstframegroup = true;
+       int nolerp;
+       double sublerp, lerp, d;
+       const animscene_t *scene;
+       const framegroupblend_t *g;
+       frameblend_t *blend = frameblend;
+
+       memset(blend, 0, MAX_FRAMEBLENDS * sizeof(*blend));
+
+       if (!model || !model->surfmesh.isanimated)
+       {
+               blend[0].lerp = 1;
+               return;
+       }
+
+       nolerp = (model->type == mod_sprite) ? !r_lerpsprites.integer : !r_lerpmodels.integer;
+       numframes = model->numframes;
+       for (k = 0, g = framegroupblend;k < MAX_FRAMEGROUPBLENDS;k++, g++)
+       {
+               f = g->frame;
+               if ((unsigned int)f >= (unsigned int)numframes)
+               {
+                       Con_DPrintf("VM_FrameBlendFromFrameGroupBlend: no such frame %d in model %s\n", f, model->name);
+                       f = 0;
+               }
+               d = lerp = g->lerp;
+               if (lerp <= 0)
+                       continue;
+               if (nolerp)
+               {
+                       if (isfirstframegroup)
+                       {
+                               d = lerp = 1;
+                               isfirstframegroup = false;
+                       }
+                       else
+                               continue;
+               }
+               if (model->animscenes)
+               {
+                       scene = model->animscenes + f;
+                       f = scene->firstframe;
+                       if (scene->framecount > 1)
+                       {
+                               // this code path is only used on .zym models and torches
+                               sublerp = scene->framerate * (cl.time - g->start);
+                               f = (int) floor(sublerp);
+                               sublerp -= f;
+                               sub2 = f + 1;
+                               if (sublerp < (1.0 / 65536.0f))
+                                       sublerp = 0;
+                               if (sublerp > (65535.0f / 65536.0f))
+                                       sublerp = 1;
+                               if (nolerp)
+                                       sublerp = 0;
+                               if (scene->loop)
+                               {
+                                       f = (f % scene->framecount);
+                                       sub2 = (sub2 % scene->framecount);
+                               }
+                               f = bound(0, f, (scene->framecount - 1)) + scene->firstframe;
+                               sub2 = bound(0, sub2, (scene->framecount - 1)) + scene->firstframe;
+                               d = sublerp * lerp;
+                               // two framelerps produced from one animation
+                               if (d > 0)
+                               {
+                                       for (i = 0;i < MAX_FRAMEBLENDS;i++)
+                                       {
+                                               if (blend[i].lerp <= 0 || blend[i].subframe == sub2)
+                                               {
+                                                       blend[i].subframe = sub2;
+                                                       blend[i].lerp += d;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               d = (1 - sublerp) * lerp;
+                       }
+               }
+               if (d > 0)
+               {
+                       for (i = 0;i < MAX_FRAMEBLENDS;i++)
+                       {
+                               if (blend[i].lerp <= 0 || blend[i].subframe == f)
+                               {
+                                       blend[i].subframe = f;
+                                       blend[i].lerp += d;
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
+void VM_UpdateEdictSkeleton(prvm_edict_t *ed, const dp_model_t *edmodel, const frameblend_t *frameblend)
+{
+       if (ed->priv.server->skeleton.model != edmodel)
+       {
+               VM_RemoveEdictSkeleton(ed);
+               ed->priv.server->skeleton.model = edmodel;
+       }
+       if (!ed->priv.server->skeleton.relativetransforms && ed->priv.server->skeleton.model && ed->priv.server->skeleton.model->num_bones)
+               ed->priv.server->skeleton.relativetransforms = Mem_Alloc(prog->progs_mempool, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t));
+       if (ed->priv.server->skeleton.relativetransforms)
+       {
+               int skeletonindex = 0;
+               skeleton_t *skeleton;
+               prvm_eval_t *val;
+               if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.skeletonindex))) skeletonindex = (int)val->_float;
+               if (skeletonindex > 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones)
+               {
+                       // custom skeleton controlled by the game (FTE_CSQC_SKELETONOBJECTS)
+                       memcpy(ed->priv.server->skeleton.relativetransforms, skeleton->relativetransforms, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t));
+               }
+               else
+               {
+                       // generated skeleton from frame animation
+                       int blendindex;
+                       int bonenum;
+                       int numbones = ed->priv.server->skeleton.model->num_bones;
+                       const float *poses = ed->priv.server->skeleton.model->data_poses;
+                       const float *framebones;
+                       float lerp;
+                       matrix4x4_t *relativetransforms = ed->priv.server->skeleton.relativetransforms;
+                       matrix4x4_t matrix;
+                       memset(relativetransforms, 0, numbones * sizeof(matrix4x4_t));
+                       for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
+                       {
+                               lerp = frameblend[blendindex].lerp;
+                               framebones = poses + 12 * frameblend[blendindex].subframe * numbones;
+                               for (bonenum = 0;bonenum < numbones;bonenum++)
+                               {
+                                       Matrix4x4_FromArray12FloatD3D(&matrix, framebones + 12 * bonenum);
+                                       Matrix4x4_Accumulate(&ed->priv.server->skeleton.relativetransforms[bonenum], &matrix, lerp);
+                               }
+                       }
+               }
+       }
+}
+
+void VM_RemoveEdictSkeleton(prvm_edict_t *ed)
+{
+       if (ed->priv.server->skeleton.relativetransforms)
+               Mem_Free(ed->priv.server->skeleton.relativetransforms);
+       memset(&ed->priv.server->skeleton, 0, sizeof(ed->priv.server->skeleton));
+}
+
+
+
+
 //============================================================================
 //BUILT-IN FUNCTIONS
 
index 123052e4b80b7cbc40efb5e1a1f19943cb63f208..6ba12f52d08db59105c2fb76babd00fd92ba0796 100644 (file)
@@ -1626,6 +1626,7 @@ void PRVM_FindOffsets(void)
        prog->fieldoffsets.rendermode                     = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
        prog->fieldoffsets.scale                          = PRVM_ED_FindFieldOffset("scale");
        prog->fieldoffsets.shadertime                     = PRVM_ED_FindFieldOffset("shadertime");
+       prog->fieldoffsets.skeletonindex                  = PRVM_ED_FindFieldOffset("skeletonindex");
        prog->fieldoffsets.style                          = PRVM_ED_FindFieldOffset("style");
        prog->fieldoffsets.tag_entity                     = PRVM_ED_FindFieldOffset("tag_entity");
        prog->fieldoffsets.tag_index                      = PRVM_ED_FindFieldOffset("tag_index");
index 271fc0547291e462d8fa057826620e669b3c4306..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,105 +0,0 @@
-#include "quakedef.h"
-
-// LordHavoc: quite tempting to break apart this function to reuse the
-//            duplicated code, but I suspect it is better for performance
-//            this way
-// LordHavoc: later note: made FRAMEBLENDINSERT macro
-void R_LerpAnimation(entity_render_t *r)
-{
-       int sub2, numframes, f, i, k;
-       int isfirstframegroup = true;
-       int nolerp;
-       double sublerp, lerp, d;
-       animscene_t *scene;
-       framegroupblend_t *g;
-       frameblend_t *blend;
-       dp_model_t *model = r->model;
-
-       memset(r->frameblend, 0, sizeof(r->frameblend));
-
-       if (!model || !model->surfmesh.isanimated)
-       {
-               r->frameblend[0].lerp = 1;
-               return;
-       }
-
-       blend = r->frameblend;
-       nolerp = (model->type == mod_sprite) ? !r_lerpsprites.integer : !r_lerpmodels.integer;
-       numframes = model->numframes;
-       for (k = 0, g = r->framegroupblend;k < MAX_FRAMEGROUPBLENDS;k++, g++)
-       {
-               if ((unsigned int)g->frame >= (unsigned int)numframes)
-               {
-                       Con_DPrintf("CL_LerpAnimation: no such frame %d\n", g->frame);
-                       g->frame = 0;
-               }
-               f = g->frame;
-               d = lerp = g->lerp;
-               if (lerp <= 0)
-                       continue;
-               if (nolerp)
-               {
-                       if (isfirstframegroup)
-                       {
-                               d = lerp = 1;
-                               isfirstframegroup = false;
-                       }
-                       else
-                               continue;
-               }
-               if (model->animscenes)
-               {
-                       scene = model->animscenes + f;
-                       f = scene->firstframe;
-                       if (scene->framecount > 1)
-                       {
-                               // this code path is only used on .zym models and torches
-                               sublerp = scene->framerate * (cl.time - g->start);
-                               f = (int) floor(sublerp);
-                               sublerp -= f;
-                               sub2 = f + 1;
-                               if (sublerp < (1.0 / 65536.0f))
-                                       sublerp = 0;
-                               if (sublerp > (65535.0f / 65536.0f))
-                                       sublerp = 1;
-                               if (nolerp)
-                                       sublerp = 0;
-                               if (scene->loop)
-                               {
-                                       f = (f % scene->framecount);
-                                       sub2 = (sub2 % scene->framecount);
-                               }
-                               f = bound(0, f, (scene->framecount - 1)) + scene->firstframe;
-                               sub2 = bound(0, sub2, (scene->framecount - 1)) + scene->firstframe;
-                               d = sublerp * lerp;
-                               // two framelerps produced from one animation
-                               if (d > 0)
-                               {
-                                       for (i = 0;i < MAX_FRAMEBLENDS;i++)
-                                       {
-                                               if (blend[i].lerp <= 0 || blend[i].subframe == sub2)
-                                               {
-                                                       blend[i].subframe = sub2;
-                                                       blend[i].lerp += d;
-                                                       break;
-                                               }
-                                       }
-                               }
-                               d = (1 - sublerp) * lerp;
-                       }
-               }
-               if (d > 0)
-               {
-                       for (i = 0;i < MAX_FRAMEBLENDS;i++)
-                       {
-                               if (blend[i].lerp <= 0 || blend[i].subframe == f)
-                               {
-                                       blend[i].subframe = f;
-                                       blend[i].lerp += d;
-                                       break;
-                               }
-                       }
-               }
-       }
-}
-
index 2a24e0c2c41ec289bcb3634b2b56601778187c7d..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,8 +0,0 @@
-
-#ifndef R_LERPANIM_H
-#define R_LERPANIM_H
-
-void R_LerpAnimation(entity_render_t *r);
-
-#endif
-
index 71a2d80afceb0d976e37e36fc07502986729d4d3..969e130a957a732b0d60a21f5d3c3c2c773ee6b9 100644 (file)
--- a/render.h
+++ b/render.h
@@ -315,6 +315,7 @@ typedef struct rsurfacestate_s
        float inversematrixscale;
        // animation blending state from entity
        frameblend_t frameblend[MAX_FRAMEBLENDS];
+       skeleton_t *skeleton;
        // directional model shading state from entity
        vec3_t modellight_ambient;
        vec3_t modellight_diffuse;
index 2faca6306d6e2d64d3d1a135683fd7141e4bf806..6f281456f8941b9cde27008749a5d5e656230c47 100644 (file)
--- a/server.h
+++ b/server.h
@@ -495,6 +495,9 @@ int SV_SoundIndex(const char *s, int precachemode);
 
 int SV_ParticleEffectIndex(const char *name);
 
+dp_model_t *SV_GetModelByIndex(int modelindex);
+dp_model_t *SV_GetModelFromEdict(prvm_edict_t *ed);
+
 void SV_SetIdealPitch (void);
 
 void SV_AddUpdates (void);
index 4d4bf704db54132541787447f283430737558d13..9be23f2b30a331562557e743f6109b066ed60b43 100644 (file)
@@ -25,6 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "snd_ogg.h"
 #include "snd_modplug.h"
 #include "csprogs.h"
+#include "cl_collision.h"
 
 
 #define SND_MIN_SPEED 8000
@@ -1251,8 +1252,10 @@ void SND_Spatialize(channel_t *ch, qboolean isstatic)
                }
                else if (cl.entities[ch->entnum].state_current.active)
                {
+                       dp_model_t *model;
                        //Con_Printf("-- entnum %i origin %f %f %f neworigin %f %f %f\n", ch->entnum, ch->origin[0], ch->origin[1], ch->origin[2], cl.entities[ch->entnum].state_current.origin[0], cl.entities[ch->entnum].state_current.origin[1], cl.entities[ch->entnum].state_current.origin[2]);
-                       if (cl.entities[ch->entnum].state_current.modelindex && cl.model_precache[cl.entities[ch->entnum].state_current.modelindex] && cl.model_precache[cl.entities[ch->entnum].state_current.modelindex]->soundfromcenter)
+                       model = CL_GetModelByIndex(cl.entities[ch->entnum].state_current.modelindex);
+                       if (model && model->soundfromcenter)
                                VectorMAM(0.5f, cl.entities[ch->entnum].render.mins, 0.5f, cl.entities[ch->entnum].render.maxs, ch->origin);
                        else
                                Matrix4x4_OriginFromMatrix(&cl.entities[ch->entnum].render.matrix, ch->origin);
index 8096d5588bd29f6dd1d882678f1fcad7429f887c..32d64e20e0b9eef4c94c51efd0c5a67b17198219 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -1063,7 +1063,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c
        // LordHavoc: this could kill tags attached to an invisible entity, I
        // just hope we never have to support that case
        i = (int)ent->fields.server->modelindex;
-       modelindex = (i >= 1 && i < MAX_MODELS && ent->fields.server->model && *PRVM_GetString(ent->fields.server->model)) ? i : 0;
+       modelindex = (i >= 1 && i < MAX_MODELS && ent->fields.server->model && *PRVM_GetString(ent->fields.server->model) && sv.models[i]) ? i : 0;
 
        flags = 0;
        i = (int)(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_size)->_float * 0.25f);
@@ -1235,7 +1235,7 @@ static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *c
        // calculate the visible box of this entity (don't use the physics box
        // as that is often smaller than a model, and would not count
        // specialvisibilityradius)
-       if ((model = sv.models[modelindex]) && (model->type != mod_null))
+       if ((model = SV_GetModelByIndex(modelindex)) && (model->type != mod_null))
        {
                float scale = cs->scale * (1.0f / 16.0f);
                if (cs->angles[0] || cs->angles[2]) // pitch and roll
@@ -1353,7 +1353,6 @@ qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmin
        dp_model_t *model;
        prvm_edict_t *touch;
        prvm_edict_t *touchedicts[MAX_EDICTS];
-       unsigned int modelindex;
        vec3_t boxmins, boxmaxs;
        vec3_t clipboxmins, clipboxmaxs;
        vec3_t endpoints[MAX_LINEOFSIGHTTRACES];
@@ -1402,13 +1401,8 @@ qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmin
                touch = touchedicts[touchindex];
                if (touch->fields.server->solid != SOLID_BSP)
                        continue;
-               modelindex = (unsigned int)touch->fields.server->modelindex;
-               if (!modelindex)
-                       continue;
-               if (modelindex >= MAX_MODELS)
-                       continue; // error?
-               model = sv.models[(int)touch->fields.server->modelindex];
-               if (!model->brush.TraceLineOfSight)
+               model = SV_GetModelFromEdict(touch);
+               if (!model || !model->brush.TraceLineOfSight)
                        continue;
                // skip obviously transparent entities
                alpha = PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.alpha)->_float;
@@ -1432,8 +1426,7 @@ qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmin
                for (touchindex = 0;touchindex < numtouchedicts;touchindex++)
                {
                        touch = touchedicts[touchindex];
-                       modelindex = (unsigned int)touch->fields.server->modelindex;
-                       model = (modelindex >= 1 && modelindex < MAX_MODELS) ? sv.models[(int)touch->fields.server->modelindex] : NULL;
+                       model = SV_GetModelFromEdict(touch);
                        if(model && model->brush.TraceLineOfSight)
                        {
                                // get the entity matrix
@@ -1494,7 +1487,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s)
                if (!s->modelindex && s->specialvisibilityradius == 0)
                        return;
 
-               isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*';
+               isbmodel = (model = SV_GetModelByIndex(s->modelindex)) != NULL && model->name[0] == '*';
                // viewmodels don't have visibility checking
                if (s->viewmodelforclient)
                {
@@ -2770,6 +2763,20 @@ int SV_ParticleEffectIndex(const char *name)
        return 0;
 }
 
+dp_model_t *SV_GetModelByIndex(int modelindex)
+{
+       return (modelindex > 0 && modelindex < MAX_MODELS) ? sv.models[modelindex] : NULL;
+}
+
+dp_model_t *SV_GetModelFromEdict(prvm_edict_t *ed)
+{
+       int modelindex;
+       if (!ed || ed->priv.server->free)
+               return NULL;
+       modelindex = (int)ed->fields.server->modelindex;
+       return (modelindex > 0 && modelindex < MAX_MODELS) ? sv.models[modelindex] : NULL;
+}
+
 /*
 ================
 SV_CreateBaseline
@@ -3296,6 +3303,7 @@ static void SV_VM_CB_FreeEdict(prvm_edict_t *ed)
        ed->fields.server->nextthink = -1;
        ed->fields.server->solid = 0;
 
+       VM_RemoveEdictSkeleton(ed);
        World_Physics_RemoveFromEntity(&sv.world, ed);
        World_Physics_RemoveJointFromEntity(&sv.world, ed);
 
index bd8b50ce30262ed9a36b8092fd8f49a009552867..6c9d2db7cb34d12fe765139734133c6e156fff30 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -46,9 +46,8 @@ void SV_Physics_Toss (prvm_edict_t *ent);
 int SV_GetPitchSign(prvm_edict_t *ent)
 {
        dp_model_t *model;
-       int modelindex;
        if (
-                       ((modelindex = (int)ent->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[modelindex]))
+                       (model = SV_GetModelFromEdict(ent))
                        ?
                        model->type == mod_alias
                        :
@@ -214,10 +213,7 @@ trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int
                model = NULL;
                if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
                {
-                       unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
-                       // if the modelindex is 0, it shouldn't be SOLID_BSP!
-                       if (modelindex > 0 && modelindex < MAX_MODELS)
-                               model = sv.models[(int)touch->fields.server->modelindex];
+                       model = SV_GetModelFromEdict(touch);
                        pitchsign = SV_GetPitchSign(touch);
                }
                if (model)
@@ -225,10 +221,13 @@ trace_t SV_TracePoint(const vec3_t start, int type, prvm_edict_t *passedict, int
                else
                        Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
                Matrix4x4_Invert_Simple(&imatrix, &matrix);
+               VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
+               VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
+               VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
                if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
-                       Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipstart, hitsupercontentsmask);
                else
-                       Collision_ClipPointToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
+                       Collision_ClipPointToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, hitsupercontentsmask);
 
                Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
        }
@@ -377,10 +376,7 @@ trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_
                model = NULL;
                if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
                {
-                       unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
-                       // if the modelindex is 0, it shouldn't be SOLID_BSP!
-                       if (modelindex > 0 && modelindex < MAX_MODELS)
-                               model = sv.models[(int)touch->fields.server->modelindex];
+                       model = SV_GetModelFromEdict(touch);
                        pitchsign = SV_GetPitchSign(touch);
                }
                if (model)
@@ -388,10 +384,13 @@ trace_t SV_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_
                else
                        Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
                Matrix4x4_Invert_Simple(&imatrix, &matrix);
+               VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
+               VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
+               VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
                if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
-                       Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
                else
-                       Collision_ClipLineToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
+                       Collision_ClipLineToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipend, hitsupercontentsmask);
 
                Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
        }
@@ -577,11 +576,7 @@ trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, co
                model = NULL;
                if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
                {
-                       unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
-                       // if the modelindex is 0, it shouldn't be SOLID_BSP!
-                       if (modelindex > 0 && modelindex < MAX_MODELS)
-                               model = sv.models[(int)touch->fields.server->modelindex];
-                       //pitchsign = 1;
+                       model = SV_GetModelFromEdict(touch);
                        pitchsign = SV_GetPitchSign(touch);
                }
                if (model)
@@ -589,10 +584,13 @@ trace_t SV_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, co
                else
                        Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
                Matrix4x4_Invert_Simple(&imatrix, &matrix);
+               VM_GenerateFrameGroupBlend(touch->priv.server->framegroupblend, touch);
+               VM_FrameBlendFromFrameGroupBlend(touch->priv.server->frameblend, touch->priv.server->framegroupblend, model);
+               VM_UpdateEdictSkeleton(touch, model, touch->priv.server->frameblend);
                if (type == MOVE_MISSILE && (int)touch->fields.server->flags & FL_MONSTER)
-                       Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
                else
-                       Collision_ClipToGenericEntity(&trace, model, (int) touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
+                       Collision_ClipToGenericEntity(&trace, model, touch->priv.server->frameblend, &touch->priv.server->skeleton, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
 
                Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
        }
@@ -635,7 +633,6 @@ int SV_PointSuperContents(const vec3_t point)
        matrix4x4_t matrix, imatrix;
        // model of other entity
        dp_model_t *model;
-       unsigned int modelindex;
        int frame;
        // list of entities to test for collisions
        int numtouchedicts;
@@ -666,10 +663,7 @@ int SV_PointSuperContents(const vec3_t point)
                        continue;
 
                // might interact, so do an exact clip
-               modelindex = (unsigned int)touch->fields.server->modelindex;
-               if (modelindex >= MAX_MODELS)
-                       continue;
-               model = sv.models[(int)touch->fields.server->modelindex];
+               model = SV_GetModelFromEdict(touch);
                if (!model || !model->PointSuperContents)
                        continue;
                Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
@@ -795,6 +789,7 @@ void SV_LinkEdict (prvm_edict_t *ent)
 {
        dp_model_t *model;
        vec3_t mins, maxs;
+       int modelindex;
 
        if (ent == prog->edicts)
                return;         // don't add the world
@@ -802,6 +797,18 @@ void SV_LinkEdict (prvm_edict_t *ent)
        if (ent->priv.server->free)
                return;
 
+       modelindex = (int)ent->fields.server->modelindex;
+       if (modelindex < 0 || modelindex >= MAX_MODELS)
+       {
+               Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
+               modelindex = 0;
+       }
+       model = SV_GetModelByIndex(modelindex);
+
+       VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
+       VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
+       VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
+
 // set the abs box
 
        if (ent->fields.server->movetype == MOVETYPE_PHYSICS)
@@ -814,13 +821,6 @@ void SV_LinkEdict (prvm_edict_t *ent)
        }
        else if (ent->fields.server->solid == SOLID_BSP)
        {
-               int modelindex = (int)ent->fields.server->modelindex;
-               if (modelindex < 0 || modelindex >= MAX_MODELS)
-               {
-                       Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
-                       modelindex = 0;
-               }
-               model = sv.models[modelindex];
                if (model != NULL)
                {
                        if (!model->TraceBox && developer.integer >= 1)
@@ -1592,7 +1592,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
                return;
        }
-       pushermodel = sv.models[index];
+       pushermodel = SV_GetModelByIndex(index);
        pusherowner = pusher->fields.server->owner;
        pusherprog = PRVM_EDICT_TO_PROG(pusher);
 
@@ -1664,9 +1664,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
        pusher->fields.server->ltime += movetime;
        SV_LinkEdict(pusher);
 
-       pushermodel = NULL;
-       if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
-               pushermodel = sv.models[(int)pusher->fields.server->modelindex];
+       pushermodel = SV_GetModelFromEdict(pusher);
        Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, pusher->fields.server->origin[0], pusher->fields.server->origin[1], pusher->fields.server->origin[2], pusher->fields.server->angles[0], pusher->fields.server->angles[1], pusher->fields.server->angles[2], 1);
        Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
 
@@ -1710,7 +1708,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                // final position, move it
                if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
                {
-                       Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
+                       Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
                        //trace = SV_TraceBox(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
                        if (!trace.startsolid)
                        {
@@ -1768,7 +1766,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                        check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
 
                // if it is still inside the pusher, block
-               Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
+               Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
                if (trace.startsolid)
                {
                        // try moving the contacted entity a tiny bit further to account for precision errors
@@ -1783,7 +1781,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                                continue;
                        }
                        pusher->fields.server->solid = savesolid;
-                       Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
+                       Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
                        if (trace.startsolid)
                        {
                                // try moving the contacted entity a tiny bit less to account for precision errors
@@ -1797,7 +1795,7 @@ void SV_PushMove (prvm_edict_t *pusher, float movetime)
                                        continue;
                                }
                                pusher->fields.server->solid = savesolid;
-                               Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
+                               Collision_ClipToGenericEntity(&trace, pushermodel, pusher->priv.server->frameblend, &pusher->priv.server->skeleton, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
                                if (trace.startsolid)
                                {
                                        // still inside pusher, so it's really blocked
index fe837a009b7579276110713a0caf913d28945631..6dc99e5c215462f7997815f0ac60e128aac2bec7 100644 (file)
@@ -19,7 +19,10 @@ char *vm_sv_extensions =
 "DP_CON_SET "
 "DP_CON_SETA "
 "DP_CON_STARTMAP "
+"DP_CSQC_ENTITYNOCULL "
+"DP_CSQC_ENTITYTRANSPARENTSORTING_OFFSET "
 "DP_CSQC_MULTIFRAME_INTERPOLATION "
+"DP_CSQC_SPAWNPARTICLE "
 "DP_EF_ADDITIVE "
 "DP_EF_BLUE "
 "DP_EF_DOUBLESIDED "
@@ -46,14 +49,15 @@ char *vm_sv_extensions =
 "DP_GFX_EXTERNALTEXTURES "
 "DP_GFX_EXTERNALTEXTURES_PERMAP "
 "DP_GFX_FOG "
+"DP_GFX_MODEL_INTERPOLATION "
 "DP_GFX_QUAKE3MODELTAGS "
 "DP_GFX_SKINFILES "
 "DP_GFX_SKYBOX "
-"DP_GFX_MODEL_INTERPOLATION "
 "DP_HALFLIFE_MAP "
 "DP_HALFLIFE_MAP_CVAR "
 "DP_HALFLIFE_SPRITE "
 "DP_INPUTBUTTONS "
+"DP_LIGHTSTYLE_STATICVALUE "
 "DP_LITSPRITES "
 "DP_LITSUPPORT "
 "DP_MONSTERWALK "
@@ -75,9 +79,9 @@ char *vm_sv_extensions =
 "DP_QC_ETOS "
 "DP_QC_EXTRESPONSEPACKET "
 "DP_QC_FINDCHAIN "
-"DP_QC_FINDCHAIN_TOFIELD "
 "DP_QC_FINDCHAINFLAGS "
 "DP_QC_FINDCHAINFLOAT "
+"DP_QC_FINDCHAIN_TOFIELD "
 "DP_QC_FINDFLAGS "
 "DP_QC_FINDFLOAT "
 "DP_QC_FS_SEARCH "
@@ -117,6 +121,7 @@ char *vm_sv_extensions =
 "DP_QUAKE3_MAP "
 "DP_QUAKE3_MODEL "
 "DP_REGISTERCVAR "
+"DP_SKELETONOBJECTS "
 "DP_SND_DIRECTIONLESSATTNNONE "
 "DP_SND_FAKETRACKS "
 "DP_SND_OGGVORBIS "
@@ -170,9 +175,9 @@ char *vm_sv_extensions =
 "DP_TE_STANDARDEFFECTBUILTINS "
 "DP_TRACE_HITCONTENTSMASK_SURFACEINFO "
 "DP_VIEWZOOM "
-"DP_LIGHTSTYLE_STATICVALUE "
 "EXT_BITSHIFT "
 "FRIK_FILE "
+"FTE_CSQC_SKELETONOBJECTS "
 "FTE_QC_CHECKPVS "
 "FTE_STRINGS "
 "KRIMZON_SV_PARSECLIENTCOMMAND "
@@ -184,9 +189,6 @@ char *vm_sv_extensions =
 "TENEBRAE_GFX_DLIGHTS "
 "TW_SV_STEPCONTROL "
 "ZQ_PAUSE "
-"DP_CSQC_SPAWNPARTICLE "
-"DP_CSQC_ENTITYTRANSPARENTSORTING_OFFSET "
-"DP_CSQC_ENTITYNOCULL "
 //"EXT_CSQC " // not ready yet
 ;
 
@@ -304,7 +306,7 @@ static void VM_SV_setmodel (void)
        e->fields.server->model = PRVM_SetEngineString(sv.model_precache[i]);
        e->fields.server->modelindex = i;
 
-       mod = sv.models[i];
+       mod = SV_GetModelByIndex(i);
 
        if (mod)
        {
@@ -2280,16 +2282,7 @@ void clippointtosurface(dp_model_t *model, msurface_t *surface, vec3_t p, vec3_t
        }
 }
 
-static dp_model_t *getmodel(prvm_edict_t *ed)
-{
-       int modelindex;
-       if (!ed || ed->priv.server->free)
-               return NULL;
-       modelindex = (int)ed->fields.server->modelindex;
-       if (modelindex < 1 || modelindex >= MAX_MODELS)
-               return NULL;
-       return sv.models[modelindex];
-}
+#define getmodel SV_GetModelFromEdict
 
 static msurface_t *getsurface(dp_model_t *model, int surfacenum)
 {
@@ -2533,7 +2526,6 @@ static void VM_SV_setattachment (void)
        prvm_edict_t *tagentity = PRVM_G_EDICT(OFS_PARM1);
        const char *tagname = PRVM_G_STRING(OFS_PARM2);
        prvm_eval_t *v;
-       int modelindex;
        dp_model_t *model;
        VM_SAFEPARMCOUNT(3, VM_SV_setattachment);
 
@@ -2560,8 +2552,8 @@ static void VM_SV_setattachment (void)
                v->_float = 0;
        if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0])
        {
-               modelindex = (int)tagentity->fields.server->modelindex;
-               if (modelindex >= 0 && modelindex < MAX_MODELS && (model = sv.models[modelindex]))
+               model = SV_GetModelFromEdict(tagentity);
+               if (model)
                {
                        v->_float = Mod_Alias_GetTagIndexForName(model, (int)tagentity->fields.server->skin, tagname);
                        if (v->_float == 0)
@@ -2578,37 +2570,26 @@ static void VM_SV_setattachment (void)
 int SV_GetTagIndex (prvm_edict_t *e, const char *tagname)
 {
        int i;
-       dp_model_t *model;
 
        i = (int)e->fields.server->modelindex;
        if (i < 1 || i >= MAX_MODELS)
                return -1;
-       model = sv.models[i];
 
-       return Mod_Alias_GetTagIndexForName(model, (int)e->fields.server->skin, tagname);
+       return Mod_Alias_GetTagIndexForName(SV_GetModelByIndex(i), (int)e->fields.server->skin, tagname);
 }
 
 int SV_GetExtendedTagInfo (prvm_edict_t *e, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix)
 {
        int r;
        dp_model_t *model;
-       int frame;
-       int modelindex;
 
        *tagname = NULL;
        *parentindex = 0;
        Matrix4x4_CreateIdentity(tag_localmatrix);
 
-       if (tagindex >= 0
-        && (modelindex = (int)e->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS
-        && (model = sv.models[(int)e->fields.server->modelindex])
-        && model->animscenes)
+       if (tagindex >= 0 && (model = SV_GetModelFromEdict(e)) && model->num_bones)
        {
-               frame = (int)e->fields.server->frame;
-               if (frame < 0 || frame >= model->numframes)
-                       frame = 0;
-
-               r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)e->fields.server->skin, model->animscenes[frame].firstframe, tagindex - 1, parentindex, tagname, tag_localmatrix);
+               r = Mod_Alias_GetExtendedTagInfoForIndex(model, (int)e->fields.server->skin, e->priv.server->frameblend, &e->priv.server->skeleton, tagindex - 1, parentindex, tagname, tag_localmatrix);
 
                if(!r) // success?
                        *parentindex += 1;
@@ -2641,19 +2622,13 @@ void SV_GetEntityMatrix (prvm_edict_t *ent, matrix4x4_t *out, qboolean viewmatri
 
 int SV_GetEntityLocalTagMatrix(prvm_edict_t *ent, int tagindex, matrix4x4_t *out)
 {
-       int modelindex;
-       int frame;
        dp_model_t *model;
-       if (tagindex >= 0
-        && (modelindex = (int)ent->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS
-        && (model = sv.models[(int)ent->fields.server->modelindex])
-        && model->animscenes)
+       if (tagindex >= 0 && (model = SV_GetModelFromEdict(ent)) && model->num_bones)
        {
-               // if model has wrong frame, engine automatically switches to model first frame
-               frame = (int)ent->fields.server->frame;
-               if (frame < 0 || frame >= model->numframes)
-                       frame = 0;
-               return Mod_Alias_GetTagMatrix(model, model->animscenes[frame].firstframe, tagindex, out);
+               VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
+               VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
+               VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
+               return Mod_Alias_GetTagMatrix(model, ent->priv.server->frameblend, &ent->priv.server->skeleton, tagindex, out);
        }
        *out = identitymatrix;
        return 0;
@@ -2688,7 +2663,11 @@ int SV_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex)
        if (modelindex <= 0 || modelindex >= MAX_MODELS)
                return 3;
 
-       model = sv.models[modelindex];
+       model = SV_GetModelByIndex(modelindex);
+
+       VM_GenerateFrameGroupBlend(ent->priv.server->framegroupblend, ent);
+       VM_FrameBlendFromFrameGroupBlend(ent->priv.server->frameblend, ent->priv.server->framegroupblend, model);
+       VM_UpdateEdictSkeleton(ent, model, ent->priv.server->frameblend);
 
        tagmatrix = identitymatrix;
        // DP_GFX_QUAKE3MODELTAGS, scan all chain and stop on unattached entity
@@ -2757,7 +2736,7 @@ static void VM_SV_gettagindex (void)
 {
        prvm_edict_t *ent;
        const char *tag_name;
-       int modelindex, tag_index;
+       int tag_index;
 
        VM_SAFEPARMCOUNT(2, VM_SV_gettagindex);
 
@@ -2766,25 +2745,24 @@ static void VM_SV_gettagindex (void)
 
        if (ent == prog->edicts)
        {
-               VM_Warning("gettagindex: can't affect world entity\n");
+               VM_Warning("VM_SV_gettagindex(entity #%i): can't affect world entity\n", PRVM_NUM_FOR_EDICT(ent));
                return;
        }
        if (ent->priv.server->free)
        {
-               VM_Warning("gettagindex: can't affect free entity\n");
+               VM_Warning("VM_SV_gettagindex(entity #%i): can't affect free entity\n", PRVM_NUM_FOR_EDICT(ent));
                return;
        }
 
-       modelindex = (int)ent->fields.server->modelindex;
        tag_index = 0;
-       if (modelindex <= 0 || modelindex >= MAX_MODELS)
-               Con_DPrintf("gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
+       if (!SV_GetModelFromEdict(ent))
+               Con_DPrintf("VM_SV_gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent));
        else
        {
                tag_index = SV_GetTagIndex(ent, tag_name);
                if (tag_index == 0)
                        if(developer.integer >= 100)
-                               Con_Printf("gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
+                               Con_Printf("VM_SV_gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name);
        }
        PRVM_G_FLOAT(OFS_RETURN) = tag_index;
 }
@@ -2801,6 +2779,7 @@ static void VM_SV_gettaginfo (void)
        int returncode;
        prvm_eval_t *val;
        vec3_t fo, le, up, trans;
+       const dp_model_t *model;
 
        VM_SAFEPARMCOUNT(2, VM_SV_gettaginfo);
 
@@ -2810,6 +2789,10 @@ static void VM_SV_gettaginfo (void)
        returncode = SV_GetTagMatrix(&tag_matrix, e, tagindex);
        Matrix4x4_ToVectors(&tag_matrix, prog->globals.server->v_forward, le, prog->globals.server->v_up, PRVM_G_VECTOR(OFS_RETURN));
        VectorScale(le, -1, prog->globals.server->v_right);
+       model = SV_GetModelFromEdict(e);
+       VM_GenerateFrameGroupBlend(e->priv.server->framegroupblend, e);
+       VM_FrameBlendFromFrameGroupBlend(e->priv.server->frameblend, e->priv.server->framegroupblend, model);
+       VM_UpdateEdictSkeleton(e, model, e->priv.server->frameblend);
        SV_GetExtendedTagInfo(e, tagindex, &parentindex, &tagname, &tag_localmatrix);
        Matrix4x4_ToVectors(&tag_localmatrix, fo, le, up, trans);
 
@@ -2958,7 +2941,7 @@ static void VM_SV_setmodelindex (void)
        e->fields.server->model = PRVM_SetEngineString(sv.model_precache[i]);
        e->fields.server->modelindex = i;
 
-       mod = sv.models[i];
+       mod = SV_GetModelByIndex(i);
 
        if (mod)
        {
@@ -3073,6 +3056,315 @@ static void VM_SV_setpause(void) {
        MSG_WriteByte(&sv.reliable_datagram, sv.paused);
 }
 
+// #263 float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS) 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.
+static void VM_SV_skel_create(void)
+{
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
+       dp_model_t *model = SV_GetModelByIndex(modelindex);
+       skeleton_t *skeleton;
+       int i;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (!model || !model->num_bones)
+               return;
+       for (i = 0;i < MAX_EDICTS;i++)
+               if (!prog->skeletons[i])
+                       break;
+       if (i == MAX_EDICTS)
+               return;
+       prog->skeletons[i] = skeleton = Mem_Alloc(cls.levelmempool, sizeof(skeleton_t) + model->num_bones * sizeof(matrix4x4_t));
+       skeleton->model = model;
+       skeleton->relativetransforms = (matrix4x4_t *)(skeleton+1);
+       // initialize to identity matrices
+       for (i = 0;i < skeleton->model->num_bones;i++)
+               skeleton->relativetransforms[i] = identitymatrix;
+       PRVM_G_FLOAT(OFS_RETURN) = i + 1;
+}
+
+// #264 float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS) 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
+static void VM_SV_skel_build(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       skeleton_t *skeleton;
+       prvm_edict_t *ed = PRVM_G_EDICT(OFS_PARM1);
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM2);
+       float retainfrac = PRVM_G_FLOAT(OFS_PARM3);
+       int firstbone = PRVM_G_FLOAT(OFS_PARM4);
+       int lastbone = PRVM_G_FLOAT(OFS_PARM5);
+       dp_model_t *model = SV_GetModelByIndex(modelindex);
+       float blendfrac;
+       int numblends;
+       int bonenum;
+       int blendindex;
+       framegroupblend_t framegroupblend[MAX_FRAMEGROUPBLENDS];
+       frameblend_t frameblend[MAX_FRAMEBLENDS];
+       matrix4x4_t blendedmatrix;
+       matrix4x4_t matrix;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       firstbone = max(0, firstbone);
+       lastbone = min(lastbone, model->num_bones - 1);
+       lastbone = min(lastbone, skeleton->model->num_bones - 1);
+       VM_GenerateFrameGroupBlend(framegroupblend, ed);
+       VM_FrameBlendFromFrameGroupBlend(frameblend, framegroupblend, model);
+       blendfrac = 1.0f - retainfrac;
+       for (numblends = 0;numblends < MAX_FRAMEBLENDS && frameblend[numblends].lerp;numblends++)
+               frameblend[numblends].lerp *= blendfrac;
+       for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
+       {
+               memset(&blendedmatrix, 0, sizeof(blendedmatrix));
+               Matrix4x4_Accumulate(&blendedmatrix, &skeleton->relativetransforms[bonenum], retainfrac);
+               for (blendindex = 0;blendindex < numblends;blendindex++)
+               {
+                       Matrix4x4_FromArray12FloatD3D(&matrix, model->data_poses + 12 * (frameblend[blendindex].subframe * model->num_bones + bonenum));
+                       Matrix4x4_Accumulate(&blendedmatrix, &matrix, frameblend[blendindex].lerp);
+               }
+               skeleton->relativetransforms[bonenum] = blendedmatrix;
+       }
+       PRVM_G_FLOAT(OFS_RETURN) = skeletonindex;
+}
+
+// #265 float(float skel) skel_get_numbones = #265; // (FTE_CSQC_SKELETONOBJECTS) returns how many bones exist in the created skeleton
+static void VM_SV_skel_get_numbones(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       skeleton_t *skeleton;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->num_bones;
+}
+
+// #266 string(float skel, float bonenum) skel_get_bonename = #266; // (FTE_CSQC_SKELETONOBJECTS) returns name of bone (as a tempstring)
+static void VM_SV_skel_get_bonename(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       PRVM_G_INT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(skeleton->model->data_bones[bonenum].name);
+}
+
+// #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (FTE_CSQC_SKELETONOBJECTS) 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)
+static void VM_SV_skel_get_boneparent(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       PRVM_G_FLOAT(OFS_RETURN) = skeleton->model->data_bones[bonenum].parent + 1;
+}
+
+// #268 float(float skel, string tagname) skel_find_bone = #268; // (FTE_CSQC_SKELETONOBJECTS) get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex
+static void VM_SV_skel_find_bone(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       const char *tagname = PRVM_G_STRING(OFS_PARM1);
+       skeleton_t *skeleton;
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       PRVM_G_FLOAT(OFS_RETURN) = Mod_Alias_GetTagIndexForName(skeleton->model, 0, tagname) + 1;
+}
+
+// #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
+static void VM_SV_skel_get_bonerel(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       vec3_t forward, left, up, origin;
+       VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+       VectorClear(prog->globals.client->v_forward);
+       VectorClear(prog->globals.client->v_right);
+       VectorClear(prog->globals.client->v_up);
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       matrix = skeleton->relativetransforms[bonenum];
+       Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
+       VectorCopy(forward, prog->globals.client->v_forward);
+       VectorNegate(left, prog->globals.client->v_right);
+       VectorCopy(up, prog->globals.client->v_up);
+       VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+// #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (FTE_CSQC_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
+static void VM_SV_skel_get_boneabs(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       matrix4x4_t temp;
+       vec3_t forward, left, up, origin;
+       VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+       VectorClear(prog->globals.client->v_forward);
+       VectorClear(prog->globals.client->v_right);
+       VectorClear(prog->globals.client->v_up);
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       matrix = skeleton->relativetransforms[bonenum];
+       // convert to absolute
+       while ((bonenum = skeleton->model->data_bones[bonenum].parent) >= 0)
+       {
+               temp = matrix;
+               Matrix4x4_Concat(&matrix, &skeleton->relativetransforms[bonenum], &temp);
+       }
+       Matrix4x4_ToVectors(&matrix, forward, left, up, origin);
+       VectorCopy(forward, prog->globals.client->v_forward);
+       VectorNegate(left, prog->globals.client->v_right);
+       VectorCopy(up, prog->globals.client->v_up);
+       VectorCopy(origin, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+// #271 void(float skel, float bonenum, vector org) skel_set_bone = #271; // (FTE_CSQC_SKELETONOBJECTS) set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
+static void VM_SV_skel_set_bone(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       vec3_t forward, left, up, origin;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       VectorCopy(prog->globals.client->v_forward, forward);
+       VectorNegate(prog->globals.client->v_right, left);
+       VectorCopy(prog->globals.client->v_up, up);
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
+       Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
+       skeleton->relativetransforms[bonenum] = matrix;
+}
+
+// #272 void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (FTE_CSQC_SKELETONOBJECTS) 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)
+static void VM_SV_skel_mul_bone(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int bonenum = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       vec3_t forward, left, up, origin;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       matrix4x4_t temp;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       if (bonenum < 0 || bonenum >= skeleton->model->num_bones)
+               return;
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM2), origin);
+       VectorCopy(prog->globals.client->v_forward, forward);
+       VectorNegate(prog->globals.client->v_right, left);
+       VectorCopy(prog->globals.client->v_up, up);
+       Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
+       temp = skeleton->relativetransforms[bonenum];
+       Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
+}
+
+// #273 void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // (FTE_CSQC_SKELETONOBJECTS) 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)
+static void VM_SV_skel_mul_bones(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int firstbone = PRVM_G_FLOAT(OFS_PARM1) - 1;
+       int lastbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
+       int bonenum;
+       vec3_t forward, left, up, origin;
+       skeleton_t *skeleton;
+       matrix4x4_t matrix;
+       matrix4x4_t temp;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       VectorCopy(PRVM_G_VECTOR(OFS_PARM3), origin);
+       VectorCopy(prog->globals.client->v_forward, forward);
+       VectorNegate(prog->globals.client->v_right, left);
+       VectorCopy(prog->globals.client->v_up, up);
+       Matrix4x4_FromVectors(&matrix, forward, left, up, origin);
+       firstbone = max(0, firstbone);
+       lastbone = min(lastbone, skeleton->model->num_bones - 1);
+       for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
+       {
+               temp = skeleton->relativetransforms[bonenum];
+               Matrix4x4_Concat(&skeleton->relativetransforms[bonenum], &matrix, &temp);
+       }
+}
+
+// #274 void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS) copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse
+static void VM_SV_skel_copybones(void)
+{
+       int skeletonindexdst = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       int skeletonindexsrc = (int)PRVM_G_FLOAT(OFS_PARM1) - 1;
+       int firstbone = PRVM_G_FLOAT(OFS_PARM2) - 1;
+       int lastbone = PRVM_G_FLOAT(OFS_PARM3) - 1;
+       int bonenum;
+       skeleton_t *skeletondst;
+       skeleton_t *skeletonsrc;
+       if (skeletonindexdst < 0 || skeletonindexdst >= MAX_EDICTS || !(skeletondst = prog->skeletons[skeletonindexdst]))
+               return;
+       if (skeletonindexsrc < 0 || skeletonindexsrc >= MAX_EDICTS || !(skeletonsrc = prog->skeletons[skeletonindexsrc]))
+               return;
+       firstbone = max(0, firstbone);
+       lastbone = min(lastbone, skeletondst->model->num_bones - 1);
+       lastbone = min(lastbone, skeletonsrc->model->num_bones - 1);
+       for (bonenum = firstbone;bonenum <= lastbone;bonenum++)
+               skeletondst->relativetransforms[bonenum] = skeletonsrc->relativetransforms[bonenum];
+}
+
+// #275 void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS) deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)
+static void VM_SV_skel_delete(void)
+{
+       int skeletonindex = (int)PRVM_G_FLOAT(OFS_PARM0) - 1;
+       skeleton_t *skeleton;
+       if (skeletonindex < 0 || skeletonindex >= MAX_EDICTS || !(skeleton = prog->skeletons[skeletonindex]))
+               return;
+       Mem_Free(skeleton);
+       prog->skeletons[skeletonindex] = NULL;
+}
+
+// #276 float(float modlindex, string framename) frameforname = #276; // (FTE_CSQC_SKELETONOBJECTS) finds number of a specified frame in the animation, returns -1 if no match found
+static void VM_SV_frameforname(void)
+{
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
+       dp_model_t *model = SV_GetModelByIndex(modelindex);
+       const char *name = PRVM_G_STRING(OFS_PARM1);
+       int i;
+       PRVM_G_FLOAT(OFS_RETURN) = -1;
+       if (!model || !model->animscenes)
+               return;
+       for (i = 0;i < model->numframes;i++)
+       {
+               if (!strcasecmp(model->animscenes[i].name, name))
+               {
+                       PRVM_G_FLOAT(OFS_RETURN) = i;
+                       break;
+               }
+       }
+}
+
+// #277 float(float modlindex, float framenum) frameduration = #277; // (FTE_CSQC_SKELETONOBJECTS) 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.
+static void VM_SV_frameduration(void)
+{
+       int modelindex = (int)PRVM_G_FLOAT(OFS_PARM0);
+       dp_model_t *model = SV_GetModelByIndex(modelindex);
+       int framenum = (int)PRVM_G_FLOAT(OFS_PARM1);
+       PRVM_G_FLOAT(OFS_RETURN) = 0;
+       if (!model || !model->animscenes || framenum < 0 || framenum >= model->numframes)
+               return;
+       if (model->animscenes[framenum].framerate)
+               PRVM_G_FLOAT(OFS_RETURN) = model->animscenes[framenum].framecount / model->animscenes[framenum].framerate;
+}
+
+
 prvm_builtin_t vm_sv_builtins[] = {
 NULL,                                                  // #0 NULL function (not callable) (QUAKE)
 VM_makevectors,                                        // #1 void(vector ang) makevectors (QUAKE)
@@ -3339,21 +3631,21 @@ NULL,                                                   // #259
 NULL,                                                  // #260
 NULL,                                                  // #261
 NULL,                                                  // #262
-NULL,                                                  // #263
-NULL,                                                  // #264
-NULL,                                                  // #265
-NULL,                                                  // #266
-NULL,                                                  // #267
-NULL,                                                  // #268
-NULL,                                                  // #269
-NULL,                                                  // #270
-NULL,                                                  // #271
-NULL,                                                  // #272
-NULL,                                                  // #273
-NULL,                                                  // #274
-NULL,                                                  // #275
-NULL,                                                  // #276
-NULL,                                                  // #277
+VM_SV_skel_create,                             // #263 float(float modlindex) skel_create = #263; // (DP_SKELETONOBJECTS) 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.
+VM_SV_skel_build,                              // #264 float(float skel, entity ent, float modlindex, float retainfrac, float firstbone, float lastbone) skel_build = #264; // (DP_SKELETONOBJECTS) 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
+VM_SV_skel_get_numbones,               // #265 float(float skel) skel_get_numbones = #265; // (DP_SKELETONOBJECTS) returns how many bones exist in the created skeleton
+VM_SV_skel_get_bonename,               // #266 string(float skel, float bonenum) skel_get_bonename = #266; // (DP_SKELETONOBJECTS) returns name of bone (as a tempstring)
+VM_SV_skel_get_boneparent,             // #267 float(float skel, float bonenum) skel_get_boneparent = #267; // (DP_SKELETONOBJECTS) returns parent num for supplied bonenum, -1 if bonenum has no parent or bone does not exist (returned value is always less than bonenum, you can loop on this)
+VM_SV_skel_find_bone,                  // #268 float(float skel, string tagname) skel_find_bone = #268; // (DP_SKELETONOBJECTS) get number of bone with specified name, 0 on failure, tagindex (bonenum+1) on success, same as using gettagindex on the modelindex
+VM_SV_skel_get_bonerel,                        // #269 vector(float skel, float bonenum) skel_get_bonerel = #269; // (DP_SKELETONOBJECTS) get matrix of bone in skeleton relative to its parent - sets v_forward, v_right, v_up, returns origin (relative to parent bone)
+VM_SV_skel_get_boneabs,                        // #270 vector(float skel, float bonenum) skel_get_boneabs = #270; // (DP_SKELETONOBJECTS) get matrix of bone in skeleton in model space - sets v_forward, v_right, v_up, returns origin (relative to entity)
+VM_SV_skel_set_bone,                   // #271 void(float skel, float bonenum, vector org) skel_set_bone = #271; // (DP_SKELETONOBJECTS) set matrix of bone relative to its parent, reads v_forward, v_right, v_up, takes origin as parameter (relative to parent bone)
+VM_SV_skel_mul_bone,                   // #272 void(float skel, float bonenum, vector org) skel_mul_bone = #272; // (DP_SKELETONOBJECTS) 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)
+VM_SV_skel_mul_bones,                  // #273 void(float skel, float startbone, float endbone, vector org) skel_mul_bones = #273; // (DP_SKELETONOBJECTS) 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)
+VM_SV_skel_copybones,                  // #274 void(float skeldst, float skelsrc, float startbone, float endbone) skel_copybones = #274; // (DP_SKELETONOBJECTS) copy bone matrices (relative to their parents) from one skeleton to another, useful for copying a skeleton to a corpse
+VM_SV_skel_delete,                             // #275 void(float skel) skel_delete = #275; // (DP_SKELETONOBJECTS) deletes skeleton at the beginning of the next frame (you can add the entity, delete the skeleton, renderscene, and it will still work)
+VM_SV_frameforname,                            // #276 float(float modlindex, string framename) frameforname = #276; // (DP_SKELETONOBJECTS) finds number of a specified frame in the animation, returns -1 if no match found
+VM_SV_frameduration,                   // #277 float(float modlindex, float framenum) frameduration = #277; // (DP_SKELETONOBJECTS) 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.
 NULL,                                                  // #278
 NULL,                                                  // #279
 NULL,                                                  // #280
diff --git a/world.c b/world.c
index 9fd89d766926ebfffe105631c1320c09d9a31748..3977a5db107e4c4cc7977868ae196531a98885bb 100644 (file)
--- a/world.c
+++ b/world.c
@@ -1973,19 +1973,12 @@ static void World_Physics_Frame_BodyFromEntity(world_t *world, prvm_edict_t *ed)
                val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.modelindex);
                if (val)
                        modelindex = (int)val->_float;
-               if (world == &sv.world && modelindex >= 1 && modelindex < MAX_MODELS)
-               {
-                       model = sv.models[modelindex];
-               }
-               else if (world == &cl.world && modelindex >= 1 && modelindex < MAX_MODELS)
-               {
-                       model = cl.model_precache[modelindex];
-               }
+               if (world == &sv.world)
+                       model = SV_GetModelByIndex(modelindex);
+               else if (world == &cl.world)
+                       model = CL_GetModelByIndex(modelindex);
                else
-               {
                        model = NULL;
-                       modelindex = 0;
-               }
                if (model)
                {
                        VectorScale(model->normalmins, scale, entmins);