Add alternative model lerping mode for proper compat with csqc/menuqc.

This commit is contained in:
Shpoike 2020-07-23 20:28:19 +01:00
parent e63dae5f49
commit e39c88a58b
3 changed files with 129 additions and 62 deletions

View File

@ -5787,10 +5787,10 @@ static void PF_m_addentity(void)
eval_t *origin = GetEdictFieldValue(ed, ED_FindFieldOffset("origin"));
eval_t *angles = GetEdictFieldValue(ed, ED_FindFieldOffset("angles"));
eval_t *frame = GetEdictFieldValue(ed, ED_FindFieldOffset("frame"));
// eval_t *frame2 = GetEdictFieldValue(ed, ED_FindFieldOffset("frame2"));
// eval_t *lerpfrac = GetEdictFieldValue(ed, ED_FindFieldOffset("lerpfrac"));
// eval_t *frame1time = GetEdictFieldValue(ed, ED_FindFieldOffset("frame1time"));
// eval_t *frame2time = GetEdictFieldValue(ed, ED_FindFieldOffset("frame2time"));
eval_t *frame2 = GetEdictFieldValue(ed, ED_FindFieldOffset("frame2"));
eval_t *lerpfrac = GetEdictFieldValue(ed, ED_FindFieldOffset("lerpfrac"));
eval_t *frame1time = GetEdictFieldValue(ed, ED_FindFieldOffset("frame1time"));
eval_t *frame2time = GetEdictFieldValue(ed, ED_FindFieldOffset("frame2time"));
eval_t *skin = GetEdictFieldValue(ed, ED_FindFieldOffset("skin"));
eval_t *alpha = GetEdictFieldValue(ed, ED_FindFieldOffset("alpha"));
@ -5799,12 +5799,16 @@ static void PF_m_addentity(void)
if (angles)
VectorCopy(angles->vector, e->angles);
e->model = model;
e->frame = frame?frame->_float:0;
e->skinnum = skin?skin->_float:0;
e->alpha = alpha?ENTALPHA_ENCODE(alpha->_float):ENTALPHA_DEFAULT;
//can't exactly use currentpose/previous pose, as we don't know them.
e->lerpflags = LERP_RESETANIM|LERP_RESETMOVE;
e->lerpflags = LERP_EXPLICIT|LERP_RESETANIM|LERP_RESETMOVE;
e->frame = frame?frame->_float:0;
e->lerp.snap.frame2 = frame2?frame2->_float:0;
e->lerp.snap.lerpfrac = lerpfrac?lerpfrac->_float:0;
e->lerp.snap.time[0] = frame1time?frame1time->_float:0;
e->lerp.snap.time[1] = frame2time?frame2time->_float:0;
}
}
}
@ -6376,8 +6380,8 @@ static struct
{"callfunction", PF_callfunction, PF_callfunction, 605,PF_callfunction, 605, D("void(.../*, string funcname*/)", "Invokes the named function. The function name is always passed as the last parameter and must always be present. The others are passed to the named function as-is")},
{"isfunction", PF_isfunction, PF_isfunction, 607,PF_isfunction, 607, D("float(string s)", "Returns true if the named function exists and can be called with the callfunction builtin.")},
{"getresolution", PF_NoSSQC, PF_NoCSQC, 608,PF_cl_getresolution, 608, D("vector(float mode, float forfullscreen)", "Returns available/common video modes.")},
{"gethostcachevalue", PF_NoSSQC, PF_gethostcachevalue, 511,PF_gethostcachevalue, 611, D("float(float type)", "Returns number of servers known")},
{"getresolution", PF_NoSSQC, PF_cl_getresolution, 608,PF_cl_getresolution, 608, D("vector(float mode, float forfullscreen)", "Returns available/common video modes.")},
{"gethostcachevalue", PF_NoSSQC, PF_gethostcachevalue, 611,PF_gethostcachevalue, 611, D("float(float type)", "Returns number of servers known")},
{"gethostcachestring", PF_NoSSQC, PF_gethostcachestring, 612,PF_gethostcachestring, 612, D("string(float type, float hostnr)", "Retrieves some specific type of info about the specified server.")},
{"parseentitydata", PF_parseentitydata, NULL/*field hacks*/, 613,NULL, 613, D("float(entity e, string s, optional float offset)", "Reads a single entity's fields into an already-spawned entity. s should contain field pairs like in a saved game: {\"foo1\" \"bar\" \"foo2\" \"5\"}. Returns <=0 on failure, otherwise returns the offset in the string that was read to.")},
{"resethostcachemasks", PF_NoSSQC, PF_resethostcachemasks, 615,PF_resethostcachemasks, 615, D("void()", "Resets server listing filters.")},
@ -6558,6 +6562,8 @@ static void PF_checkbuiltin (void)
{ //but it will be defined if its actually executed.
if (extensionbuiltins[i].desc && !strncmp(extensionbuiltins[i].desc, "stub.", 5))
G_FLOAT(OFS_RETURN) = false; //pretend it won't work if it probably won't be useful
else if (!extensionbuiltins[i].menufunc)
G_FLOAT(OFS_RETURN) = false; //works, but not in this module
else
G_FLOAT(OFS_RETURN) = true;
break;
@ -6572,6 +6578,9 @@ static void PF_checkbuiltin (void)
{ //but it will be defined if its actually executed.
if (extensionbuiltins[i].desc && !strncmp(extensionbuiltins[i].desc, "stub.", 5))
G_FLOAT(OFS_RETURN) = false; //pretend it won't work if it probably won't be useful
else if ((qcvm == &cl.qcvm && !extensionbuiltins[i].ssqcfunc)
|| (qcvm == &sv.qcvm && !extensionbuiltins[i].csqcfunc))
G_FLOAT(OFS_RETURN) = false; //works, but not in this module
else
G_FLOAT(OFS_RETURN) = true;
break;

View File

@ -730,9 +730,9 @@ void GL_DrawAliasFrame (aliashdr_t *paliashdr, lerpdata_t lerpdata)
R_SetupAliasFrame -- johnfitz -- rewritten to support lerping
=================
*/
void R_SetupAliasFrame (aliashdr_t *paliashdr, int frame, lerpdata_t *lerpdata)
void R_SetupAliasFrame (aliashdr_t *paliashdr, entity_t *e, lerpdata_t *lerpdata)
{
entity_t *e = currententity;
int frame = e->frame;
int posenum, numposes;
if ((frame >= paliashdr->numframes) || (frame < 0))
@ -741,62 +741,106 @@ void R_SetupAliasFrame (aliashdr_t *paliashdr, int frame, lerpdata_t *lerpdata)
frame = 0;
}
posenum = paliashdr->frames[frame].firstpose;
numposes = paliashdr->frames[frame].numposes;
if (numposes > 1)
if (e->lerpflags & LERP_EXPLICIT)
{
float time = cl.time + e->syncbase; //Spike: Readded syncbase
if (time < 0)
time = 0; //just in case...
e->lerptime = paliashdr->frames[frame].interval; //FIXME: no per-frame intervals
posenum += (int)(time / e->lerptime) % numposes;
int frame2 = e->lerp.snap.frame2;
float frac = e->lerp.snap.lerpfrac;
float time = cl.time;
if ((frame2 >= paliashdr->numframes) || (frame2 < 0))
frame2 = 0;
if (paliashdr->frames[(frac > 0.5)?frame2:frame].numposes > 1)
{ //our stronger sequence is a framegroup, but we can only do two-way blends.
if (frac > 0.5)
{
frame = frame2;
time = e->lerp.snap.time[1];
}
else
time = e->lerp.snap.time[0];
if (time < 0)
time = 0; //just in case...
frac = (time / paliashdr->frames[frame].interval);
posenum = (int)frac;
lerpdata->blend = (frac - posenum);
posenum += paliashdr->frames[frame].firstpose;
numposes = paliashdr->frames[frame].numposes;
lerpdata->pose1 = (posenum)%numposes;
lerpdata->pose2 = (posenum+1)%numposes;
}
else
{
lerpdata->blend = frac;
lerpdata->pose1 = paliashdr->frames[frame].firstpose;
lerpdata->pose1 += (unsigned int)(e->lerp.snap.time[0]/paliashdr->frames[frame].interval) % paliashdr->frames[frame].numposes;
lerpdata->pose2 = paliashdr->frames[frame2].firstpose;
lerpdata->pose2 += (unsigned int)(e->lerp.snap.time[1]/paliashdr->frames[frame2].interval) % paliashdr->frames[frame].numposes;
}
}
else
e->lerptime = 0.1;
{
posenum = paliashdr->frames[frame].firstpose;
numposes = paliashdr->frames[frame].numposes;
if (e->lerpflags & LERP_RESETANIM) //kill any lerp in progress
{
e->lerpstart = 0;
e->previouspose = posenum;
e->currentpose = posenum;
e->lerpflags -= LERP_RESETANIM;
}
else if (e->currentpose != posenum) // pose changed, start new lerp
{
if (e->lerpflags & LERP_RESETANIM2) //defer lerping one more time
if (numposes > 1)
{
e->lerpstart = 0;
e->previouspose = posenum;
e->currentpose = posenum;
e->lerpflags -= LERP_RESETANIM2;
float time = cl.time + e->syncbase; //Spike: Readded syncbase
if (time < 0)
time = 0; //just in case...
e->lerp.state.lerptime = paliashdr->frames[frame].interval; //FIXME: no per-frame intervals
posenum += (int)(time / e->lerp.state.lerptime) % numposes;
}
else
e->lerp.state.lerptime = 0.1;
if (e->lerpflags & LERP_RESETANIM) //kill any lerp in progress
{
e->lerpstart = cl.time;
e->previouspose = e->currentpose;
e->currentpose = posenum;
e->lerp.state.lerpstart = 0;
e->lerp.state.previouspose = posenum;
e->lerp.state.currentpose = posenum;
e->lerpflags -= LERP_RESETANIM;
}
else if (e->lerp.state.currentpose != posenum) // pose changed, start new lerp
{
if (e->lerpflags & LERP_RESETANIM2) //defer lerping one more time
{
e->lerp.state.lerpstart = 0;
e->lerp.state.previouspose = posenum;
e->lerp.state.currentpose = posenum;
e->lerpflags -= LERP_RESETANIM2;
}
else
{
e->lerp.state.lerpstart = cl.time;
e->lerp.state.previouspose = e->lerp.state.currentpose;
e->lerp.state.currentpose = posenum;
}
}
//set up values
if (r_lerpmodels.value && !(e->model->flags & MOD_NOLERP && r_lerpmodels.value != 2))
{
if (e->lerpflags & LERP_FINISH && numposes == 1)
lerpdata->blend = CLAMP (0, (cl.time - e->lerp.state.lerpstart) / (e->lerpfinish - e->lerp.state.lerpstart), 1);
else
lerpdata->blend = CLAMP (0, (cl.time - e->lerp.state.lerpstart) / e->lerp.state.lerptime, 1);
lerpdata->pose1 = e->lerp.state.previouspose;
lerpdata->pose2 = e->lerp.state.currentpose;
}
else //don't lerp
{
lerpdata->blend = 1;
lerpdata->pose1 = posenum;
lerpdata->pose2 = posenum;
}
}
//set up values
if (r_lerpmodels.value && !(e->model->flags & MOD_NOLERP && r_lerpmodels.value != 2))
{
if (e->lerpflags & LERP_FINISH && numposes == 1)
lerpdata->blend = CLAMP (0, (cl.time - e->lerpstart) / (e->lerpfinish - e->lerpstart), 1);
else
lerpdata->blend = CLAMP (0, (cl.time - e->lerpstart) / e->lerptime, 1);
lerpdata->pose1 = e->previouspose;
lerpdata->pose2 = e->currentpose;
}
else //don't lerp
{
lerpdata->blend = 1;
lerpdata->pose1 = posenum;
lerpdata->pose2 = posenum;
}
if (paliashdr->numboneposes)
{
static bonepose_t inverted[256];
@ -1002,7 +1046,7 @@ void R_DrawAliasModel (entity_t *e)
// setup pose/lerp data -- do it first so we don't miss updates due to culling
//
paliashdr = (aliashdr_t *)Mod_Extradata (e->model);
R_SetupAliasFrame (paliashdr, e->frame, &lerpdata);
R_SetupAliasFrame (paliashdr, e, &lerpdata);
R_SetupEntityTransform (e, &lerpdata);
glsl = &r_alias_glsl[(paliashdr->poseverttype==PV_IQM)?ALIAS_GLSL_SKELETAL:ALIAS_GLSL_BASIC];
@ -1328,7 +1372,7 @@ void GL_DrawAliasShadow (entity_t *e)
if (entalpha == 0) return;
paliashdr = (aliashdr_t *)Mod_Extradata (e->model);
R_SetupAliasFrame (paliashdr, e->frame, &lerpdata);
R_SetupAliasFrame (paliashdr, e, &lerpdata);
R_SetupEntityTransform (e, &lerpdata);
R_LightPoint (e->origin);
lheight = currententity->origin[2] - lightspot[2];
@ -1375,7 +1419,7 @@ void R_DrawAliasModel_ShowTris (entity_t *e)
return;
paliashdr = (aliashdr_t *)Mod_Extradata (e->model);
R_SetupAliasFrame (paliashdr, e->frame, &lerpdata);
R_SetupAliasFrame (paliashdr, e, &lerpdata);
R_SetupEntityTransform (e, &lerpdata);
glPushMatrix ();

View File

@ -44,6 +44,7 @@ typedef struct efrag_s
#define LERP_RESETANIM2 (1<<2) //set this and previous flag to disable anim lerping for two anim frames
#define LERP_RESETMOVE (1<<3) //disable movement lerping until next origin/angles change
#define LERP_FINISH (1<<4) //use lerpfinish time from server update instead of assuming interval of 0.1
#define LERP_EXPLICIT (1<<5) //for csqc, using explicit frame1/2+frac+times
//johnfitz
typedef struct entity_s
@ -82,11 +83,24 @@ typedef struct entity_s
byte eflags; //spike -- mostly a mirror of netstate, but handles tag inheritance (eww!)
byte alpha; //johnfitz -- alpha
byte lerpflags; //johnfitz -- lerping
float lerpstart; //johnfitz -- animation lerping
float lerptime; //johnfitz -- animation lerping
union
{
struct
{ //read-only lerp data for csqc...
int frame2;
float lerpfrac;
float time[2];
} snap;
struct
{ //updated by the model rendering code.
float lerpstart; //johnfitz -- animation lerping
float lerptime; //johnfitz -- animation lerping
short previouspose; //johnfitz -- animation lerping
short currentpose; //johnfitz -- animation lerping
} state;
} lerp;
float lerpfinish; //johnfitz -- lerping -- server sent us a more accurate interval, use it instead of 0.1
short previouspose; //johnfitz -- animation lerping
short currentpose; //johnfitz -- animation lerping
// short futurepose; //johnfitz -- animation lerping
float movelerpstart; //johnfitz -- transform lerping
vec3_t previousorigin; //johnfitz -- transform lerping