Add support for HLMDL actions, and some builtins to interact with it.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6148 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Eukara 2022-01-07 03:18:00 +00:00
parent f612b97fc9
commit 13b2a7935c
11 changed files with 174 additions and 21 deletions

View file

@ -3799,9 +3799,13 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
int numframes = 0;
float duration = 0;
qboolean loop = false;
if (!Mod_FrameInfoForNum(ent.model, mods->surfaceidx, mods->framegroup, &fname, &numframes, &duration, &loop))
int act = -1;
if (!Mod_FrameInfoForNum(ent.model, mods->surfaceidx, mods->framegroup, &fname, &numframes, &duration, &loop, &act))
fname = "Unknown Sequence";
Draw_FunString(0, y, va("Frame%i: %s (%i poses, %f of %f secs, %s)", mods->framegroup, fname, numframes, ent.framestate.g[FS_REG].frametime[0], duration, loop?"looped":"unlooped"));
if (act != -1)
Draw_FunString(0, y, va("Frame%i[%i]: %s (%i poses, %f of %f secs, %s)", mods->framegroup, act, fname, numframes, ent.framestate.g[FS_REG].frametime[0], duration, loop?"looped":"unlooped"));
else
Draw_FunString(0, y, va("Frame%i: %s (%i poses, %f of %f secs, %s)", mods->framegroup, fname, numframes, ent.framestate.g[FS_REG].frametime[0], duration, loop?"looped":"unlooped"));
y+=8;
}

View file

