From: havoc Date: Thu, 8 Mar 2007 23:36:23 +0000 (+0000) Subject: reworked prediction code to work better when riding lifts X-Git-Tag: xonotic-v0.1.0preview~3473 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=440894b67050bb1db5aa0a78b8a35a06944c6b7f;p=xonotic%2Fdarkplaces.git reworked prediction code to work better when riding lifts git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6943 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/cl_input.c b/cl_input.c index 47971a54..6ac140d6 100644 --- a/cl_input.c +++ b/cl_input.c @@ -560,8 +560,6 @@ void CL_UpdatePrydonCursor(void) cl.cmd.cursor_fraction = CL_SelectTraceLine(cl.cmd.cursor_start, cl.cmd.cursor_end, cl.cmd.cursor_impact, cl.cmd.cursor_normal, &cl.cmd.cursor_entitynumber, (chase_active.integer || cl.intermission) ? &cl.entities[cl.playerentity].render : NULL); } -void CL_ClientMovement_Replay(void); - void CL_ClientMovement_InputQW(void) { int i; @@ -594,7 +592,8 @@ void CL_ClientMovement_InputQW(void) cl.movement_queue[cl.movement_numqueue].crouch = false; cl.movement_numqueue++; } - CL_ClientMovement_Replay(); + + cl.movement_replay = true; } void CL_ClientMovement_Input(qboolean buttonjump, qboolean buttoncrouch) @@ -642,7 +641,8 @@ void CL_ClientMovement_Input(qboolean buttonjump, qboolean buttoncrouch) cl.movement_queue[cl.movement_numqueue].crouch = buttoncrouch; cl.movement_numqueue++; } - CL_ClientMovement_Replay(); + + cl.movement_replay = true; } typedef enum waterlevel_e @@ -1174,39 +1174,54 @@ void CL_ClientMovement_Replay(void) } CL_ClientMovement_UpdateStatus(&s); - // store replay location - if (cl.movecmd[0].time > cl.movecmd[1].time) + if (cl.movement_replay) { - cl.movement_time[1] = cl.movecmd[1].time; - cl.movement_time[0] = cl.movecmd[0].time; - cl.movement_time[2] = cl.time; - VectorCopy(cl.movement_origin, cl.movement_oldorigin); + cl.movement_replay = false; + // update interpolation timestamps if time has passed + if (cl.movecmd[0].time != cl.movecmd[1].time) + { + cl.movement_time[1] = cl.movecmd[1].time; + cl.movement_time[0] = cl.movecmd[0].time; + cl.movement_time[2] = cl.time; + VectorCopy(cl.movement_origin, cl.movement_oldorigin); + //VectorCopy(s.origin, cl.entities[cl.playerentity].state_current.origin); + //VectorSet(cl.entities[cl.playerentity].state_current.angles, 0, cl.viewangles[1], 0); + } + + // update the interpolation target position and velocity VectorCopy(s.origin, cl.movement_origin); VectorCopy(s.velocity, cl.movement_velocity); - //VectorCopy(s.origin, cl.entities[cl.playerentity].state_current.origin); - //VectorSet(cl.entities[cl.playerentity].state_current.angles, 0, cl.viewangles[1], 0); - - // update the onground flag if appropriate - // when not predicted, cl.onground is only cleared by cl_parse.c, but can - // be set forcefully here to hide server inconsistencies in the onground - // flag (such as when stepping up stairs, the onground flag tends to turn - // off briefly due to precision errors, particularly at high framerates), - // such inconsistencies can mess up the gun bobbing and stair smoothing, - // so they must be avoided. - if (cl.movement_predicted) - cl.onground = s.onground; - else if (s.onground) + } + + // update the onground flag if appropriate + if (cl.movement_predicted) + { + // when predicted we simply set the flag according to the UpdateStatus + cl.onground = s.onground; + } + else + { + // when not predicted, cl.onground is cleared by cl_parse.c each time + // an update packet is received, but can be forced on here to hide + // server inconsistencies in the onground flag + // (which mostly occur when stepping up stairs at very high framerates + // where after the step up the move continues forward and not + // downward so the ground is not detected) + // + // such onground inconsistencies can cause jittery gun bobbing and + // stair smoothing, so we set onground if UpdateStatus says so + if (s.onground) cl.onground = true; + } - // react to onground state changes (for gun bob) - if (cl.onground) - { - if (!cl.oldonground) - cl.hitgroundtime = cl.movecmd[0].time; - cl.lastongroundtime = cl.movecmd[0].time; - } - cl.oldonground = cl.onground; + // react to onground state changes (for gun bob) + if (cl.onground) + { + if (!cl.oldonground) + cl.hitgroundtime = cl.movecmd[0].time; + cl.lastongroundtime = cl.movecmd[0].time; } + cl.oldonground = cl.onground; } void QW_MSG_WriteDeltaUsercmd(sizebuf_t *buf, usercmd_t *from, usercmd_t *to) diff --git a/cl_main.c b/cl_main.c index 73566049..3a16b69f 100644 --- a/cl_main.c +++ b/cl_main.c @@ -760,7 +760,7 @@ extern void V_CalcViewBlend(void); extern void V_CalcRefdef(void); // note this is a recursive function, recursionlimit should be 32 or so on the initial call -void CL_UpdateNetworkEntity(entity_t *e, int recursionlimit) +void CL_UpdateNetworkEntity(entity_t *e, int recursionlimit, qboolean interpolate) { const matrix4x4_t *matrix; matrix4x4_t blendmatrix, tempmatrix, matrix2; @@ -830,7 +830,7 @@ void CL_UpdateNetworkEntity(entity_t *e, int recursionlimit) if (!t->state_current.active) return; // update the parent first - CL_UpdateNetworkEntity(t, recursionlimit - 1); + CL_UpdateNetworkEntity(t, recursionlimit - 1, interpolate); // make relative to the entity matrix = &t->render.matrix; // some properties of the tag entity carry over @@ -878,7 +878,7 @@ void CL_UpdateNetworkEntity(entity_t *e, int recursionlimit) VectorLerp(cl.movement_oldorigin, lerp, cl.movement_origin, origin); VectorSet(angles, 0, cl.viewangles[1], 0); } - else if (e->persistent.lerpdeltatime > 0 && (lerp = (cl.time - e->persistent.lerpstarttime) / e->persistent.lerpdeltatime) < 1) + else if (interpolate && e->persistent.lerpdeltatime > 0 && (lerp = (cl.time - e->persistent.lerpstarttime) / e->persistent.lerpdeltatime) < 1) { // interpolate the origin and angles lerp = max(0, lerp); @@ -1064,23 +1064,64 @@ void CL_UpdateNetworkEntityTrail(entity_t *e) /* =============== -CL_UpdateEntities +CL_UpdateViewEntities =============== */ -void CL_UpdateEntities(void) +void CL_UpdateViewEntities(void) +{ + int i; + // update any RENDER_VIEWMODEL entities to use the new view matrix + for (i = 1;i < cl.num_entities;i++) + { + if (cl.entities_active[i]) + { + entity_t *ent = cl.entities + i; + if ((ent->render.flags & RENDER_VIEWMODEL) || ent->state_current.tagentity) + CL_UpdateNetworkEntity(ent, 32, true); + } + } + // and of course the engine viewmodel needs updating as well + CL_UpdateNetworkEntity(&cl.viewent, 32, true); +} + +/* +=============== +CL_UpdateNetworkCollisionEntities +=============== +*/ +void CL_UpdateNetworkCollisionEntities(void) { entity_t *ent; int i; - // process network entities - // first link the player - CL_UpdateNetworkEntity(cl.entities + cl.viewentity, 32); + // start on the entity after the world + cl.num_brushmodel_entities = 0; + for (i = 1;i < cl.num_entities;i++) + { + if (cl.entities_active[i]) + { + ent = cl.entities + i; + if (ent->state_current.active && ent->render.model && ent->render.model->name[0] == '*' && ent->render.model->TraceBox) + { + // do not interpolate the bmodels for this + CL_UpdateNetworkEntity(ent, 32, false); + cl.brushmodel_entities[cl.num_brushmodel_entities++] = ent->state_current.number; + } + } + } +} - // set up the view - V_CalcRefdef(); +/* +=============== +CL_UpdateNetworkEntities +=============== +*/ +void CL_UpdateNetworkEntities(void) +{ + entity_t *ent; + int i; // start on the entity after the world - // skip the player entity because it was already processed for (i = 1;i < cl.num_entities;i++) { if (cl.entities_active[i]) @@ -1088,18 +1129,20 @@ void CL_UpdateEntities(void) ent = cl.entities + i; if (ent->state_current.active) { - CL_UpdateNetworkEntity(ent, 32); + CL_UpdateNetworkEntity(ent, 32, true); // view models should never create light/trails if (!(ent->render.flags & RENDER_VIEWMODEL)) CL_UpdateNetworkEntityTrail(ent); - if (ent->render.model && ent->render.model->name[0] == '*' && ent->render.model->TraceBox) - cl.brushmodel_entities[cl.num_brushmodel_entities++] = ent->state_current.number; } else cl.entities_active[i] = false; } } +} +void CL_UpdateViewModel(void) +{ + entity_t *ent; ent = &cl.viewent; ent->state_previous = ent->state_current; ent->state_current = defaultstate; @@ -1128,7 +1171,7 @@ void CL_UpdateEntities(void) ent->render.frame1time = ent->render.frame2time = cl.time; ent->render.framelerp = 1; } - CL_UpdateNetworkEntity(ent, 32); + CL_UpdateNetworkEntity(ent, 32, true); } // note this is a recursive function, but it can never get in a runaway loop (because of the delayedlink flags) @@ -1640,8 +1683,28 @@ int CL_ReadFromServer(void) V_DriftPitch(); V_FadeViewFlashs(); - // now update all the network entities and the view matrix - CL_UpdateEntities(); + if (cl.movement_predicted) + { + // if prediction is enabled we have to update all the collidable + // network entities before the prediction code can be run + CL_UpdateNetworkCollisionEntities(); + } + + // now update the player prediction + CL_ClientMovement_Replay(); + + // update the player entity (which may be predicted) + CL_UpdateNetworkEntity(cl.entities + cl.viewentity, 32, true); + + // now update the view (which depends on that player entity) + V_CalcRefdef(); + + // now update all the network entities and create particle trails + // (some entities may depend on the view) + CL_UpdateNetworkEntities(); + + // update the engine-based viewmodel + CL_UpdateViewModel(); CL_RelinkLightFlashes(); CSQC_RelinkAllEntities(ENTMASK_ENGINE | ENTMASK_ENGINEVIEWMODELS); diff --git a/client.h b/client.h index 31018173..5e9df7de 100644 --- a/client.h +++ b/client.h @@ -717,6 +717,8 @@ typedef struct client_state_s // these fields are only updated by CL_ClientMovement (called by CL_SendMove after parsing each network packet) // set by CL_ClientMovement_Replay functions qboolean movement_predicted; + // if true the CL_ClientMovement_Replay function will update origin, etc + qboolean movement_replay; // this is set true by svc_time parsing and causes a new movement to be // queued for prediction purposes qboolean movement_needupdate; @@ -1032,7 +1034,7 @@ void CL_Disconnect (void); void CL_Disconnect_f (void); void CL_UpdateRenderEntity(entity_render_t *ent); -void CL_UpdateEntities(void); +void CL_UpdateViewEntities(void); // // cl_input @@ -1058,6 +1060,7 @@ void CL_ParseTEnt (void); void CL_NewBeam (int ent, vec3_t start, vec3_t end, model_t *m, int lightning); void CL_RelinkBeams (void); void CL_Beam_CalculatePositions (const beam_t *b, vec3_t start, vec3_t end); +void CL_ClientMovement_Replay(void); void CL_ClearTempEntities (void); entity_t *CL_NewTempEntity (void); diff --git a/clvm_cmds.c b/clvm_cmds.c index f11af64d..03f3fe91 100644 --- a/clvm_cmds.c +++ b/clvm_cmds.c @@ -758,25 +758,13 @@ static void VM_CL_R_SetView (void) PRVM_G_FLOAT(OFS_RETURN) = 1; } -extern void CL_UpdateNetworkEntity(entity_t *e, int recursionlimit); //#304 void() renderscene (EXT_CSQC) static void VM_CL_R_RenderScene (void) { - int i; VM_SAFEPARMCOUNT(0, VM_CL_R_RenderScene); // we need to update any RENDER_VIEWMODEL entities at this point because // csqc supplies its own view matrix - for (i = 1;i < cl.num_entities;i++) - { - if (cl.entities_active[i]) - { - entity_t *ent = cl.entities + i; - if ((ent->render.flags & RENDER_VIEWMODEL) || ent->state_current.tagentity) - CL_UpdateNetworkEntity(ent, 32); - } - } - // and of course the engine viewmodel needs updating as well - CL_UpdateNetworkEntity(&cl.viewent, 32); + CL_UpdateViewEntities(); // now draw stuff! R_RenderView(); }