From 66ef8e16c1283799dbcce70421c659eac2242acc Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Thu, 5 Jul 2012 19:06:35 +0900 Subject: [PATCH] Fix the ghost entities in demo playback. Really, when cl_nodelta is in effect (eg, .qwd demo recording and thus playback). QW now uses the new shared entity state block as I'd intended. Thanks to the cleanup of ghost entities (ie, entities that have been removed but continue to be rendered), glsl overkill has gone from 157 to 163 fps :) --- include/client/entities.h | 4 - include/qw/protocol.h | 1 + nq/source/cl_ents.c | 6 +- qw/include/client.h | 3 + qw/source/cl_entparse.c | 67 ++++++++------- qw/source/cl_ents.c | 170 +++++++++++++++++++++++++++----------- 6 files changed, 169 insertions(+), 82 deletions(-) diff --git a/include/client/entities.h b/include/client/entities.h index 7fb867ca1..ae046b8c4 100644 --- a/include/client/entities.h +++ b/include/client/entities.h @@ -53,10 +53,6 @@ typedef struct entity_state_s { byte glow_size; byte glow_color; byte colormod; - - struct skin_s *skin; //FIXME this should not be here, but better state - //change tracking in the client is needed for this - //to be moved } entity_state_t; typedef struct { diff --git a/include/qw/protocol.h b/include/qw/protocol.h index cfd1c22b9..76f2c625d 100644 --- a/include/qw/protocol.h +++ b/include/qw/protocol.h @@ -306,6 +306,7 @@ #define MAX_DEMO_PACKET_ENTITIES 196 // doesn't count nails typedef struct { int num_entities; + int ent_nums[MAX_DEMO_PACKET_ENTITIES]; entity_state_t *entities; } packet_entities_t; diff --git a/nq/source/cl_ents.c b/nq/source/cl_ents.c index 2d8a17abd..0ac26a263 100644 --- a/nq/source/cl_ents.c +++ b/nq/source/cl_ents.c @@ -362,6 +362,7 @@ CL_RelinkEntities (void) if (cl_forcelink[i]) { // The entity was not updated in the last message so move to the // final spot + ent->pose1 = ent->pose2 = -1; VectorCopy (new->origin, ent->origin); if (!(ent->model->flags & EF_ROTATE)) CL_TransformEntity (ent, new->angles, true); @@ -372,10 +373,10 @@ CL_RelinkEntities (void) } VectorCopy (ent->origin, ent->old_origin); } else { - // If the delta is large, assume a teleport and don't lerp f = frac; VectorCopy (ent->origin, ent->old_origin); VectorSubtract (new->origin, old->origin, delta); + // If the delta is large, assume a teleport and don't lerp if (fabs (delta[0]) > 100 || fabs (delta[1] > 100) || fabs (delta[2]) > 100) { // assume a teleportation, not a motion @@ -419,7 +420,8 @@ CL_RelinkEntities (void) CL_TransformEntity (ent, angles, false); } CL_EntityEffects (i, ent, new); - CL_NewDlight (i, ent->origin, new->effects, 0, 0); + CL_NewDlight (i, ent->origin, new->effects, new->glow_size, + new->glow_color); if (VectorDistance_fast (old->origin, ent->origin) > (256 * 256)) VectorCopy (ent->origin, old->origin); if (ent->model->flags & ~EF_ROTATE) diff --git a/qw/include/client.h b/qw/include/client.h index dd1095d14..4c55ff7b0 100644 --- a/qw/include/client.h +++ b/qw/include/client.h @@ -203,6 +203,8 @@ typedef struct { // accidentally do something the first frame // sentcmds[cl.netchan.outgoing_sequence & UPDATE_MASK] = cmd frame_t frames[UPDATE_BACKUP]; + int link_sequence; + int prev_sequence; // information for local display int stats[MAX_CL_STATS]; // health, etc @@ -340,6 +342,7 @@ extern client_state_t cl; extern entity_t *cl_static_entities; extern entity_t cl_entities[512]; +extern byte cl_entity_valid[2][512]; extern qboolean nomaster; extern char *server_version; // version of server we connected to diff --git a/qw/source/cl_entparse.c b/qw/source/cl_entparse.c index fa006c0d0..ffad98174 100644 --- a/qw/source/cl_entparse.c +++ b/qw/source/cl_entparse.c @@ -103,12 +103,8 @@ CL_ParseDelta (entity_state_t *from, entity_state_t *to, int bits) if (bits & U_FRAME) to->frame = MSG_ReadByte (net_message); - if (bits & U_COLORMAP) { - byte cmap = MSG_ReadByte (net_message); - if (cmap != to->colormap) - to->skin = mod_funcs->Skin_SetColormap (to->skin, cmap); - to->colormap = cmap; - } + if (bits & U_COLORMAP) + to->colormap = MSG_ReadByte (net_message); if (bits & U_SKIN) to->skinnum = MSG_ReadByte (net_message); @@ -193,22 +189,32 @@ FlushEntityPacket (void) } } +static void +copy_state (packet_entities_t *newp, packet_entities_t *oldp, int newindex, + int oldindex, int num) +{ + newp->entities[num] = oldp->entities[num]; + newp->ent_nums[newindex] = num; + cl_entity_valid[0][num] = 1; +} + void CL_ParsePacketEntities (qboolean delta) { - byte from; - int oldindex, newindex, newnum, oldnum, oldpacket, newpacket, word; + byte from; + int oldindex, newindex, newnum, oldnum, oldpacket, word; packet_entities_t *oldp, *newp, dummy; - qboolean full; + qboolean full; - newpacket = cls.netchan.incoming_sequence & UPDATE_MASK; - newp = &cl.frames[newpacket].packet_entities; - cl.frames[newpacket].invalid = false; + cl.prev_sequence = cl.link_sequence; + cl.link_sequence = cls.netchan.incoming_sequence; + newp = &cl.frames[cl.link_sequence & UPDATE_MASK].packet_entities; + cl.frames[cl.link_sequence & UPDATE_MASK].invalid = false; if (delta) { from = MSG_ReadByte (net_message); - oldpacket = cl.frames[newpacket].delta_sequence; + oldpacket = cl.frames[cl.link_sequence & UPDATE_MASK].delta_sequence; if (cls.demoplayback2) from = oldpacket = (cls.netchan.incoming_sequence - 1); if ((from & UPDATE_MASK) != (oldpacket & UPDATE_MASK)) @@ -217,6 +223,8 @@ CL_ParsePacketEntities (qboolean delta) oldpacket = -1; full = false; + //memcpy (cl_entity_valid[1], cl_entity_valid[0], + // sizeof (cl_entity_valid[0])); if (oldpacket != -1) { if (cls.netchan.outgoing_sequence - oldpacket >= UPDATE_BACKUP - 1) { // we can't use this, it is too old @@ -230,12 +238,14 @@ CL_ParsePacketEntities (qboolean delta) dummy.num_entities = 0; cl.validsequence = cls.netchan.incoming_sequence; full = true; + memset (cl_entity_valid[0], 0, sizeof (cl_entity_valid[0])); } oldindex = 0; newindex = 0; newp->num_entities = 0; + newnum = 0; while (1) { word = MSG_ReadShort (net_message); if (net_message->badread) { // something didn't parse right... @@ -248,15 +258,14 @@ CL_ParsePacketEntities (qboolean delta) if (newindex >= MAX_DEMO_PACKET_ENTITIES) Host_Error ("CL_ParsePacketEntities: newindex == " "MAX_DEMO_PACKET_ENTITIES"); - newp->entities[newindex] = oldp->entities[oldindex]; - newindex++; - oldindex++; + oldnum = oldp->ent_nums[oldindex]; + copy_state (newp, oldp, newindex++, oldindex++, oldnum); } break; } newnum = word & 511; oldnum = oldindex >= oldp->num_entities ? 9999 : - oldp->entities[oldindex].number; + oldp->ent_nums[oldindex]; while (newnum > oldnum) { if (full) { @@ -268,15 +277,14 @@ CL_ParsePacketEntities (qboolean delta) if (newindex >= MAX_DEMO_PACKET_ENTITIES) Host_Error ("CL_ParsePacketEntities: newindex == " "MAX_DEMO_PACKET_ENTITIES"); - newp->entities[newindex] = oldp->entities[oldindex]; - newindex++; - oldindex++; + copy_state (newp, oldp, newindex++, oldindex++, oldnum); oldnum = oldindex >= oldp->num_entities ? 9999 : - oldp->entities[oldindex].number; + oldp->ent_nums[oldindex]; } if (newnum < oldnum) { // new from baseline if (word & U_REMOVE) { + cl_entity_valid[0][newnum] = 0; if (full) { cl.validsequence = 0; Sys_Printf ("WARNING: U_REMOVE on full update\n"); @@ -290,8 +298,9 @@ CL_ParsePacketEntities (qboolean delta) Host_Error ("CL_ParsePacketEntities: newindex == " "MAX_DEMO_PACKET_ENTITIES"); CL_ParseDelta (&qw_entstates.baseline[newnum], - &newp->entities[newindex], - word); + &newp->entities[newnum], word); + newp->ent_nums[newindex] = newnum; + cl_entity_valid[0][newnum] = 1; newindex++; continue; } @@ -302,20 +311,20 @@ CL_ParsePacketEntities (qboolean delta) Sys_Printf ("WARNING: delta on full update"); } if (word & U_REMOVE) { // Clear the entity - entity_t *ent = &cl_entities[newnum]; - if (ent->efrag) - r_funcs->R_RemoveEfrags (ent); - memset (ent, 0, sizeof (entity_t)); + cl_entity_valid[0][newnum] = 0; oldindex++; continue; } - CL_ParseDelta (&oldp->entities[oldindex], - &newp->entities[newindex], word); + CL_ParseDelta (&oldp->entities[oldnum], + &newp->entities[newnum], word); + newp->ent_nums[newindex] = newnum; + cl_entity_valid[0][newnum] = 1; newindex++; oldindex++; } } + cl.num_entities = max (cl.num_entities, newnum); newp->num_entities = newindex; } diff --git a/qw/source/cl_ents.c b/qw/source/cl_ents.c index fa9f219c1..c397fde2e 100644 --- a/qw/source/cl_ents.c +++ b/qw/source/cl_ents.c @@ -45,6 +45,7 @@ #include "qw/msg_ucmd.h" #include "qw/bothdefs.h" +#include "chase.h" #include "cl_cam.h" #include "cl_ents.h" #include "cl_main.h" @@ -60,6 +61,7 @@ entity_t cl_player_ents[MAX_CLIENTS]; entity_t cl_flag_ents[MAX_CLIENTS]; entity_t cl_entities[512]; // FIXME: magic number +byte cl_entity_valid[2][512]; void CL_ClearEnts (void) @@ -68,6 +70,7 @@ CL_ClearEnts (void) i = qw_entstates.num_frames * qw_entstates.num_entities; memset (qw_entstates.frame[0], 0, i * sizeof (entity_state_t)); + memset (cl_entity_valid, 0, sizeof (cl_entity_valid)); for (i = 0; i < sizeof (cl_entities) / sizeof (cl_entities[0]); i++) CL_Init_Entity (&cl_entities[i]); for (i = 0; i < sizeof (cl_flag_ents) / sizeof (cl_flag_ents[0]); i++) @@ -232,90 +235,163 @@ CL_ModelEffects (entity_t *ent, int num, int glow_color) r_funcs->particles->R_GlowTrail (ent, glow_color); } +static void +set_entity_model (entity_t *ent, int modelindex) +{ + ent->model = cl.model_precache[modelindex]; + // automatic animation (torches, etc) can be either all together + // or randomized + if (ent->model) { + if (ent->model->synctype == ST_RAND) + ent->syncbase = (float) (rand () & 0x7fff) / 0x7fff; + else + ent->syncbase = 0.0; + } +} + static void CL_LinkPacketEntities (void) { - int pnum; - entity_t *ent; - entity_state_t *s1; - model_t *model; - packet_entities_t *pack; + int i, j, forcelink; + float frac, f; + entity_t *ent; + entity_state_t *new, *old; + vec3_t delta; - pack = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK].packet_entities; - - for (pnum = 0; pnum < pack->num_entities; pnum++) { - s1 = &pack->entities[pnum]; + frac = 1; + for (i = 0; i < 512; i++) { + new = &qw_entstates.frame[cl.link_sequence & UPDATE_MASK][i]; + old = &qw_entstates.frame[cl.prev_sequence & UPDATE_MASK][i]; + ent = &cl_entities[i]; + forcelink = cl_entity_valid[0][i] != cl_entity_valid[1][i]; + cl_entity_valid[1][i] = cl_entity_valid[0][i]; + // if the object wasn't included in the last packet, remove it + if (!cl_entity_valid[0][i]) { + ent->model = NULL; + ent->pose1 = ent->pose2 = -1; + if (ent->efrag) + r_funcs->R_RemoveEfrags (ent); // just became empty + continue; + } // spawn light flashes, even ones coming from invisible objects - CL_NewDlight (s1->number, s1->origin, s1->effects, s1->glow_size, - s1->glow_color); - - ent = &cl_entities[s1->number]; + CL_NewDlight (i, new->origin, new->effects, new->glow_size, + new->glow_color); // if set to invisible, skip - if (!s1->modelindex - || (cl_deadbodyfilter->int_val && is_dead_body (s1)) - || (cl_gibfilter->int_val && is_gib (s1))) { + if (!new->modelindex + || (cl_deadbodyfilter->int_val && is_dead_body (new)) + || (cl_gibfilter->int_val && is_gib (new))) { if (ent->efrag) r_funcs->R_RemoveEfrags (ent); continue; } - ent->model = model = cl.model_precache[s1->modelindex]; + if (forcelink) + *old = *new; + + if (forcelink || new->modelindex != old->modelindex) { + old->modelindex = new->modelindex; + set_entity_model (ent, new->modelindex); + } + ent->frame = new->frame; + if (forcelink || new->colormap != old->colormap + || new->skinnum != old->skinnum) { + old->skinnum = new->skinnum; + ent->skinnum = new->skinnum; + old->colormap = new->colormap; + if (new->colormap && (new->colormap <= MAX_CLIENTS) + && cl.players[new->colormap - 1].name + && cl.players[new->colormap - 1].name->value[0]) { + player_info_t *player = &cl.players[new->colormap - 1]; + ent->skin = mod_funcs->Skin_SetSkin (ent->skin, new->colormap, + player->skinname->value); + ent->skin = mod_funcs->Skin_SetColormap (ent->skin, + new->colormap); + } else { + ent->skin = mod_funcs->Skin_SetColormap (ent->skin, 0); + } + } + ent->scale = new->scale / 16.0; + + VectorCopy (ent_colormod[new->colormod], ent->colormod); + ent->colormod[3] = new->alpha / 255.0; ent->min_light = 0; ent->fullbright = 0; - - if (s1->modelindex == cl_playerindex) { + if (new->modelindex == cl_playerindex) { ent->min_light = min (cl.fbskins, cl_fb_players->value); if (ent->min_light >= 1.0) ent->fullbright = 1; } - // set colormap - ent->skin = s1->skin; - - VectorCopy (ent_colormod[s1->colormod], ent->colormod); - ent->colormod[3] = s1->alpha / 255.0; - ent->scale = s1->scale / 16.0; - // Ender: Extend (Colormod) [QSG - End] - - // set skin - ent->skinnum = s1->skinnum; - - // set frame - ent->frame = s1->frame; - - if (!ent->efrag) { + if (forcelink) { ent->pose1 = ent->pose2 = -1; - - // No trail if new this frame - VectorCopy (s1->origin, ent->origin); + VectorCopy (new->origin, ent->origin); + if (!(ent->model->flags & EF_ROTATE)) + CL_TransformEntity (ent, new->angles, true); + if (i != cl.viewentity || chase_active->int_val) { + if (ent->efrag) + r_funcs->R_RemoveEfrags (ent); + r_funcs->R_AddEfrags (ent); + } VectorCopy (ent->origin, ent->old_origin); } else { + f = frac; VectorCopy (ent->origin, ent->old_origin); - VectorCopy (s1->origin, ent->origin); - if (!VectorCompare (ent->origin, ent->old_origin)) { - // the entity moved, it must be relinked - r_funcs->R_RemoveEfrags (ent); + VectorSubtract (new->origin, old->origin, delta); + // If the delta is large, assume a teleport and don't lerp + if (fabs (delta[0]) > 100 || fabs (delta[1] > 100) + || fabs (delta[2]) > 100) { + // assume a teleportation, not a motion + VectorCopy (new->origin, ent->origin); + if (!(ent->model->flags & EF_ROTATE)) + CL_TransformEntity (ent, new->angles, true); + ent->pose1 = ent->pose2 = -1; + } else { + vec3_t angles, d; + // interpolate the origin and angles + VectorMultAdd (old->origin, f, delta, ent->origin); + if (!(ent->model->flags & EF_ROTATE)) { + VectorSubtract (new->angles, old->angles, d); + for (j = 0; j < 3; j++) { + if (d[j] > 180) + d[j] -= 360; + else if (d[j] < -180) + d[j] += 360; + } + VectorMultAdd (old->angles, f, d, angles); + CL_TransformEntity (ent, angles, false); + } + } + if (i != cl.viewentity || chase_active->int_val) { + if (ent->efrag) { + if (!VectorCompare (ent->origin, ent->old_origin)) { + r_funcs->R_RemoveEfrags (ent); + r_funcs->R_AddEfrags (ent); + } + } else { + r_funcs->R_AddEfrags (ent); + } } } if (!ent->efrag) r_funcs->R_AddEfrags (ent); // rotate binary objects locally - if (model->flags & EF_ROTATE) { + if (ent->model->flags & EF_ROTATE) { vec3_t angles; angles[PITCH] = 0; angles[YAW] = anglemod (100 * cl.time); angles[ROLL] = 0; CL_TransformEntity (ent, angles, false); - } else { - CL_TransformEntity (ent, s1->angles, false); } - - if (model->flags & ~EF_ROTATE) - CL_ModelEffects (ent, -s1->number, s1->glow_color); + //CL_EntityEffects (i, ent, new); + //CL_NewDlight (i, ent->origin, new->effects, 0, 0); + if (VectorDistance_fast (old->origin, ent->origin) > (256 * 256)) + VectorCopy (ent->origin, old->origin); + if (ent->model->flags & ~EF_ROTATE) + CL_ModelEffects (ent, -new->number, new->glow_color); } }