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;
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)
cl.movement_queue[cl.movement_numqueue].crouch = buttoncrouch;
cl.movement_numqueue++;
}
- CL_ClientMovement_Replay();
+
+ cl.movement_replay = true;
}
typedef enum waterlevel_e
}
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)
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;
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
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);
/*
===============
-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])
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;
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)
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);
// 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;
void CL_Disconnect_f (void);
void CL_UpdateRenderEntity(entity_render_t *ent);
-void CL_UpdateEntities(void);
+void CL_UpdateViewEntities(void);
//
// cl_input
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);
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();
}