@ -7054,8 +7054,9 @@ static struct {
// {"skel_postmul_bones", PF_skel_postmul_bones, 0},//void(float skel, float startbone, float endbone, vector org) skel_mul_bone = #273; // (FTE_CSQC_SKELETONOBJECTS) (reads v_forward etc)
{"skel_copybones", PF_skel_copybones, 274},//void(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones = #274; // (FTE_CSQC_SKELETONOBJECTS)
{"skel_delete", PF_skel_delete, 275},//void(float skel) skel_delete = #275; // (FTE_CSQC_SKELETONOBJECTS)
{"frameforname", PF_frameforname, 276},//void(float modidx, string framename) frameforname = #276 (FTE_CSQC_SKELETONOBJECTS)
{"frameduration", PF_frameduration, 277},//void(float modidx, float framenum) frameduration = #277 (FTE_CSQC_SKELETONOBJECTS)
{"frameforname", PF_frameforname, 276},//float(float modidx, string framename) frameforname = #276 (FTE_CSQC_SKELETONOBJECTS)
{"frameduration", PF_frameduration, 277},//float(float modidx, float framenum) frameduration = #277 (FTE_CSQC_SKELETONOBJECTS)
{"frameforaction", PF_frameforaction, 0},//float(float modidx, string actionid) frameforaction = #0
{"processmodelevents", PF_processmodelevents, 0},
{"getnextmodelevent", PF_getnextmodelevent, 0},
{"getmodeleventidx", PF_getmodeleventidx, 0},

View file

@ -903,7 +903,8 @@ void skel_generateragdoll_f(void)
int numframes;
float duration;
qboolean loop;
if (!Mod_FrameInfoForNum(mod, 0, i, &fname, &numframes, &duration, &loop))
int act;
if (!Mod_FrameInfoForNum(mod, 0, i, &fname, &numframes, &duration, &loop, &act))
break;
VFS_PUTS(f, va("//%i %s (%i frames) (%f secs)%s", i, fname, numframes, duration, loop?" (loop)":""));
}
@ -2669,7 +2670,7 @@ void QCBUILTIN PF_gettagindex (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
void QCBUILTIN PF_frametoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
int modelindex = G_FLOAT(OFS_PARM0);
unsigned int skinnum = G_FLOAT(OFS_PARM1);
int surfaceidx = 0;
model_t *mod = w->Get_CModel(w, modelindex);
@ -2684,7 +2685,7 @@ void QCBUILTIN PF_frametoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
void QCBUILTIN PF_frameforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
int modelindex = G_FLOAT(OFS_PARM0);
int surfaceidx = 0;
const char *str = PF_VarString(prinst, 1, pr_globals);
model_t *mod = w->Get_CModel(w, modelindex);
@ -2694,10 +2695,23 @@ void QCBUILTIN PF_frameforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_
else
G_FLOAT(OFS_RETURN) = -1;
}
void QCBUILTIN PF_frameforaction (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
int modelindex = G_FLOAT(OFS_PARM0);
int surfaceidx = 0;
int actionid = G_INT(OFS_PARM1);
model_t *mod = w->Get_CModel(w, modelindex);
if (mod)
G_FLOAT(OFS_RETURN) = Mod_FrameNumForAction(mod, surfaceidx, actionid);
else
G_FLOAT(OFS_RETURN) = -1;
}
void QCBUILTIN PF_frameduration (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
int modelindex = G_FLOAT(OFS_PARM0);
unsigned int framenum = G_FLOAT(OFS_PARM1);
int surfaceidx = 0;
model_t *mod = w->Get_CModel(w, modelindex);
@ -2723,7 +2737,7 @@ void QCBUILTIN PF_modelframecount (pubprogfuncs_t *prinst, struct globalvars_s *
void QCBUILTIN PF_processmodelevents (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *w = prinst->parms->user;
unsigned int modelindex = G_FLOAT(OFS_PARM0);
int modelindex = G_FLOAT(OFS_PARM0);
unsigned int frame = G_FLOAT(OFS_PARM1);
float basetime = G_FLOAT(OFS_PARM2);
float targettime = G_FLOAT(OFS_PARM3);
@ -2786,8 +2800,9 @@ void QCBUILTIN PF_processmodelevents (pubprogfuncs_t *prinst, struct globalvars_
char *data;
float loopduration;
qboolean looping;
int act;
if (Mod_FrameInfoForNum(mod, 0, frame, &data, &code, &loopduration, &looping))
if (Mod_FrameInfoForNum(mod, 0, frame, &data, &code, &loopduration, &looping, &act))
{
if (looping && loopduration)
starttime = loopduration*(unsigned int)(basetime/loopduration);
@ -2891,8 +2906,9 @@ void QCBUILTIN PF_getnextmodelevent (pubprogfuncs_t *prinst, struct globalvars_s
char *data;
float loopduration;
qboolean looping;
int act;
if (!Mod_FrameInfoForNum(mod, 0, frame, &data, &code, &loopduration, &looping))
if (!Mod_FrameInfoForNum(mod, 0, frame, &data, &code, &loopduration, &looping, &act))
return; //invalid frame
if (looping && loopduration)

View file

@ -555,6 +555,7 @@ void Mod_Shutdown (qboolean final);
int Mod_TagNumForName(struct model_s *model, const char *name);
int Mod_SkinNumForName(struct model_s *model, int surfaceidx, const char *name);
int Mod_FrameNumForName(struct model_s *model, int surfaceidx, const char *name);
int Mod_FrameNumForAction(struct model_s *model, int surfaceidx, int actionid);
float Mod_GetFrameDuration(struct model_s *model, int surfaceidx, int frameno);
void Mod_ResortShaders(void);

View file

@ -2883,6 +2883,8 @@ typedef struct
unsigned int posecount;
float fps;
qboolean loop;
int action;
int actionweight;
char name[MAX_QPATH];
} frameinfo_t;
static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups)
@ -2923,11 +2925,25 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups)
frames[count].loop = true;
else
frames[count].loop = !!atoi(tok);
frames[count].action = -1;
frames[count].actionweight = 0;
Q_snprintfz(frames[count].name, sizeof(frames[count].name), "groupified_%d_anim", count); //to match DP. frameforname cares.
line = COM_ParseType(line, tok, sizeof(tok), &ttype);
if (ttype != TTP_EOF)
{
Q_strncpyz(frames[count].name, tok, sizeof(frames[count].name));
else
Q_snprintfz(frames[count].name, sizeof(frames[count].name), "groupified_%d_anim", count); //to match DP. frameforname cares.
line = COM_ParseType(line, tok, sizeof(tok), &ttype);
}
if (ttype != TTP_EOF)
{
frames[count].action = atoi(tok);
line = COM_ParseType(line, tok, sizeof(tok), &ttype);
}
if (ttype != TTP_EOF)
frames[count].actionweight = atoi(tok);
if (frames[count].posecount>0 && frames[count].fps)
count++;
@ -3509,6 +3525,8 @@ static void *Q1MDL_LoadFrameGroup (galiasinfo_t *galias, dmdl_t *pq1inmodel, mod
for (i = 0; i < pq1inmodel->numframes; i++)
{
frame->action = -1;
frame->actionweight = 0;
switch(LittleLong(pframetype->type))
{
case ALIAS_SINGLE:
@ -3979,6 +3997,8 @@ static void Mesh_HandleFramegroupsFile(model_t *mod, galiasinfo_t *galias)
o->numposes = p;
o->rate = framegroups[a].fps;
o->loop = framegroups[a].loop;
o->action = -1;
o->actionweight = 0;
Q_strncpyz(o->name, framegroups[a].name, sizeof(o->name));
}
galias->numanimations = numanims;
@ -4981,6 +5001,8 @@ static qboolean QDECL Mod_LoadKingpinModel (model_t *mod, void *buffer, size_t f
{
poutframe->poseofs = pose;
poutframe->numposes = 1;
poutframe->action = -1;
poutframe->actionweight = 0;
galias->numanimations++;
#ifndef SERVERONLY
@ -5490,6 +5512,47 @@ int Mod_FrameNumForName(model_t *model, int surfaceidx, const char *name)
return -1;
}
int Mod_FrameNumForAction(model_t *model, int surfaceidx, int actionid)
{
galiasanimation_t *group;
galiasinfo_t *inf;
int i;
float weight;
if (!model)
return -1;
#ifdef HALFLIFEMODELS
if (model->type == mod_halflife)
return HLMDL_FrameForAction(model, actionid);
#endif
if (model->type != mod_alias)
return -1;
inf = Mod_Extradata(model);
while(surfaceidx-->0 && inf)
inf = inf->nextsurf;
if (inf)
{
for (i = 0, weight = 0, group = inf->ofsanimations; i < inf->numanimations; i++, group++)
{
if (group->action == actionid)
weight += group->actionweight;
}
weight *= frandom();
for (i = 0, group = inf->ofsanimations; i < inf->numanimations; i++, group++)
{
if (group->action == actionid)
{
if (weight <= group->actionweight)
return i;
weight -= group->actionweight;
}
}
}
return -1;
}
qboolean Mod_GetModelEvent(model_t *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata)
{
@ -5586,7 +5649,7 @@ const char *Mod_FrameNameForNum(model_t *model, int surfaceidx, int num)
return NULL;
}
qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop)
qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop, int *act)
{
galiasanimation_t *group;
galiasinfo_t *inf;
@ -5615,11 +5678,12 @@ qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **nam
*numframes = group[num].numposes;
*loop = group[num].loop;
*duration = group->numposes/group->rate;
*act = group[num].action;
return true;
}
#ifdef HALFLIFEMODELS
if (model->type == mod_halflife)
return HLMDL_FrameInfoForNum(model, surfaceidx, num, name, numframes, duration, loop);
return HLMDL_FrameInfoForNum(model, surfaceidx, num, name, numframes, duration, loop, act);
#endif
return false;
}
@ -5766,7 +5830,8 @@ float Mod_GetFrameDuration(model_t *model, int surfaceidx, int frameno)
float duration;
char *name;
qboolean loop;
HLMDL_FrameInfoForNum(model, surfaceidx, frameno, &name, &unused, &duration, &loop);
int act;
HLMDL_FrameInfoForNum(model, surfaceidx, frameno, &name, &unused, &duration, &loop, &act);
return duration;
}
#endif
@ -6020,6 +6085,8 @@ static galiasinfo_t *Mod_LoadQ3ModelLod(model_t *mod, int *surfcount, void *buff
group->poseofs = pose + first;
group->loop = framegroups[i].loop;
group->events = NULL;
group->action = -1;
group->actionweight = 0;
group++;
}
}
@ -6033,6 +6100,8 @@ static galiasinfo_t *Mod_LoadQ3ModelLod(model_t *mod, int *surfcount, void *buff
group->poseofs = pose + i;
group->loop = false;
group->events = NULL;
group->action = -1;
group->actionweight = 0;
group++;
}
}
@ -6523,6 +6592,8 @@ static qboolean QDECL Mod_LoadZymoticModel(model_t *mod, void *buffer, size_t fs
grp->loop = !(BigLong(inscene->flags) & ZYMSCENEFLAG_NOLOOP);
grp->numposes = BigLong(inscene->length);
grp->boneofs = matrix + BigLong(inscene->start)*12*root->numbones;
grp->action = -1;
grp->actionweight = 0;
}
if (inscene != (zymscene_t*)((char*)header + header->lump_scenes.start+header->lump_scenes.length))
@ -7083,6 +7154,8 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize)
group[j].loop = frameinfo[j].loop;
group[j].rate = frameinfo[j].fps;
group[j].skeltype = SKEL_RELATIVE;
group[j].action = -1;
group[j].actionweight = 0;
}
num_animinfo = numgroups;
}
@ -7107,6 +7180,8 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize)
group[iframe].loop = true;
group[iframe].rate = animinfo[j].fps;
group[iframe].skeltype = SKEL_RELATIVE;
group[iframe].action = -1;
group[iframe].actionweight = 0;
iframe++;
}
}
@ -7126,6 +7201,8 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize)
group[i].loop = true;
group[i].rate = animinfo[i].fps;
group[i].skeltype = SKEL_RELATIVE;
group[i].action = -1;
group[i].actionweight = 0;
}
}
for (j = 0; j < num_animkeys; j += num_boneinfo)
@ -7153,6 +7230,8 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize)
group->loop = true;
group->rate = 10;
group->skeltype = SKEL_ABSOLUTE;
group->action = -1;
group->actionweight = 0;
}
@ -7451,6 +7530,8 @@ static qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t
outgroups[i].numposes = 1;
outgroups[i].skeltype = SKEL_RELATIVE;
outgroups[i].boneofs = outposedata;
outgroups[i].action = -1;
outgroups[i].actionweight = 0;
inposedata = (float*)((char*)buffer + inframes[i].ofs_bonepositions);
for (j = 0; j < header->num_bones*12; j++)
@ -7488,6 +7569,8 @@ static qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t
outgroups[i].loop = framegroups[i].loop;
outgroups[i].rate = framegroups[i].fps;
outgroups[i].events = NULL;
outgroups[i].action = -1;
outgroups[i].actionweight = 0;
Q_strncpyz(outgroups[i].name, framegroups[i].name, sizeof(outgroups[i].name));
}
}
@ -8290,6 +8373,8 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
framegroups[i].posecount = LittleLong(anim[i].num_frames);
framegroups[i].fps = LittleFloat(anim[i].framerate);
framegroups[i].loop = !!(LittleLong(anim[i].flags) & IQM_LOOP);
framegroups[i].action = -1;
framegroups[i].actionweight = 0;
Q_strncpyz(framegroups[i].name, strings+anim[i].name, sizeof(fgroup[i].name));
}
}
@ -8303,6 +8388,8 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
framegroups->posecount = 1;
framegroups->fps = 10;
framegroups->loop = 1;
framegroups->action = -1;
framegroups->actionweight = 0;
strcpy(framegroups->name, "base");
}
@ -8473,6 +8560,9 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
if (fgroup[i].rate <= 0)
fgroup[i].rate = 10;
fgroup[i].action = framegroups[i].action;
fgroup[i].actionweight = framegroups[i].actionweight;
}
free(framegroups);

