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);
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)
{
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);
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);
// 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;
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);
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);
// 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;
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);
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);
// 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;
*/
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++;
}
}
// 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
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);
// 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);
// 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)
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)
{
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;
}
// 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);
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);
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
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))
}
// 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
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;
{
int r;
dp_model_t *model;
- int frame;
*tagname = NULL;
*parentindex = 0;
&& (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;
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;
{
prvm_edict_t *ent;
const char *tag_name;
- int modelindex, tag_index;
+ int tag_index;
VM_SAFEPARMCOUNT(2, VM_CL_gettagindex);
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;
}
int returncode;
prvm_eval_t *val;
vec3_t fo, le, up, trans;
+ const dp_model_t *model;
VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo);
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);
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:
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
//===========================================
-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];
#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);
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];
#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);
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];
#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);
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);
}
// 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);
}
// 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);
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;
}
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);
}
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;
-}
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
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
{
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;
}
//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;
//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;
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)
{
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
{
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;
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;
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
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
}
}
-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;
}
}
-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;
}
}
-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)
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;
}
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)
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++)
}
}
-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;
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)
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;
{
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;
}
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)
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)
}
//#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;
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;
}
#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];
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;
}
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;
}
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
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;
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];
if (VectorCompare(start, end))
{
- Mod_Q3BSP_TracePoint(model, frame, trace, start, hitsupercontentsmask);
+ Mod_Q3BSP_TracePoint(model, frameblend, skeleton, trace, start, hitsupercontentsmask);
return;
}
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];
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;
model_brushq3_t;
struct frameblend_s;
+struct skeleton_s;
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)
// 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
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;
// 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);
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;
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
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;
// 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
{
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:
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
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");
-#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;
- }
- }
- }
- }
-}
-
-
-#ifndef R_LERPANIM_H
-#define R_LERPANIM_H
-
-void R_LerpAnimation(entity_render_t *r);
-
-#endif
-
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;
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);
#include "snd_ogg.h"
#include "snd_modplug.h"
#include "csprogs.h"
+#include "cl_collision.h"
#define SND_MIN_SPEED 8000
}
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);
// 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);
// 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
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];
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;
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
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)
{
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
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);
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
:
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)
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);
}
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)
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);
}
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)
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);
}
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;
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);
{
dp_model_t *model;
vec3_t mins, maxs;
+ int modelindex;
if (ent == prog->edicts)
return; // don't add the world
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)
}
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)
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);
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);
// 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)
{
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
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
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
"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 "
"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 "
"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 "
"DP_QUAKE3_MAP "
"DP_QUAKE3_MODEL "
"DP_REGISTERCVAR "
+"DP_SKELETONOBJECTS "
"DP_SND_DIRECTIONLESSATTNNONE "
"DP_SND_FAKETRACKS "
"DP_SND_OGGVORBIS "
"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 "
"TENEBRAE_GFX_DLIGHTS "
"TW_SV_STEPCONTROL "
"ZQ_PAUSE "
-"DP_CSQC_SPAWNPARTICLE "
-"DP_CSQC_ENTITYTRANSPARENTSORTING_OFFSET "
-"DP_CSQC_ENTITYNOCULL "
//"EXT_CSQC " // not ready yet
;
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)
{
}
}
-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)
{
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);
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)
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;
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;
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
{
prvm_edict_t *ent;
const char *tag_name;
- int modelindex, tag_index;
+ int tag_index;
VM_SAFEPARMCOUNT(2, VM_SV_gettagindex);
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;
}
int returncode;
prvm_eval_t *val;
vec3_t fo, le, up, trans;
+ const dp_model_t *model;
VM_SAFEPARMCOUNT(2, VM_SV_gettaginfo);
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);
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)
{
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)
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
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);