View file

@ -62,6 +62,8 @@ typedef struct galiasanimation_s
int numposes;
//float *poseendtime; //first starts at 0, anim duration is poseendtime[numposes-1]
float rate; //average framerate of animation.
int action;
float actionweight;
#ifdef NONSKELETALMODELS
galiaspose_t *poseofs;
#endif
@ -270,7 +272,7 @@ typedef struct modplugfuncs_s
#define plugmodfuncs_name "Models_IDX" STRINGIFY(sizeof_index_t)
#endif
} plugmodfuncs_t;
#define MODPLUGFUNCS_VERSION 2
#define MODPLUGFUNCS_VERSION 3
#ifdef SKELETALMODELS
void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weights, int numweights, vecV_t *xyzout, vec3_t *normout);
@ -286,7 +288,7 @@ const char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num);
const char *Mod_SurfaceNameForNum(model_t *model, int num);
const char *Mod_FrameNameForNum(model_t *model, int surfaceidx, int num);
const char *Mod_SkinNameForNum(model_t *model, int surfaceidx, int num);
qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop);
qboolean Mod_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop, int *act);
qboolean Mod_DoCRC(model_t *mod, char *buffer, int buffersize);

View file

@ -315,6 +315,7 @@ void QCBUILTIN PF_setattachment(pubprogfuncs_t *prinst, struct globalvars_s *pr_
void QCBUILTIN PF_frametoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_skintoname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_frameforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_frameforaction (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_frameduration (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_modelframecount (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_skinforname (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);

View file

@ -612,6 +612,38 @@ int HLMDL_FrameForName(model_t *mod, const char *name)
return -1;
}
int HLMDL_FrameForAction(model_t *mod, int actionid)
{
int i;
hlmdl_header_t *h;
hlmdl_sequencelist_t *seqs;
hlmodel_t *mc;
int weight = 0;
if (!mod || mod->type != mod_halflife)
return -1; //halflife models only, please
mc = Mod_Extradata(mod);
h = mc->header;
seqs = (hlmdl_sequencelist_t*)((char*)h+h->seqindex);
//figure out the total weight.
for (i = 0; i < h->numseq; i++)
if (seqs[i].action == actionid)
weight += seqs[i].actionweight;
//pick a random number between 0 and the total weight...
weight *= frandom();
//now figure out which sequence that gives us.
for (i = 0; i < h->numseq; i++)
if (seqs[i].action == actionid)
{
if (weight <= seqs[i].actionweight)
return i;
weight -= seqs[i].actionweight;
}
return -1; //failed...
}
qboolean HLMDL_GetModelEvent(model_t *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata)
{
hlmodel_t *mc = Mod_Extradata(model);
@ -1146,7 +1178,7 @@ const char *HLMDL_FrameNameForNum(model_t *mod, int surfaceidx, int seqnum)
((unsigned int)seqnum>=model->header->numseq?0:seqnum);
return sequence->name;
}
qboolean HLMDL_FrameInfoForNum(model_t *mod, int surfaceidx, int seqnum, char **name, int *numframes, float *duration, qboolean *loop)
qboolean HLMDL_FrameInfoForNum(model_t *mod, int surfaceidx, int seqnum, char **name, int *numframes, float *duration, qboolean *loop, int *act)
{
hlmodel_t *model = Mod_Extradata(mod);
hlmdl_sequencelist_t *sequence = (hlmdl_sequencelist_t *) ((qbyte *) model->header + model->header->seqindex) +
@ -1156,6 +1188,7 @@ qboolean HLMDL_FrameInfoForNum(model_t *mod, int surfaceidx, int seqnum, char **
*numframes = sequence->numframes;
*duration = (sequence->numframes-1)/sequence->timing;
*loop = sequence->loop;
*act = sequence->action;
return true;
}

View file

@ -224,7 +224,8 @@ typedef struct
char name[32];
float timing;
int loop;
int unknown1[2];
int action;
int actionweight;
int num_events;
int ofs_events;
int numframes;
@ -330,8 +331,9 @@ void *Mod_GetHalfLifeModelData(model_t *mod);
//reflectioney things, including bone data
int HLMDL_BoneForName(model_t *mod, const char *name);
int HLMDL_FrameForName(model_t *mod, const char *name);
int HLMDL_FrameForAction(model_t *mod, int actionid);
const char *HLMDL_FrameNameForNum(model_t *model, int surfaceidx, int num);
qboolean HLMDL_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop);
qboolean HLMDL_FrameInfoForNum(model_t *model, int surfaceidx, int num, char **name, int *numframes, float *duration, qboolean *loop, int *act);
qboolean HLMDL_GetModelEvent(model_t *model, int animation, int eventidx, float *timestamp, int *eventcode, char **eventdata);
int HLMDL_GetNumBones(model_t *mod, qboolean tagstoo);
int HLMDL_GetBoneParent(model_t *mod, int bonenum);

View file

@ -11407,6 +11407,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"skel_delete", PF_skel_delete, 0, 0, 0, 275, D("void(float skel)", "Deletes a skeletal object. The actual delete is delayed, allowing the skeletal object to be deleted in an entity's predraw function yet still be valid by the time the addentity+renderscene builtins need it. Also uninstanciates any ragdoll currently in effect on the skeletal object.")}, // (FTE_CSQC_SKELETONOBJECTS)
{"frameforname", PF_frameforname, 0, 0, 0, 276, D("float(float modidx, string framename)", "Looks up a framegroup from a model by name, avoiding the need for hardcoding. Returns -1 on error.")},// (FTE_CSQC_SKELETONOBJECTS)
{"frameduration", PF_frameduration, 0, 0, 0, 277, D("float(float modidx, float framenum)", "Retrieves the duration (in seconds) of the specified framegroup.")},// (FTE_CSQC_SKELETONOBJECTS)
{"frameforaction", PF_frameforaction, 0, 0, 0, 0, D("float(float modidx, int actionid)", "Returns a random frame/animation for the specified mod-defined action, or -1 if no animations have the specified action.")},
{"processmodelevents",PF_processmodelevents,0, 0, 0, 0, D("void(float modidx, float framenum, __inout float basetime, float targettime, void(float timestamp, int code, string data) callback)", "Calls a callback for each event that has been reached. Basetime is set to targettime.")},
{"getnextmodelevent",PF_getnextmodelevent,0, 0, 0, 0, D("float(float modidx, float framenum, __inout float basetime, float targettime, __out int code, __out string data)", "Reports the next event within a model's animation. Returns a boolean if an event was found between basetime and targettime. Writes to basetime,code,data arguments (if an event was found, basetime is set to the event's time, otherwise to targettime).\nWARNING: this builtin cannot deal with multiple events with the same timestamp (only the first will be reported).")},
{"getmodeleventidx",PF_getmodeleventidx,0, 0, 0, 0, D("float(float modidx, float framenum, int eventidx, __out float timestamp, __out int code, __out string data)", "Reports an indexed event within a model's animation. Writes to timestamp,code,data arguments on success. Returns false if the animation/event/model was out of range/invalid. Does not consider looping animations (retry from index 0 if it fails and you know that its a looping animation). This builtin is more annoying to use than getnextmodelevent, but can be made to deal with multiple events with the exact same timestamp.")},

View file

@ -3428,6 +3428,8 @@ static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize,
}
fg->loop = !!mod_gltf_loop->ival;
fg->skeltype = SKEL_RELATIVE;
fg->action = -1;
fg->actionweight = 0;
for(chan = JSON_FindIndexedChild(anim, "channels", 0); chan; chan = chan->sibling)
{
struct gltf_animsampler s;