]> git.rm.cloudns.org Git - xonotic/darkplaces.git/commitdiff
md3 tag attachments (implemented but untested), also the capability to attach any...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 24 Jul 2003 19:30:54 +0000 (19:30 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 24 Jul 2003 19:30:54 +0000 (19:30 +0000)
redesigned large portions of entity networking (client and server) to deal with the hierarchical entity associations, viewmodel handling (client and server), view handling (client)
this may have fixed some unknown bugs regarding viewmodels and such (I hope so, it's cleaner, although more complicated)

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@3320 d7cf8633-e32d-0410-b094-e92efae38249

cl_main.c
cl_screen.c
client.h
pr_cmds.c
pr_edict.c
progs.h
protocol.c
protocol.h
sv_main.c
todo
view.c

index d47902f83721e474bf024d65c85a4599797f13a1..547045307769442e510866a76fab273d2b8876dd 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -478,299 +478,344 @@ extern qboolean Nehahrademcompatibility;
 #define MAXVIEWMODELS 32
 entity_t *viewmodels[MAXVIEWMODELS];
 int numviewmodels;
-void CL_RelinkNetworkEntity(entity_t *e)
+
+matrix4x4_t viewmodelmatrix;
+
+static int entitylinkframenumber;
+
+static const vec3_t muzzleflashorigin = {18, 0, 0};
+
+extern void V_DriftPitch(void);
+extern void V_FadeViewFlashs(void);
+extern void V_CalcViewBlend(void);
+
+extern void V_CalcRefdef(void);
+// note this is a recursive function, but it can never get in a runaway loop (because of the delayedlink flags)
+void CL_LinkNetworkEntity(entity_t *e)
 {
-       int trailtype, temp;
-       float oldorg[3], delta[3], lerp, dlightcolor[3], mins[3], maxs[3], v[3], v2[3], d;
-       entity_persistent_t *p;
-       entity_render_t *r;
-       p = &e->persistent;
-       r = &e->render;
-
-       if (e->state_previous.active && e->state_current.modelindex == e->state_previous.modelindex)
+       matrix4x4_t *matrix, blendmatrix, tempmatrix, matrix2;
+       int j, k, l, trailtype, temp;
+       float origin[3], angles[3], delta[3], lerp, dlightcolor[3], mins[3], maxs[3], v[3], v2[3], d;
+       entity_t *t;
+       model_t *model;
+       //entity_persistent_t *p = &e->persistent;
+       //entity_render_t *r = &e->render;
+       if (e->persistent.linkframe != entitylinkframenumber)
        {
-               // movement lerp
-               if (p->lerpdeltatime > 0 && (lerp = (cl.time - p->lerpstarttime) / p->lerpdeltatime) < 1)
+               e->persistent.linkframe = entitylinkframenumber;
+               // skip inactive entities and world
+               if (!e->state_current.active || e == cl_entities)
+                       return;
+               if (e->render.flags & RENDER_VIEWMODEL)
                {
-                       // read the old origin
-                       VectorCopy(r->origin, oldorg);
-                       // interpolate the origin and angles
-                       r->origin[0] = p->oldorigin[0] + lerp * (p->neworigin[0] - p->oldorigin[0]);
-                       r->origin[1] = p->oldorigin[1] + lerp * (p->neworigin[1] - p->oldorigin[1]);
-                       r->origin[2] = p->oldorigin[2] + lerp * (p->neworigin[2] - p->oldorigin[2]);
-                       VectorSubtract(p->newangles, p->oldangles, delta);
-                       if (delta[0] < -180) delta[0] += 360;else if (delta[0] >= 180) delta[0] -= 360;
-                       if (delta[1] < -180) delta[1] += 360;else if (delta[1] >= 180) delta[1] -= 360;
-                       if (delta[2] < -180) delta[2] += 360;else if (delta[2] >= 180) delta[2] -= 360;
-                       VectorMA(p->oldangles, lerp, delta, r->angles);
+                       if (cl.stats[STAT_HEALTH] <= 0 || !r_drawviewmodel.integer || chase_active.integer || envmap || (cl.items & IT_INVISIBILITY))
+                               return;
+                       if (cl.viewentity)
+                               CL_LinkNetworkEntity(cl_entities + cl.viewentity);
+                       matrix = &viewmodelmatrix;
+                       if (e == &cl.viewent && cl.viewentity >= 0 && cl.viewentity < MAX_EDICTS && cl_entities[cl.viewentity].state_current.active)
+                       {
+                               e->state_current.alpha = cl_entities[cl.viewentity].state_current.alpha;
+                               e->state_current.effects = EF_NOSHADOW | (cl_entities[cl.viewentity].state_current.effects & (EF_ADDITIVE | EF_REFLECTIVE | EF_FULLBRIGHT));
+                       }
                }
                else
                {
-                       // no interpolation
-                       VectorCopy(p->neworigin, r->origin);
-                       VectorCopy(p->neworigin, oldorg);
-                       VectorCopy(p->newangles, r->angles);
-               }
-               // animation lerp
-               if (r->frame2 != e->state_current.frame)
-               {
-                       // begin a new frame lerp
-                       r->frame1 = r->frame2;
-                       r->frame1time = r->frame2time;
-                       r->frame = r->frame2 = e->state_current.frame;
-                       r->frame2time = cl.time;
-                       r->framelerp = 0;
+                       t = cl_entities + e->state_current.tagentity;
+                       if (!t->state_current.active)
+                               return;
+                       // note: this can link to world
+                       CL_LinkNetworkEntity(t);
+                       // make relative to the entity
+                       matrix = &t->render.matrix;
+                       // if a valid tagindex is used, make it relative to that tag instead
+                       if (e->state_current.tagentity && e->state_current.tagindex >= 1 && (model = t->render.model) && e->state_current.tagindex <= t->render.model->alias.aliasnum_tags)
+                       {
+                               // blend the matrices
+                               R_LerpAnimation(&t->render);
+                               memset(&blendmatrix, 0, sizeof(blendmatrix));
+                               for (j = 0;j < 4 && t->render.frameblend[j].lerp > 0;j++)
+                               {
+                                       matrix = &t->render.model->alias.aliasdata_tags[t->render.frameblend[j].frame * t->render.model->alias.aliasnum_tags + (e->state_current.tagindex - 1)].matrix;
+                                       d = t->render.frameblend[j].lerp;
+                                       for (l = 0;l < 4;l++)
+                                               for (k = 0;k < 4;k++)
+                                                       blendmatrix.m[l][k] += d * matrix->m[l][k];
+                               }
+                               // concat the tag matrices onto the entity matrix
+                               Matrix4x4_Concat(&tempmatrix, &t->render.matrix, &blendmatrix);
+                               // use the constructed tag matrix
+                               matrix = &tempmatrix;
+                       }
                }
+               e->render.alpha = e->state_current.alpha * (1.0f / 255.0f); // FIXME: interpolate?
+               e->render.scale = e->state_current.scale * (1.0f / 16.0f); // FIXME: interpolate?
+               e->render.flags = e->state_current.flags;
+               if (e - cl_entities == cl.viewentity)
+                       e->render.flags |= RENDER_EXTERIORMODEL;
+               e->render.effects = e->state_current.effects;
+               if (e->state_current.flags & RENDER_COLORMAPPED)
+                       e->render.colormap = e->state_current.colormap;
+               else if (cl.scores != NULL && e->state_current.colormap)
+                       e->render.colormap = cl.scores[e->state_current.colormap - 1].colors; // color it
                else
+                       e->render.colormap = -1; // no special coloring
+               e->render.skinnum = e->state_current.skin;
+               // set up the render matrix
+               if (e->state_previous.active && e->state_current.modelindex == e->state_previous.modelindex)
                {
-                       // update frame lerp fraction
-                       r->framelerp = r->frame2time > r->frame1time ? ((cl.time - r->frame2time) / (r->frame2time - r->frame1time)) : 1;
-                       r->framelerp = bound(0, r->framelerp, 1);
-               }
-       }
-       else
-       {
-               // no interpolation
-               VectorCopy(p->neworigin, r->origin);
-               VectorCopy(p->neworigin, oldorg);
-               VectorCopy(p->newangles, r->angles);
-               r->frame = r->frame1 = r->frame2 = e->state_current.frame;
-               r->frame1time = r->frame2time = cl.time;
-               r->framelerp = 1;
-       }
-
-       r->alpha = e->state_current.alpha * (1.0f / 255.0f); // FIXME: interpolate?
-       r->scale = e->state_current.scale * (1.0f / 16.0f); // FIXME: interpolate?
-       r->flags = e->state_current.flags;
-       if (e - cl_entities == cl.viewentity)
-               r->flags |= RENDER_EXTERIORMODEL;
-       r->effects = e->state_current.effects;
-       if (e->state_current.flags & RENDER_COLORMAPPED)
-               r->colormap = e->state_current.colormap;
-       else if (cl.scores != NULL && e->state_current.colormap)
-               r->colormap = cl.scores[e->state_current.colormap - 1].colors; // color it
-       else
-               r->colormap = -1; // no special coloring
-       r->skinnum = e->state_current.skin;
-
-       // handle effects now...
-       trailtype = -1;
-       dlightcolor[0] = 0;
-       dlightcolor[1] = 0;
-       dlightcolor[2] = 0;
-
-       // LordHavoc: if the entity has no effects, don't check each
-       if (r->effects)
-       {
-               if (r->effects & EF_BRIGHTFIELD)
-               {
-                       if (gamemode == GAME_NEXUIZ)
+                       // movement lerp
+                       if (e->persistent.lerpdeltatime > 0 && (lerp = (cl.time - e->persistent.lerpstarttime) / e->persistent.lerpdeltatime) < 1)
                        {
-                               dlightcolor[0] += 100.0f;
-                               dlightcolor[1] += 200.0f;
-                               dlightcolor[2] += 400.0f;
-                               trailtype = 8;
+                               // interpolate the origin and angles
+                               VectorLerp(e->persistent.oldorigin, lerp, e->persistent.neworigin, origin);
+                               VectorSubtract(e->persistent.newangles, e->persistent.oldangles, delta);
+                               if (delta[0] < -180) delta[0] += 360;else if (delta[0] >= 180) delta[0] -= 360;
+                               if (delta[1] < -180) delta[1] += 360;else if (delta[1] >= 180) delta[1] -= 360;
+                               if (delta[2] < -180) delta[2] += 360;else if (delta[2] >= 180) delta[2] -= 360;
+                               VectorMA(e->persistent.oldangles, lerp, delta, angles);
                        }
                        else
-                               CL_EntityParticles(e);
-               }
-               if (r->effects & EF_MUZZLEFLASH)
-                       p->muzzleflash = 100.0f;
-               if (r->effects & EF_DIMLIGHT)
-               {
-                       dlightcolor[0] += 200.0f;
-                       dlightcolor[1] += 200.0f;
-                       dlightcolor[2] += 200.0f;
-               }
-               if (r->effects & EF_BRIGHTLIGHT)
-               {
-                       dlightcolor[0] += 400.0f;
-                       dlightcolor[1] += 400.0f;
-                       dlightcolor[2] += 400.0f;
-               }
-               // LordHavoc: added EF_RED and EF_BLUE
-               if (r->effects & EF_RED) // red
-               {
-                       dlightcolor[0] += 200.0f;
-                       dlightcolor[1] +=  20.0f;
-                       dlightcolor[2] +=  20.0f;
-               }
-               if (r->effects & EF_BLUE) // blue
-               {
-                       dlightcolor[0] +=  20.0f;
-                       dlightcolor[1] +=  20.0f;
-                       dlightcolor[2] += 200.0f;
-               }
-               if (r->effects & EF_FLAME)
-               {
-                       mins[0] = r->origin[0] - 16.0f;
-                       mins[1] = r->origin[1] - 16.0f;
-                       mins[2] = r->origin[2] - 16.0f;
-                       maxs[0] = r->origin[0] + 16.0f;
-                       maxs[1] = r->origin[1] + 16.0f;
-                       maxs[2] = r->origin[2] + 16.0f;
-                       // how many flames to make
-                       temp = (int) (cl.time * 300) - (int) (cl.oldtime * 300);
-                       CL_FlameCube(mins, maxs, temp);
-                       d = lhrandom(200, 250);
-                       dlightcolor[0] += d * 1.0f;
-                       dlightcolor[1] += d * 0.7f;
-                       dlightcolor[2] += d * 0.3f;
+                       {
+                               // no interpolation
+                               VectorCopy(e->persistent.neworigin, origin);
+                               VectorCopy(e->persistent.newangles, angles);
+                       }
+                       // animation lerp
+                       if (e->render.frame2 != e->state_current.frame)
+                       {
+                               // begin a new frame lerp
+                               e->render.frame1 = e->render.frame2;
+                               e->render.frame1time = e->render.frame2time;
+                               e->render.frame = e->render.frame2 = e->state_current.frame;
+                               e->render.frame2time = cl.time;
+                               e->render.framelerp = 0;
+                       }
+                       else
+                       {
+                               // update frame lerp fraction
+                               e->render.framelerp = e->render.frame2time > e->render.frame1time ? ((cl.time - e->render.frame2time) / (e->render.frame2time - e->render.frame1time)) : 1;
+                               e->render.framelerp = bound(0, e->render.framelerp, 1);
+                       }
                }
-               if (r->effects & EF_STARDUST)
+               else
                {
-                       mins[0] = r->origin[0] - 16.0f;
-                       mins[1] = r->origin[1] - 16.0f;
-                       mins[2] = r->origin[2] - 16.0f;
-                       maxs[0] = r->origin[0] + 16.0f;
-                       maxs[1] = r->origin[1] + 16.0f;
-                       maxs[2] = r->origin[2] + 16.0f;
-                       // how many particles to make
-                       temp = (int) (cl.time * 200) - (int) (cl.oldtime * 200);
-                       CL_Stardust(mins, maxs, temp);
-                       d = 100;
-                       dlightcolor[0] += d * 1.0f;
-                       dlightcolor[1] += d * 0.7f;
-                       dlightcolor[2] += d * 0.3f;
+                       // no interpolation
+                       VectorCopy(e->persistent.neworigin, origin);
+                       VectorCopy(e->persistent.newangles, angles);
+                       e->render.frame = e->render.frame1 = e->render.frame2 = e->state_current.frame;
+                       e->render.frame1time = e->render.frame2time = cl.time;
+                       e->render.framelerp = 1;
                }
-       }
 
-       r->model = cl.model_precache[e->state_current.modelindex];
-       if (r->model)
-       {
-               Mod_CheckLoaded(r->model);
-               if (r->model->type != mod_brush)
-                       r->angles[0] = -r->angles[0];
-               // LordHavoc: if the model has no flags, don't check each
-               if (r->model->flags & EF_ROTATE)
+               e->render.model = cl.model_precache[e->state_current.modelindex];
+               if (e->render.model)
                {
-                       r->angles[1] = ANGLEMOD(100*cl.time);
-                       if (cl_itembobheight.value)
-                               r->origin[2] += (cos(cl.time * cl_itembobspeed.value * (2.0 * M_PI)) + 1.0) * 0.5 * cl_itembobheight.value;
+                       Mod_CheckLoaded(e->render.model);
+                       if (e->render.model->type != mod_brush)
+                               angles[0] = -angles[0];
+                       if (e->render.model->flags & EF_ROTATE)
+                       {
+                               angles[1] = ANGLEMOD(100*cl.time);
+                               if (cl_itembobheight.value)
+                                       origin[2] += (cos(cl.time * cl_itembobspeed.value * (2.0 * M_PI)) + 1.0) * 0.5 * cl_itembobheight.value;
+                       }
                }
-       }
 
-       Matrix4x4_CreateFromQuakeEntity(&r->matrix, r->origin[0], r->origin[1], r->origin[2], r->angles[0], r->angles[1], r->angles[2], r->scale);
-       Matrix4x4_Invert_Simple(&r->inversematrix, &r->matrix);
-       CL_BoundingBoxForEntity(&e->render);
-
-       // LordHavoc: if the model has no flags, don't check each
-       if (r->model && r->model->flags)
-       {
-               if (r->model->flags & EF_GIB)
-                       trailtype = 2;
-               else if (r->model->flags & EF_ZOMGIB)
-                       trailtype = 4;
-               else if (r->model->flags & EF_TRACER)
+               // FIXME: e->render.scale should go away
+               Matrix4x4_CreateFromQuakeEntity(&matrix2, origin[0], origin[1], origin[2], angles[0], angles[1], angles[2], e->render.scale);
+               // concat the matrices to make the entity relative to its tag
+               Matrix4x4_Concat(&e->render.matrix, matrix, &matrix2);
+               // make the other useful stuff
+               Matrix4x4_Invert_Simple(&e->render.inversematrix, &e->render.matrix);
+               CL_BoundingBoxForEntity(&e->render);
+
+               // handle effects now that we know where this entity is in the world...
+               origin[0] = e->render.matrix.m[0][3];
+               origin[1] = e->render.matrix.m[1][3];
+               origin[2] = e->render.matrix.m[2][3];
+               trailtype = -1;
+               dlightcolor[0] = 0;
+               dlightcolor[1] = 0;
+               dlightcolor[2] = 0;
+               // LordHavoc: if the entity has no effects, don't check each
+               if (e->render.effects)
                {
-                       trailtype = 3;
-                       dlightcolor[0] += 0x10;
-                       dlightcolor[1] += 0x40;
-                       dlightcolor[2] += 0x10;
-               }
-               else if (r->model->flags & EF_TRACER2)
-               {
-                       trailtype = 5;
-                       dlightcolor[0] += 0x50;
-                       dlightcolor[1] += 0x30;
-                       dlightcolor[2] += 0x10;
+                       if (e->render.effects & EF_BRIGHTFIELD)
+                       {
+                               if (gamemode == GAME_NEXUIZ)
+                               {
+                                       dlightcolor[0] += 100.0f;
+                                       dlightcolor[1] += 200.0f;
+                                       dlightcolor[2] += 400.0f;
+                                       trailtype = 8;
+                               }
+                               else
+                                       CL_EntityParticles(e);
+                       }
+                       if (e->render.effects & EF_MUZZLEFLASH)
+                               e->persistent.muzzleflash = 100.0f;
+                       if (e->render.effects & EF_DIMLIGHT)
+                       {
+                               dlightcolor[0] += 200.0f;
+                               dlightcolor[1] += 200.0f;
+                               dlightcolor[2] += 200.0f;
+                       }
+                       if (e->render.effects & EF_BRIGHTLIGHT)
+                       {
+                               dlightcolor[0] += 400.0f;
+                               dlightcolor[1] += 400.0f;
+                               dlightcolor[2] += 400.0f;
+                       }
+                       // LordHavoc: more effects
+                       if (e->render.effects & EF_RED) // red
+                       {
+                               dlightcolor[0] += 200.0f;
+                               dlightcolor[1] +=  20.0f;
+                               dlightcolor[2] +=  20.0f;
+                       }
+                       if (e->render.effects & EF_BLUE) // blue
+                       {
+                               dlightcolor[0] +=  20.0f;
+                               dlightcolor[1] +=  20.0f;
+                               dlightcolor[2] += 200.0f;
+                       }
+                       if (e->render.effects & EF_FLAME)
+                       {
+                               mins[0] = origin[0] - 16.0f;
+                               mins[1] = origin[1] - 16.0f;
+                               mins[2] = origin[2] - 16.0f;
+                               maxs[0] = origin[0] + 16.0f;
+                               maxs[1] = origin[1] + 16.0f;
+                               maxs[2] = origin[2] + 16.0f;
+                               // how many flames to make
+                               temp = (int) (cl.time * 300) - (int) (cl.oldtime * 300);
+                               CL_FlameCube(mins, maxs, temp);
+                               d = lhrandom(200, 250);
+                               dlightcolor[0] += d * 1.0f;
+                               dlightcolor[1] += d * 0.7f;
+                               dlightcolor[2] += d * 0.3f;
+                       }
+                       if (e->render.effects & EF_STARDUST)
+                       {
+                               mins[0] = origin[0] - 16.0f;
+                               mins[1] = origin[1] - 16.0f;
+                               mins[2] = origin[2] - 16.0f;
+                               maxs[0] = origin[0] + 16.0f;
+                               maxs[1] = origin[1] + 16.0f;
+                               maxs[2] = origin[2] + 16.0f;
+                               // how many particles to make
+                               temp = (int) (cl.time * 200) - (int) (cl.oldtime * 200);
+                               CL_Stardust(mins, maxs, temp);
+                               d = 100;
+                               dlightcolor[0] += d * 1.0f;
+                               dlightcolor[1] += d * 0.7f;
+                               dlightcolor[2] += d * 0.3f;
+                       }
                }
-               else if (r->model->flags & EF_ROCKET)
+               // muzzleflash fades over time, and is offset a bit
+               if (e->persistent.muzzleflash > 0)
                {
-                       trailtype = 0;
-                       dlightcolor[0] += 200.0f;
-                       dlightcolor[1] += 160.0f;
-                       dlightcolor[2] +=  80.0f;
+                       Matrix4x4_Transform(&e->render.matrix, muzzleflashorigin, v2);
+                       CL_TraceLine(origin, v2, v, NULL, 0, true, NULL);
+                       CL_AllocDlight(NULL, v, e->persistent.muzzleflash, 1, 1, 1, 0, 0);
+                       e->persistent.muzzleflash -= cl.frametime * 1000;
                }
-               else if (r->model->flags & EF_GRENADE)
+               // LordHavoc: if the model has no flags, don't check each
+               if (e->render.model && e->render.model->flags)
                {
-                       // LordHavoc: r->alpha == -1 is for Nehahra dem compatibility (cigar smoke)
-                       trailtype = r->alpha == -1 ? 7 : 1;
+                       if (e->render.model->flags & EF_GIB)
+                               trailtype = 2;
+                       else if (e->render.model->flags & EF_ZOMGIB)
+                               trailtype = 4;
+                       else if (e->render.model->flags & EF_TRACER)
+                       {
+                               trailtype = 3;
+                               dlightcolor[0] += 0x10;
+                               dlightcolor[1] += 0x40;
+                               dlightcolor[2] += 0x10;
+                       }
+                       else if (e->render.model->flags & EF_TRACER2)
+                       {
+                               trailtype = 5;
+                               dlightcolor[0] += 0x50;
+                               dlightcolor[1] += 0x30;
+                               dlightcolor[2] += 0x10;
+                       }
+                       else if (e->render.model->flags & EF_ROCKET)
+                       {
+                               trailtype = 0;
+                               dlightcolor[0] += 200.0f;
+                               dlightcolor[1] += 160.0f;
+                               dlightcolor[2] +=  80.0f;
+                       }
+                       else if (e->render.model->flags & EF_GRENADE)
+                       {
+                               // LordHavoc: e->render.alpha == -1 is for Nehahra dem compatibility (cigar smoke)
+                               trailtype = e->render.alpha == -1 ? 7 : 1;
+                       }
+                       else if (e->render.model->flags & EF_TRACER3)
+                       {
+                               trailtype = 6;
+                               dlightcolor[0] += 0x50;
+                               dlightcolor[1] += 0x20;
+                               dlightcolor[2] += 0x40;
+                       }
                }
-               else if (r->model->flags & EF_TRACER3)
+               // LordHavoc: customizable glow
+               if (e->state_current.glowsize)
                {
-                       trailtype = 6;
-                       dlightcolor[0] += 0x50;
-                       dlightcolor[1] += 0x20;
-                       dlightcolor[2] += 0x40;
+                       // * 4 for the expansion from 0-255 to 0-1023 range,
+                       // / 255 to scale down byte colors
+                       VectorMA(dlightcolor, e->state_current.glowsize * (4.0f / 255.0f), (qbyte *)&palette_complete[e->state_current.glowcolor], dlightcolor);
                }
-       }
-
-       // LordHavoc: customizable glow
-       if (e->state_current.glowsize)
-       {
-               // * 4 for the expansion from 0-255 to 0-1023 range,
-               // / 255 to scale down byte colors
-               VectorMA(dlightcolor, e->state_current.glowsize * (4.0f / 255.0f), (qbyte *)&palette_complete[e->state_current.glowcolor], dlightcolor);
-       }
-
-       // trails need a previous frame...
-       if (e->state_previous.active)
-       {
-               // LordHavoc: customizable trail
-               if (r->flags & RENDER_GLOWTRAIL)
-                       CL_RocketTrail2(oldorg, r->origin, e->state_current.glowcolor, e);
-               else if (trailtype >= 0)
-                       CL_RocketTrail(oldorg, r->origin, trailtype, e);
-       }
-
-       if ((dlightcolor[0] || dlightcolor[1] || dlightcolor[2]) && !(r->flags & RENDER_VIEWMODEL))
-       {
-               VectorCopy(r->origin, v);
-               // hack to make glowing player light shine on their gun
-               if ((e - cl_entities) == cl.viewentity/* && !chase_active.integer*/)
-                       v[2] += 30;
-               CL_AllocDlight(r, v, 1, dlightcolor[0], dlightcolor[1], dlightcolor[2], 0, 0);
-       }
-
-       if (p->muzzleflash > 0)
-       {
-               v2[0] = r->matrix.m[0][0] * 18 + r->matrix.m[0][3];
-               v2[1] = r->matrix.m[0][1] * 18 + r->matrix.m[1][3];
-               v2[2] = r->matrix.m[0][2] * 18 + r->matrix.m[2][3];
-               CL_TraceLine(r->origin, v2, v, NULL, 0, true, NULL);
-
-               CL_AllocDlight(NULL, v, p->muzzleflash, 1, 1, 1, 0, 0);
-               p->muzzleflash -= cl.frametime * 1000;
-       }
-
-       // note: the cl.viewentity and intermission check is to hide player
-       // shadow during intermission and during the Nehahra movie and
-       // Nehahra cinematics
-       if (!(r->effects & (EF_NOSHADOW | EF_ADDITIVE))
-        && (r->alpha == 1)
-        && !(r->flags & RENDER_VIEWMODEL)
-        && ((e - cl_entities) != cl.viewentity || (!cl.intermission && !Nehahrademcompatibility && !cl_noplayershadow.integer)))
-               r->flags |= RENDER_SHADOW;
-
-       // don't show entities with no modelindex (note: this still shows
-       // entities which have a modelindex that resolved to a NULL model)
-       if (r->model && !(r->effects & EF_NODRAW))
-       {
-               if (r->flags & RENDER_VIEWMODEL)
+               // make the dlight
+               if ((dlightcolor[0] || dlightcolor[1] || dlightcolor[2]) && !(e->render.flags & RENDER_VIEWMODEL) && !e->state_current.tagentity)
                {
-                       // store a list of view-relative entities for later adjustment in view code
-                       if (numviewmodels < MAXVIEWMODELS)
-                               viewmodels[numviewmodels++] = e;
+                       VectorCopy(origin, v);
+                       // hack to make glowing player light shine on their gun
+                       if ((e - cl_entities) == cl.viewentity/* && !chase_active.integer*/)
+                               v[2] += 30;
+                       CL_AllocDlight(&e->render, v, 1, dlightcolor[0], dlightcolor[1], dlightcolor[2], 0, 0);
                }
-               else
+               // trails need the previous frame
+               if (e->state_previous.active && e->state_previous.modelindex == e->state_current.modelindex)
                {
-                       if (r->model->name[0] == '*' && r->model->type == mod_brush)
-                               cl_brushmodel_entities[cl_num_brushmodel_entities++] = &e->render;
-                       if (r_refdef.numentities < r_refdef.maxentities)
-                               r_refdef.entities[r_refdef.numentities++] = &e->render;
-                       if (cl_num_entities < e->state_current.number + 1)
-                               cl_num_entities = e->state_current.number + 1;
+                       if (e->render.flags & RENDER_GLOWTRAIL)
+                               CL_RocketTrail2(e->persistent.trail_origin, origin, e->state_current.glowcolor, e);
+                       else if (trailtype >= 0)
+                               CL_RocketTrail(e->persistent.trail_origin, origin, trailtype, e);
                }
+               VectorCopy(origin, e->persistent.trail_origin);
+               // note: the cl.viewentity and intermission check is to hide player
+               // shadow during intermission and during the Nehahra movie and
+               // Nehahra cinematics
+               if (!(e->render.effects & (EF_NOSHADOW | EF_ADDITIVE))
+                && (e->render.alpha == 1)
+                && !(e->render.flags & RENDER_VIEWMODEL)
+                && ((e - cl_entities) != cl.viewentity || (!cl.intermission && !Nehahrademcompatibility && !cl_noplayershadow.integer)))
+                       e->render.flags |= RENDER_SHADOW;
+               // as soon as player is known we can call V_CalcRefDef
+               if ((e - cl_entities) == cl.viewentity)
+                       V_CalcRefdef();
+               if (e->render.model && e->render.model->name[0] == '*' && e->render.model->type == mod_brush)
+                       cl_brushmodel_entities[cl_num_brushmodel_entities++] = &e->render;
+               // don't show entities with no modelindex (note: this still shows
+               // entities which have a modelindex that resolved to a NULL model)
+               if (e->render.model && !(e->render.effects & EF_NODRAW) && r_refdef.numentities < r_refdef.maxentities)
+                       r_refdef.entities[r_refdef.numentities++] = &e->render;
+               if (cl_num_entities < e->state_current.number + 1)
+                       cl_num_entities = e->state_current.number + 1;
        }
 }
 
-void CL_RelinkWorld (void)
+void CL_RelinkWorld(void)
 {
        entity_t *ent = &cl_entities[0];
        if (cl_num_entities < 1)
                cl_num_entities = 1;
        cl_brushmodel_entities[cl_num_brushmodel_entities++] = &ent->render;
+       // FIXME: this should be done at load
        Matrix4x4_CreateIdentity(&ent->render.matrix);
        Matrix4x4_CreateIdentity(&ent->render.inversematrix);
        CL_BoundingBoxForEntity(&ent->render);
@@ -796,17 +841,29 @@ static void CL_RelinkNetworkEntities(void)
        entity_t *ent;
        int i;
 
+       ent = &cl.viewent;
+       ent->state_previous = ent->state_current;
+       ClearStateToDefault(&ent->state_current);
+       ent->state_current.time = cl.time;
+       ent->state_current.number = -1;
+       ent->state_current.active = true;
+       ent->state_current.modelindex = cl.stats[STAT_WEAPON];
+       ent->state_current.frame = cl.stats[STAT_WEAPONFRAME];
+       ent->state_current.flags = RENDER_VIEWMODEL;
+
        // start on the entity after the world
+       entitylinkframenumber++;
        for (i = 1, ent = cl_entities + 1;i < MAX_EDICTS;i++, ent++)
        {
                if (cl_entities_active[i])
                {
                        if (ent->state_current.active)
-                               CL_RelinkNetworkEntity(ent);
+                               CL_LinkNetworkEntity(ent);
                        else
                                cl_entities_active[i] = false;
                }
        }
+       CL_LinkNetworkEntity(&cl.viewent);
 }
 
 static void CL_RelinkEffects(void)
@@ -973,7 +1030,6 @@ void CL_LerpPlayer(float frac)
 {
        int i;
        float d;
-       entity_t *ent;
 
        cl.viewzoom = cl.viewzoomold + frac * (cl.viewzoomnew - cl.viewzoomold);
 
@@ -993,48 +1049,8 @@ void CL_LerpPlayer(float frac)
                        cl.viewangles[i] = cl.mviewangles[1][i] + frac * d;
                }
        }
-
-       // set up gun
-       ent = &cl.viewent;
-       ent->state_previous = ent->state_current;
-       ClearStateToDefault(&ent->state_current);
-       ent->state_current.time = cl.time;
-       ent->state_current.number = -1;
-       ent->state_current.active = true;
-       ent->state_current.modelindex = cl.stats[STAT_WEAPON];
-       ent->state_current.frame = cl.stats[STAT_WEAPONFRAME];
-       if (cl.viewentity >= 0 && cl.viewentity < MAX_EDICTS && cl_entities[cl.viewentity].state_current.active)
-       {
-               ent->state_current.alpha = cl_entities[cl.viewentity].state_current.alpha;
-               ent->state_current.effects = cl_entities[cl.viewentity].state_current.effects;
-       }
-       ent->state_current.flags = RENDER_VIEWMODEL;
-       CL_RelinkNetworkEntity(ent);
 }
 
-void CL_RelinkEntities(void)
-{
-       float frac;
-
-       numviewmodels = 0;
-
-       // fraction from previous network update to current
-       frac = CL_LerpPoint();
-
-       CL_ClearTempEntities();
-       CL_DecayLights();
-       CL_RelinkWorld();
-       CL_RelinkStaticEntities();
-       CL_RelinkNetworkEntities();
-       CL_RelinkEffects();
-       CL_MoveParticles();
-
-       CL_LerpPlayer(frac);
-
-       CL_RelinkBeams();
-}
-
-
 /*
 ===============
 CL_ReadFromServer
@@ -1052,10 +1068,30 @@ int CL_ReadFromServer(void)
 
        if (cls.state == ca_connected && cls.signon == SIGNONS)
        {
-               CL_RelinkEntities();
+               // prepare for a new frame
+               CL_LerpPlayer(CL_LerpPoint());
+               CL_DecayLights();
+               CL_ClearTempEntities();
+               V_DriftPitch();
+               V_FadeViewFlashs();
+
+               // relink network entities (note: this sets up the view!)
+               CL_RelinkNetworkEntities();
+
+               // move particles
+               CL_MoveParticles();
+
+               // link stuff
+               CL_RelinkWorld();
+               CL_RelinkStaticEntities();
+               CL_RelinkBeams();
+               CL_RelinkEffects();
 
                // run cgame code (which can add more entities)
                CL_CGVM_Frame();
+
+               // update view blend
+               V_CalcViewBlend();
        }
 
        return 0;
index 17e8e70c8a61d3e57abbc5fb88ee7ab3a28341e6..19e72e96a7b57fdd55c3cacc1ec657ef9fd6ab85 100644 (file)
@@ -972,9 +972,6 @@ void CL_UpdateScreen(void)
 
        DrawQ_Clear();
 
-       if (!intimerefresh)
-               V_CalcRefdef();
-
        if (cls.signon == SIGNONS)
                R_TimeReport("setup");
 
index 92516fbea38d363d6062b4e5dfdaa4a5b4e90b63..7682a9571fea60ea32dd9c0f05f36da8cd8b88e4 100644 (file)
--- a/client.h
+++ b/client.h
@@ -157,6 +157,10 @@ entity_render_t;
 
 typedef struct entity_persistent_s
 {
+       int linkframe;
+
+       vec3_t trail_origin;
+
        // particle trail
        float trail_time;
 
index f49b2d205729fee951b4daaddcc9142507575fb8..141e9b6076feca3c5b76d454e92a78e181c0922e 100644 (file)
--- a/pr_cmds.c
+++ b/pr_cmds.c
@@ -2949,6 +2949,40 @@ void PF_argv (void)
                G_INT(OFS_RETURN) = PR_SetString("");
 }
 
+//void(entity e, entity tagentity, string tagname) setattachment = #443; // attachs e to a tag on tagentity (note: use "" to attach to entity origin/angles instead of a tag)
+void PF_setattachment (void)
+{
+       edict_t *e = G_EDICT(OFS_PARM0);
+       edict_t *tagentity = G_EDICT(OFS_PARM1);
+       char *tagname = G_STRING(OFS_PARM2);
+       eval_t *v;
+       int i, modelindex;
+       model_t *model;
+
+       if (tagentity == NULL)
+               tagentity = sv.edicts;
+
+       v = GETEDICTFIELDVALUE(e, eval_tag_entity);
+       if (v)
+               v->edict = EDICT_TO_PROG(tagentity);
+
+       v = GETEDICTFIELDVALUE(e, eval_tag_index);
+       if (v)
+               v->_float = 0;
+       if (tagentity != NULL && tagentity != sv.edicts && tagname && tagname[0])
+       {
+               modelindex = (int)tagentity->v->modelindex;
+               if (modelindex >= 0 && modelindex < MAX_MODELS)
+               {
+                       model = sv.models[modelindex];
+                       if (model->alias.aliasnum_tags)
+                               for (i = 0;i < model->alias.aliasnum_tags;i++)
+                                       if (!strcmp(tagname, model->alias.aliasdata_tags[i].name))
+                                               v->_float = i;
+               }
+       }
+}
+
 
 builtin_t pr_builtin[] =
 {
@@ -3119,7 +3153,7 @@ PF_getsurfaceclippedpoint,        // #439 vector(entity e, float s, vector p) getsurfac
 PF_clientcommand,                      // #440 void(entity e, string s) clientcommand (KRIMZON_SV_PARSECLIENTCOMMAND)
 PF_tokenize,                           // #441 float(string s) tokenize (KRIMZON_SV_PARSECLIENTCOMMAND)
 PF_argv,                                       // #442 string(float n) argv (KRIMZON_SV_PARSECLIENTCOMMAND)
-NULL,                                          // #443
+PF_setattachment,                      // #443 void(entity e, entity tagentity, string tagname) setattachment (DP_GFX_QUAKE3MODELTAGS)
 NULL,                                          // #444
 NULL,                                          // #445
 NULL,                                          // #446
index 4e1ed0d74724c3f4f86310eb84d8cd45753ba206..324ad388e95ca966e1fc6d52ac910772c9423239 100644 (file)
@@ -120,6 +120,8 @@ int eval_pmodel;
 int eval_punchvector;
 int eval_viewzoom;
 int eval_clientcolors;
+int eval_tag_entity;
+int eval_tag_index;
 
 mfunction_t *SV_PlayerPhysicsQC;
 mfunction_t *EndFrameQC;
@@ -172,6 +174,8 @@ void FindEdictFieldOffsets(void)
        eval_punchvector = FindFieldOffset("punchvector");
        eval_viewzoom = FindFieldOffset("viewzoom");
        eval_clientcolors = FindFieldOffset("clientcolors");
+       eval_tag_entity = FindFieldOffset("tag_entity");
+       eval_tag_index = FindFieldOffset("tag_index");
 
        // LordHavoc: allowing QuakeC to override the player movement code
        SV_PlayerPhysicsQC = ED_FindFunction ("SV_PlayerPhysics");
@@ -1189,7 +1193,9 @@ dpfield_t dpfields[] =
        {ev_vector, "movement"},
        {ev_float, "pmodel"},
        {ev_vector, "punchvector"},
-       {ev_float, "clientcolors"}
+       {ev_float, "clientcolors"},
+       {ev_entity, "tag_entity"},
+       {ev_float, "tag_index"}
 };
 
 /*
diff --git a/progs.h b/progs.h
index 5934f45d8dfc00af9000edfdcbfcba0c379f4157..0ee48852ea793e863b49c298d721cd5b957255d4 100644 (file)
--- a/progs.h
+++ b/progs.h
@@ -121,6 +121,8 @@ extern int eval_pmodel;
 extern int eval_punchvector;
 extern int eval_viewzoom;
 extern int eval_clientcolors;
+extern int eval_tag_entity;
+extern int eval_tag_index;
 
 #define GETEDICTFIELDVALUE(ed, fieldoffset) (fieldoffset ? (eval_t *)((qbyte *)ed->v + fieldoffset) : NULL)
 
index 0e50aaebd8fa914834752ce27afa838fc0aa46c1..c24fea9730035518c80e4a1dce105c5fa6eeb702 100644 (file)
@@ -3,20 +3,10 @@
 
 void ClearStateToDefault(entity_state_t *s)
 {
-       s->active = 0;
-       s->time = 0;
-       VectorClear(s->origin);
-       VectorClear(s->angles);
-       s->effects = 0;
-       s->modelindex = 0;
-       s->frame = 0;
-       s->colormap = 0;
-       s->skin = 0;
+       memset(s, 0, sizeof(*s));
        s->alpha = 255;
        s->scale = 16;
-       s->glowsize = 0;
        s->glowcolor = 254;
-       s->flags = 0;
 }
 
 // (server) clears the database to contain no frames (thus delta compression compresses against nothing)
@@ -42,10 +32,10 @@ void EntityFrame_AckFrame(entity_database_t *d, int frame)
 }
 
 // (server) clears frame, to prepare for adding entities
-void EntityFrame_Clear(entity_frame_t *f, vec3_t eye)
+void EntityFrame_Clear(entity_frame_t *f, vec3_t eye, int framenum)
 {
        f->time = 0;
-       f->framenum = 0;
+       f->framenum = framenum;
        f->numentities = 0;
        if (eye == NULL)
        {
@@ -57,23 +47,21 @@ void EntityFrame_Clear(entity_frame_t *f, vec3_t eye)
        }
 }
 
-// (server) allocates an entity slot in frame, returns NULL if full
-entity_state_t *EntityFrame_NewEntity(entity_frame_t *f, int number)
+// (server) adds an entity to frame
+void EntityFrame_AddEntity(entity_frame_t *f, entity_state_t *s)
 {
-       entity_state_t *e;
-       if (f->numentities >= MAX_ENTITY_DATABASE)
-               return NULL;
-       e = &f->entitydata[f->numentities++];
-       e->active = true;
-       e->number = number;
-       return e;
+       if (f->numentities < MAX_ENTITY_DATABASE)
+       {
+               f->entitydata[f->numentities] = *s;
+               f->entitydata[f->numentities++].active = true;
+       }
 }
 
 // (server and client) reads a frame from the database
 void EntityFrame_FetchFrame(entity_database_t *d, int framenum, entity_frame_t *f)
 {
        int i, n;
-       EntityFrame_Clear(f, NULL);
+       EntityFrame_Clear(f, NULL, -1);
        for (i = 0;i < d->numframes && d->frames[i].framenum < framenum;i++);
        if (i < d->numframes && framenum == d->frames[i].framenum)
        {
@@ -87,8 +75,6 @@ void EntityFrame_FetchFrame(entity_database_t *d, int framenum, entity_frame_t *
                        memcpy(f->entitydata + n, d->entitydata, sizeof(*f->entitydata) * (f->numentities - n));
                VectorCopy(d->eye, f->eye);
        }
-       else
-               f->framenum = -1;
 }
 
 // (server and client) adds a entity_frame to the database, for future reference
@@ -256,6 +242,8 @@ void EntityFrame_Write(entity_database_t *d, entity_frame_t *f, sizebuf_t *msg)
                        bits |= E_GLOWCOLOR;
                if (ent->flags != delta->flags)
                        bits |= E_FLAGS;
+               if (ent->tagindex != delta->tagindex || ent->tagentity != delta->tagentity)
+                       bits |= E_TAGATTACHMENT;
 
                if (bits) // don't send anything if it hasn't changed
                {
@@ -329,6 +317,11 @@ void EntityFrame_Write(entity_database_t *d, entity_frame_t *f, sizebuf_t *msg)
                                MSG_WriteByte(msg, ent->glowsize);
                        if (bits & E_GLOWCOLOR)
                                MSG_WriteByte(msg, ent->glowcolor);
+                       if (bits & E_TAGATTACHMENT)
+                       {
+                               MSG_WriteShort(msg, ent->tagentity);
+                               MSG_WriteByte(msg, ent->tagindex);
+                       }
                }
        }
        for (;onum < o->numentities;onum++)
@@ -349,7 +342,7 @@ void EntityFrame_Read(entity_database_t *d)
 
        ClearStateToDefault(&baseline);
 
-       EntityFrame_Clear(f, NULL);
+       EntityFrame_Clear(f, NULL, -1);
 
        // read the frame header info
        f->time = cl.mtime[0];
@@ -489,6 +482,11 @@ void EntityFrame_Read(entity_database_t *d)
                        if (dpprotocol == DPPROTOCOL_VERSION2)
                                if (bits & E_FLAGS)
                                        e->flags = MSG_ReadByte();
+                       if (bits & E_TAGATTACHMENT)
+                       {
+                               e->tagentity = MSG_ReadShort();
+                               e->tagindex = MSG_ReadByte();
+                       }
                }
        }
        while (old < oldend)
index 35e51a171c14f0dc6409fac49a9f782360158f2c..0256ea3dd0255fc36afd92b80c8b4079f04cff02 100644 (file)
@@ -317,6 +317,12 @@ typedef struct
        unsigned short modelindex;
        unsigned short frame;
        unsigned short effects;
+       unsigned short tagentity;
+       unsigned short specialvisibilityradius;
+       unsigned short viewmodelforclient;
+       unsigned short exteriormodelforclient;
+       unsigned short nodrawtoclient;
+       unsigned short drawonlytoclient;
        qbyte colormap;
        qbyte skin;
        qbyte alpha;
@@ -324,6 +330,7 @@ typedef struct
        qbyte glowsize;
        qbyte glowcolor;
        qbyte flags;
+       qbyte tagindex;
 }
 entity_state_t;
 
@@ -470,7 +477,7 @@ entity_frame_t;
 #define E_SOUND1               (1<<24)
 #define E_SOUNDVOL             (1<<25)
 #define E_SOUNDATTEN   (1<<26)
-#define E_UNUSED4              (1<<27)
+#define E_TAGATTACHMENT        (1<<27)
 #define E_UNUSED5              (1<<28)
 #define E_UNUSED6              (1<<29)
 #define E_UNUSED7              (1<<30)
@@ -484,9 +491,9 @@ void EntityFrame_ClearDatabase(entity_database_t *d);
 // (server and client) removes frames older than 'frame' from database
 void EntityFrame_AckFrame(entity_database_t *d, int frame);
 // (server) clears frame, to prepare for adding entities
-void EntityFrame_Clear(entity_frame_t *f, vec3_t eye);
-// (server) allocates an entity slot in frame, returns NULL if full
-entity_state_t *EntityFrame_NewEntity(entity_frame_t *f, int number);
+void EntityFrame_Clear(entity_frame_t *f, vec3_t eye, int framenum);
+// (server) adds an entity to frame
+void EntityFrame_AddEntity(entity_frame_t *f, entity_state_t *s);
 // (server and client) reads a frame from the database
 void EntityFrame_FetchFrame(entity_database_t *d, int framenum, entity_frame_t *f);
 // (server and client) adds a entity_frame to the database, for future
index bf05f17f52f7b9eea0e01ddf3f7da53adef0c2bf..d9fb0507e6b0177c31703ce4b61346a0a3f5e39a 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -796,147 +796,193 @@ void SV_WriteEntitiesToClient (client_t *client, edict_t *clent, sizebuf_t *msg)
                Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d portal %d trace\n", client->name, totalentities, visibleentities, culled_pvs + culled_portal + culled_trace, culled_pvs, culled_portal, culled_trace);
 }
 #else
-static entity_frame_t sv_writeentitiestoclient_entityframe;
-void SV_WriteEntitiesToClient (client_t *client, edict_t *clent, sizebuf_t *msg)
+static int numsendentities;
+static entity_state_t sendentities[MAX_EDICTS];
+static entity_state_t *sendentitiesindex[MAX_EDICTS];
+
+void SV_PrepareEntitiesForSending(void)
 {
-       int e, clentnum, flags, alpha, glowcolor, glowsize, scale, effects, modelindex;
-       int culled_pvs, culled_portal, culled_trace, visibleentities, totalentities;
-       float alphaf, lightsize;
-       qbyte *pvs;
-       vec3_t origin, angles, entmins, entmaxs, lightmins, lightmaxs, testorigin, testeye;
+       int e, i;
        edict_t *ent;
+       entity_state_t cs;
        eval_t *val;
-       model_t *model;
-       entity_state_t *s;
-
-       Mod_CheckLoaded(sv.worldmodel);
-
-// find the client's PVS
-       // the real place being tested from
-       VectorAdd (clent->v->origin, clent->v->view_ofs, testeye);
-       pvs = SV_FatPVS (testeye);
-
-       // the place being reported (to consider the fact the client still
-       // applies the view_ofs[2], so we have to only send the fractional part
-       // of view_ofs[2], undoing what the client will redo)
-       VectorCopy (testeye, testorigin);
-       e = (int) clent->v->view_ofs[2] & 255;
-       if (e >= 128)
-               e -= 256;
-       testorigin[2] -= (float) e;
-       EntityFrame_Clear(&sv_writeentitiestoclient_entityframe, testorigin);
-
-       culled_pvs = 0;
-       culled_portal = 0;
-       culled_trace = 0;
-       visibleentities = 0;
-       totalentities = 0;
-
-       clentnum = EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
        // send all entities that touch the pvs
-       ent = NEXT_EDICT(sv.edicts);
-       for (e = 1;e < sv.num_edicts;e++, ent = NEXT_EDICT(ent))
+       numsendentities = 0;
+       sendentitiesindex[0] = NULL;
+       for (e = 1, ent = NEXT_EDICT(sv.edicts);e < sv.num_edicts;e++, ent = NEXT_EDICT(ent))
        {
+               sendentitiesindex[e] = NULL;
                if (ent->e->free)
                        continue;
-               flags = 0;
 
-               if (ent != clent) // LordHavoc: always send player
+               ClearStateToDefault(&cs);
+               cs.active = true;
+               cs.number = e;
+               VectorCopy(ent->v->origin, cs.origin);
+               VectorCopy(ent->v->angles, cs.angles);
+               cs.flags = 0;
+               cs.effects = (int)ent->v->effects;
+               cs.colormap = (qbyte)ent->v->colormap;
+               cs.skin = (qbyte)ent->v->skin;
+               cs.frame = (qbyte)ent->v->frame;
+               cs.viewmodelforclient = GETEDICTFIELDVALUE(ent, eval_viewmodelforclient)->edict;
+               cs.exteriormodelforclient = GETEDICTFIELDVALUE(ent, eval_exteriormodeltoclient)->edict;
+               cs.nodrawtoclient = GETEDICTFIELDVALUE(ent, eval_nodrawtoclient)->edict;
+               cs.drawonlytoclient = GETEDICTFIELDVALUE(ent, eval_drawonlytoclient)->edict;
+               cs.tagentity = GETEDICTFIELDVALUE(ent, eval_tag_entity)->edict;
+               cs.tagindex = (qbyte)GETEDICTFIELDVALUE(ent, eval_tag_index)->_float;
+               i = (int)(GETEDICTFIELDVALUE(ent, eval_glow_size)->_float * 0.25f);
+               cs.glowsize = (qbyte)bound(0, i, 255);
+               if (GETEDICTFIELDVALUE(ent, eval_glow_trail)->_float)
+                       cs.flags |= RENDER_GLOWTRAIL;
+
+               cs.modelindex = 0;
+               i = (int)ent->v->modelindex;
+               if (i >= 1 && i < MAX_MODELS && *PR_GetString(ent->v->model))
+                       cs.modelindex = i;
+
+
+               cs.alpha = 255;
+               i = (int)(GETEDICTFIELDVALUE(ent, eval_alpha)->_float * 255.0f);
+               if (i)
+                       cs.alpha = (qbyte)bound(0, i, 255);
+               // halflife
+               i = (int)(GETEDICTFIELDVALUE(ent, eval_renderamt)->_float);
+               if (i)
+                       cs.alpha = (qbyte)bound(0, i, 255);
+
+               cs.scale = 16;
+               i = (int)(GETEDICTFIELDVALUE(ent, eval_scale)->_float * 16.0f);
+               if (i)
+                       cs.scale = (qbyte)bound(0, i, 255);
+
+               cs.glowcolor = 254;
+               i = (int)(GETEDICTFIELDVALUE(ent, eval_glow_color)->_float);
+               if (i)
+                       cs.glowcolor = (int) val->_float;
+
+               if (GETEDICTFIELDVALUE(ent, eval_fullbright)->_float)
+                       cs.effects |= EF_FULLBRIGHT;
+
+               if (ent->v->movetype == MOVETYPE_STEP)
+                       cs.flags |= RENDER_STEP;
+               if ((cs.effects & EF_LOWPRECISION) && cs.origin[0] >= -32768 && cs.origin[1] >= -32768 && cs.origin[2] >= -32768 && cs.origin[0] <= 32767 && cs.origin[1] <= 32767 && cs.origin[2] <= 32767)
+                       cs.flags |= RENDER_LOWPRECISION;
+               if (ent->v->colormap >= 1024)
+                       cs.flags |= RENDER_COLORMAPPED;
+               if (cs.viewmodelforclient)
+                       cs.flags |= RENDER_VIEWMODEL; // show relative to the view
+
+               cs.specialvisibilityradius = 0;
+               if (cs.glowsize)
+                       cs.specialvisibilityradius = max(cs.specialvisibilityradius, cs.glowsize * 4);
+               if (cs.flags & RENDER_GLOWTRAIL)
+                       cs.specialvisibilityradius = max(cs.specialvisibilityradius, 100);
+               if (cs.effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
                {
-                       if ((val = GETEDICTFIELDVALUE(ent, eval_viewmodelforclient)) && val->edict)
-                       {
-                               if (val->edict == clentnum)
-                                       flags |= RENDER_VIEWMODEL; // show relative to the view
-                               else
-                               {
-                                       // don't show to anyone else
-                                       continue;
-                               }
-                       }
-                       else
-                       {
-                               // LordHavoc: never draw something told not to display to this client
-                               if ((val = GETEDICTFIELDVALUE(ent, eval_nodrawtoclient)) && val->edict == clentnum)
-                                       continue;
-                               if ((val = GETEDICTFIELDVALUE(ent, eval_drawonlytoclient)) && val->edict && val->edict != clentnum)
-                                       continue;
-                       }
+                       if (cs.effects & EF_BRIGHTFIELD)
+                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 80);
+                       if (cs.effects & EF_MUZZLEFLASH)
+                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 100);
+                       if (cs.effects & EF_BRIGHTLIGHT)
+                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 400);
+                       if (cs.effects & EF_DIMLIGHT)
+                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 200);
+                       if (cs.effects & EF_RED)
+                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 200);
+                       if (cs.effects & EF_BLUE)
+                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 200);
+                       if (cs.effects & EF_FLAME)
+                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 250);
+                       if (cs.effects & EF_STARDUST)
+                               cs.specialvisibilityradius = max(cs.specialvisibilityradius, 100);
                }
 
-               glowsize = 0;
-               effects = ent->v->effects;
-               if ((val = GETEDICTFIELDVALUE(ent, eval_glow_size)))
-                       glowsize = (int) val->_float >> 2;
-               glowsize = bound(0, glowsize, 255);
+               if (numsendentities >= MAX_EDICTS)
+                       continue;
+               // we can omit invisible entities with no effects that are not clients
+               // LordHavoc: this could kill tags attached to an invisible entity, I
+               // just hope we never have to support that case
+               if (cs.number > svs.maxclients && ((cs.effects & EF_NODRAW) || (!cs.modelindex && !cs.specialvisibilityradius)))
+                       continue;
+               sendentitiesindex[e] = sendentities + numsendentities;
+               sendentities[numsendentities++] = cs;
+       }
+}
 
-               lightsize = 0;
-               if (effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
-               {
-                       if (effects & EF_BRIGHTFIELD)
-                               lightsize = max(lightsize, 80);
-                       if (effects & EF_MUZZLEFLASH)
-                               lightsize = max(lightsize, 100);
-                       if (effects & EF_BRIGHTLIGHT)
-                               lightsize = max(lightsize, 400);
-                       if (effects & EF_DIMLIGHT)
-                               lightsize = max(lightsize, 200);
-                       if (effects & EF_RED)
-                               lightsize = max(lightsize, 200);
-                       if (effects & EF_BLUE)
-                               lightsize = max(lightsize, 200);
-                       if (effects & EF_FLAME)
-                               lightsize = max(lightsize, 250);
-                       if (effects & EF_STARDUST)
-                               lightsize = max(lightsize, 100);
-               }
-               if (glowsize)
-                       lightsize = max(lightsize, glowsize << 2);
+static int sententitiesmark = 0;
+static int sententities[MAX_EDICTS];
+static int sententitiesconsideration[MAX_EDICTS];
+static int sv_writeentitiestoclient_culled_pvs;
+static int sv_writeentitiestoclient_culled_portal;
+static int sv_writeentitiestoclient_culled_trace;
+static int sv_writeentitiestoclient_visibleentities;
+static int sv_writeentitiestoclient_totalentities;
+static entity_frame_t sv_writeentitiestoclient_entityframe;
+static int sv_writeentitiestoclient_clentnum;
+static qbyte *sv_writeentitiestoclient_pvs;
+static vec3_t sv_writeentitiestoclient_testeye;
+static client_t *sv_writeentitiestoclient_client;
 
-               if ((val = GETEDICTFIELDVALUE(ent, eval_glow_trail)))
-               if (val->_float != 0)
+void SV_MarkWriteEntityStateToClient(entity_state_t *s)
+{
+       vec3_t entmins, entmaxs, lightmins, lightmaxs, testorigin;
+       model_t *model;
+       trace_t trace;
+       if (sententitiesconsideration[s->number] == sententitiesmark)
+               return;
+       sententitiesconsideration[s->number] = sententitiesmark;
+       // viewmodels don't have visibility checking
+       if (s->viewmodelforclient)
+       {
+               if (s->viewmodelforclient != sv_writeentitiestoclient_clentnum)
+                       return;
+       }
+       // never reject player
+       else if (s->number != sv_writeentitiestoclient_clentnum)
+       {
+               // check various rejection conditions
+               if (s->nodrawtoclient == sv_writeentitiestoclient_clentnum)
+                       return;
+               if (s->drawonlytoclient && s->drawonlytoclient != sv_writeentitiestoclient_clentnum)
+                       return;
+               if (s->effects & EF_NODRAW)
+                       return;
+               // LordHavoc: only send entities with a model or important effects
+               if (!s->modelindex && s->specialvisibilityradius == 0)
+                       return;
+               if (s->tagentity)
                {
-                       flags |= RENDER_GLOWTRAIL;
-                       lightsize = max(lightsize, 100);
+                       // tag attached entities simply check their parent
+                       if (!sendentitiesindex[s->tagentity])
+                               return;
+                       SV_MarkWriteEntityStateToClient(sendentitiesindex[s->tagentity]);
+                       if (sententities[s->tagentity] != sententitiesmark)
+                               return;
                }
-
-               modelindex = 0;
-               if (ent->v->modelindex >= 0 && ent->v->modelindex < MAX_MODELS && *PR_GetString(ent->v->model))
+               // always send world submodels, they don't generate much traffic
+               else if ((model = sv.models[s->modelindex]) == NULL || model->name[0] != '*')
                {
-                       modelindex = ent->v->modelindex;
-                       model = sv.models[(int)ent->v->modelindex];
                        Mod_CheckLoaded(model);
-               }
-               else
-               {
-                       model = NULL;
-                       if (ent != clent) // LordHavoc: always send player
-                               if (lightsize == 0) // no effects
-                                       continue;
-               }
-
-               VectorCopy(ent->v->angles, angles);
-               VectorCopy(ent->v->origin, origin);
-
-               // ent has survived every check so far, check if it is visible
-               // always send embedded brush models, they don't generate much traffic
-               if (ent != clent && ((flags & RENDER_VIEWMODEL) == 0) && (model == NULL || model->type != mod_brush || model->name[0] != '*'))
-               {
-                       // use the predicted origin
-                       entmins[0] = origin[0] - 1.0f;
-                       entmins[1] = origin[1] - 1.0f;
-                       entmins[2] = origin[2] - 1.0f;
-                       entmaxs[0] = origin[0] + 1.0f;
-                       entmaxs[1] = origin[1] + 1.0f;
-                       entmaxs[2] = origin[2] + 1.0f;
+                       // entity has survived every check so far, check if visible
+                       // enlarged box to account for prediction (not that there is
+                       // any currently, but still helps the 'run into a room and
+                       // watch items pop up' problem)
+                       entmins[0] = s->origin[0] - 32.0f;
+                       entmins[1] = s->origin[1] - 32.0f;
+                       entmins[2] = s->origin[2] - 32.0f;
+                       entmaxs[0] = s->origin[0] + 32.0f;
+                       entmaxs[1] = s->origin[1] + 32.0f;
+                       entmaxs[2] = s->origin[2] + 32.0f;
                        // using the model's bounding box to ensure things are visible regardless of their physics box
                        if (model)
                        {
-                               if (ent->v->angles[0] || ent->v->angles[2]) // pitch and roll
+                               if (s->angles[0] || s->angles[2]) // pitch and roll
                                {
                                        VectorAdd(entmins, model->rotatedmins, entmins);
                                        VectorAdd(entmaxs, model->rotatedmaxs, entmaxs);
                                }
-                               else if (ent->v->angles[1])
+                               else if (s->angles[1])
                                {
                                        VectorAdd(entmins, model->yawmins, entmins);
                                        VectorAdd(entmaxs, model->yawmaxs, entmaxs);
@@ -947,157 +993,130 @@ void SV_WriteEntitiesToClient (client_t *client, edict_t *clent, sizebuf_t *msg)
                                        VectorAdd(entmaxs, model->normalmaxs, entmaxs);
                                }
                        }
-                       lightmins[0] = min(entmins[0], origin[0] - lightsize);
-                       lightmins[1] = min(entmins[1], origin[1] - lightsize);
-                       lightmins[2] = min(entmins[2], origin[2] - lightsize);
-                       lightmaxs[0] = min(entmaxs[0], origin[0] + lightsize);
-                       lightmaxs[1] = min(entmaxs[1], origin[1] + lightsize);
-                       lightmaxs[2] = min(entmaxs[2], origin[2] + lightsize);
-
-                       totalentities++;
-
+                       lightmins[0] = min(entmins[0], s->origin[0] - s->specialvisibilityradius);
+                       lightmins[1] = min(entmins[1], s->origin[1] - s->specialvisibilityradius);
+                       lightmins[2] = min(entmins[2], s->origin[2] - s->specialvisibilityradius);
+                       lightmaxs[0] = min(entmaxs[0], s->origin[0] + s->specialvisibilityradius);
+                       lightmaxs[1] = min(entmaxs[1], s->origin[1] + s->specialvisibilityradius);
+                       lightmaxs[2] = min(entmaxs[2], s->origin[2] + s->specialvisibilityradius);
+                       sv_writeentitiestoclient_totalentities++;
                        // if not touching a visible leaf
-                       if (sv_cullentities_pvs.integer && !SV_BoxTouchingPVS(pvs, lightmins, lightmaxs, sv.worldmodel->brushq1.nodes))
+                       if (sv_cullentities_pvs.integer && !SV_BoxTouchingPVS(sv_writeentitiestoclient_pvs, lightmins, lightmaxs, sv.worldmodel->brushq1.nodes))
                        {
-                               culled_pvs++;
-                               continue;
+                               sv_writeentitiestoclient_culled_pvs++;
+                               return;
                        }
-
                        // or not visible through the portals
-                       if (sv_cullentities_portal.integer && !Portal_CheckBox(sv.worldmodel, testeye, lightmins, lightmaxs))
+                       if (sv_cullentities_portal.integer && !Portal_CheckBox(sv.worldmodel, sv_writeentitiestoclient_testeye, lightmins, lightmaxs))
                        {
-                               culled_portal++;
-                               continue;
+                               sv_writeentitiestoclient_culled_portal++;
+                               return;
                        }
-
+                       // or not seen by random tracelines
                        if (sv_cullentities_trace.integer)
                        {
                                // LordHavoc: test center first
                                testorigin[0] = (entmins[0] + entmaxs[0]) * 0.5f;
                                testorigin[1] = (entmins[1] + entmaxs[1]) * 0.5f;
                                testorigin[2] = (entmins[2] + entmaxs[2]) * 0.5f;
-                               if (SV_Move(testeye, vec3_origin, vec3_origin, testorigin, MOVE_NOMONSTERS, NULL).fraction == 1)
-                                       client->visibletime[e] = realtime + 1;
+                               trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_NOMONSTERS, NULL);
+                               if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs))
+                                       sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
                                else
                                {
                                        // LordHavoc: test random offsets, to maximize chance of detection
                                        testorigin[0] = lhrandom(entmins[0], entmaxs[0]);
                                        testorigin[1] = lhrandom(entmins[1], entmaxs[1]);
                                        testorigin[2] = lhrandom(entmins[2], entmaxs[2]);
-                                       if (SV_Move(testeye, vec3_origin, vec3_origin, testorigin, MOVE_NOMONSTERS, NULL).fraction == 1)
-                                               client->visibletime[e] = realtime + 1;
+                                       trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_NOMONSTERS, NULL);
+                                       if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs))
+                                               sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
                                        else
                                        {
-                                               if (lightsize)
+                                               if (s->specialvisibilityradius)
                                                {
                                                        // LordHavoc: test random offsets, to maximize chance of detection
                                                        testorigin[0] = lhrandom(lightmins[0], lightmaxs[0]);
                                                        testorigin[1] = lhrandom(lightmins[1], lightmaxs[1]);
                                                        testorigin[2] = lhrandom(lightmins[2], lightmaxs[2]);
-                                                       if (SV_Move(testeye, vec3_origin, vec3_origin, testorigin, MOVE_NOMONSTERS, NULL).fraction == 1)
-                                                               client->visibletime[e] = realtime + 1;
-                                                       else
-                                                       {
-                                                               if (realtime > client->visibletime[e])
-                                                               {
-                                                                       culled_trace++;
-                                                                       continue;
-                                                               }
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       if (realtime > client->visibletime[e])
-                                                       {
-                                                               culled_trace++;
-                                                               continue;
-                                                       }
+                                                       trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_NOMONSTERS, NULL);
+                                                       if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs))
+                                                               sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1;
                                                }
                                        }
                                }
+                               if (realtime > sv_writeentitiestoclient_client->visibletime[s->number])
+                               {
+                                       sv_writeentitiestoclient_culled_trace++;
+                                       return;
+                               }
                        }
-                       visibleentities++;
+                       sv_writeentitiestoclient_visibleentities++;
                }
+       }
+       // this just marks it for sending
+       // FIXME: it would be more efficient to send here, but the entity
+       // compressor isn't that flexible
+       sententities[s->number] = sententitiesmark;
+}
 
-               alphaf = 255.0f;
-               scale = 16;
-               glowcolor = 254;
-               effects = ent->v->effects;
+void SV_WriteEntitiesToClient(client_t *client, edict_t *clent, sizebuf_t *msg)
+{
+       int i;
+       vec3_t testorigin;
+       entity_state_t *s;
 
-               if ((val = GETEDICTFIELDVALUE(ent, eval_alpha)))
-               if (val->_float != 0)
-                       alphaf = val->_float * 255.0;
+       sv_writeentitiestoclient_client = client;
 
-               // HalfLife support
-               if ((val = GETEDICTFIELDVALUE(ent, eval_renderamt)))
-               if (val->_float != 0)
-                       alphaf = val->_float;
+       sv_writeentitiestoclient_culled_pvs = 0;
+       sv_writeentitiestoclient_culled_portal = 0;
+       sv_writeentitiestoclient_culled_trace = 0;
+       sv_writeentitiestoclient_visibleentities = 0;
+       sv_writeentitiestoclient_totalentities = 0;
 
-               if (alphaf == 0.0f)
-                       alphaf = 255.0f;
-               alpha = bound(0, alphaf, 255);
+       Mod_CheckLoaded(sv.worldmodel);
 
-               if ((val = GETEDICTFIELDVALUE(ent, eval_scale)))
-               if ((scale = (int) (val->_float * 16.0)) == 0) scale = 16;
-               if (scale < 0) scale = 0;
-               if (scale > 255) scale = 255;
+// find the client's PVS
+       // the real place being tested from
+       VectorAdd(clent->v->origin, clent->v->view_ofs, sv_writeentitiestoclient_testeye);
+       sv_writeentitiestoclient_pvs = SV_FatPVS(sv_writeentitiestoclient_testeye);
 
-               if ((val = GETEDICTFIELDVALUE(ent, eval_glow_color)))
-               if (val->_float != 0)
-                       glowcolor = (int) val->_float;
+       sv_writeentitiestoclient_clentnum = EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
 
-               if ((val = GETEDICTFIELDVALUE(ent, eval_fullbright)))
-               if (val->_float != 0)
-                       effects |= EF_FULLBRIGHT;
+       sententitiesmark++;
 
-               if (ent != clent)
+       // the place being reported (to consider the fact the client still
+       // applies the view_ofs[2], so we have to only send the fractional part
+       // of view_ofs[2], undoing what the client will redo)
+       VectorCopy(sv_writeentitiestoclient_testeye, testorigin);
+       i = (int) clent->v->view_ofs[2] & 255;
+       if (i >= 128)
+               i -= 256;
+       testorigin[2] -= (float) i;
+
+       for (i = 0;i < numsendentities;i++)
+               SV_MarkWriteEntityStateToClient(sendentities + i);
+
+       EntityFrame_Clear(&sv_writeentitiestoclient_entityframe, testorigin, ++client->entityframenumber);
+       for (i = 0;i < numsendentities;i++)
+       {
+               s = sendentities + i;
+               if (sententities[s->number] == sententitiesmark)
                {
-                       if (lightsize == 0) // no effects
+                       if (s->exteriormodelforclient && s->exteriormodelforclient == sv_writeentitiestoclient_clentnum)
                        {
-                               if (model) // model
-                               {
-                                       // don't send if flagged for NODRAW and there are no effects
-                                       if (model->flags == 0 && ((effects & EF_NODRAW) || scale <= 0 || alpha <= 0))
-                                               continue;
-                               }
-                               else // no model and no effects
-                                       continue;
+                               s->flags |= RENDER_EXTERIORMODEL;
+                               EntityFrame_AddEntity(&sv_writeentitiestoclient_entityframe, s);
+                               s->flags &= ~RENDER_EXTERIORMODEL;
                        }
+                       else
+                               EntityFrame_AddEntity(&sv_writeentitiestoclient_entityframe, s);
                }
-
-               if ((val = GETEDICTFIELDVALUE(ent, eval_exteriormodeltoclient)) && val->edict == clentnum)
-                       flags |= RENDER_EXTERIORMODEL;
-
-               if (ent->v->movetype == MOVETYPE_STEP)
-                       flags |= RENDER_STEP;
-               // don't send an entity if it's coordinates would wrap around
-               if ((effects & EF_LOWPRECISION) && origin[0] >= -32768 && origin[1] >= -32768 && origin[2] >= -32768 && origin[0] <= 32767 && origin[1] <= 32767 && origin[2] <= 32767)
-                       flags |= RENDER_LOWPRECISION;
-
-               s = EntityFrame_NewEntity(&sv_writeentitiestoclient_entityframe, e);
-               // if we run out of space, abort
-               if (!s)
-                       break;
-               VectorCopy(origin, s->origin);
-               VectorCopy(angles, s->angles);
-               if (ent->v->colormap >= 1024)
-                       flags |= RENDER_COLORMAPPED;
-               s->colormap = ent->v->colormap;
-               s->skin = ent->v->skin;
-               s->frame = ent->v->frame;
-               s->modelindex = modelindex;
-               s->effects = effects;
-               s->alpha = alpha;
-               s->scale = scale;
-               s->glowsize = glowsize;
-               s->glowcolor = glowcolor;
-               s->flags = flags;
        }
-       sv_writeentitiestoclient_entityframe.framenum = ++client->entityframenumber;
        EntityFrame_Write(&client->entitydatabase, &sv_writeentitiestoclient_entityframe, msg);
 
        if (sv_cullentities_stats.integer)
-               Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d portal %d trace\n", client->name, totalentities, visibleentities, culled_pvs + culled_portal + culled_trace, culled_pvs, culled_portal, culled_trace);
+               Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d portal %d trace\n", client->name, sv_writeentitiestoclient_totalentities, sv_writeentitiestoclient_visibleentities, sv_writeentitiestoclient_culled_pvs + sv_writeentitiestoclient_culled_portal + sv_writeentitiestoclient_culled_trace, sv_writeentitiestoclient_culled_pvs, sv_writeentitiestoclient_culled_portal, sv_writeentitiestoclient_culled_trace);
 }
 #endif
 
@@ -1446,10 +1465,10 @@ SV_SendClientMessages
 */
 void SV_SendClientMessages (void)
 {
-       int                     i;
+       int i, prepared = false;
 
 // update frags, names, etc
-       SV_UpdateToReliableMessages ();
+       SV_UpdateToReliableMessages();
 
 // build individual updates
        for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
@@ -1465,6 +1484,12 @@ void SV_SendClientMessages (void)
 
                if (host_client->spawned)
                {
+                       if (!prepared)
+                       {
+                               prepared = true;
+                               // only prepare entities once per frame
+                               SV_PrepareEntitiesForSending();
+                       }
                        if (!SV_SendClientDatagram (host_client))
                                continue;
                }
@@ -1511,9 +1536,8 @@ void SV_SendClientMessages (void)
                }
        }
 
-
 // clear muzzle flashes
-       SV_CleanupEnts ();
+       SV_CleanupEnts();
 }
 
 
diff --git a/todo b/todo
index 82e3c60e5b878485af8f10d32a0764ecce8d24e2..17f5316af52e07fe80181335389792705aba5a15 100644 (file)
--- a/todo
+++ b/todo
@@ -1,4 +1,25 @@
 - todo: difficulty ratings are: 0 = trivial, 1 = easy, 2 = easy-moderate, 3 = moderate, 4 = moderate-hard, 5 = hard, 6 = hard++, 7 = nightmare, d = done, n = done but have not notified the people who asked for it, f = failed
+
+
+
+stuff left to do on tags: make server write entities sequentially
+4 darkplaces: tags support on md3 (Electro needs this urgently)
+
+
+
+
+1 darkplaces: add EndGame function (called on server shutdown or level change) (SeienAbunae, Nexuiz)
+0 darkplaces: document how polygon collision works in the code (KrimZon)
+0 darkplaces: fix particle trails! (Supajoe)
+2 darkplaces: .skin loading for models (override skins - not exactly shaders, but adequate, one would be common/nodraw for quake3 compatibility) (Electro)
+0 darkplaces: rename r_picmip and r_max_size and such to glquake names
+1 darkplaces: look at and integrate Vic's updated zone.[ch] (Vic)
+1 darkplaces: add a .modelflags variable which if non-zero overrides model flags (Electro, Arwing)
+1 darkplaces: make r_fogsky cvar to allow use of sky instead of fog when fog is used (SeienAbunae)
+0 darkplaces: document the TEI stuff used in Nexuiz?  check telejano site first (SeienAbunae)
+3 darkplaces: skyroom needs to be added ("info_skyroom" entity sets view origin, scanned by client at load, and by server to send all entities in skyroom) (SeienAbunae)
+0 darkplaces: r_skyscroll1 and r_skyscroll2 cvars (SeienAbunae)
+5 darkplaces: do a minimap that works by simply using nearclip to sheer off anything above the eye, and draws anything below normally, or via a cvar as height coloring (Supajoe)
 0 darkplaces: add DP_SV_ROTATINGBMODEL extension to explain that MOVETYPE_PUSH/SOLID_BSP support rotation in darkplaces and a demonstration of how to use it without qc modifications (Uffe, Supajoe)
 0 darkplaces: add cvars for sbar alpha (background and foreground) (Throvold@uboot.com)
 0 darkplaces: add lightning beam settings to menu (romi)
@@ -29,7 +50,6 @@
 0 darkplaces: make sure that textureless models are white and not invisible, apparently creating a .bmp texture (not supported) made the models black, even more odd... (McKilled, QorpsE)
 0 darkplaces: r_shadow should load .ent when importing light entities
 0 darkplaces: reset zoom on connect (Rick)
-0 darkplaces: revert nice noclip to nq noclip because it can break mods that trap player input and expect nq movement (MauveBib)
 0 darkplaces: revert noclip movement to match nq for compatibility with mods that trap movement as input (MauveBib)
 0 darkplaces: segfault reading memory in windows when starting a new server from menu (yummyluv)
 0 darkplaces: test TecnoX and find the frikbot crash in SV_Physics (KrimZon)
 6 darkplaces: add water refraction like HalfLife2 (Mitchell)
 6 darkplaces: figure out an LOD scheme for really large outdoor environments (Uffe)
 7 darkplaces: Quake3 bsp support (Vermeulen, Mitchell, SeienAbunae)
-7 darkplaces: add DP_ENT_DISTORTIONFIELD which visually pulls things inward/outward around an entity (Supajoe)
+7 darkplaces: add DP_ENT_DISTORTIONFIELD which visually pulls things inward/outward around an entity (Supajoe, SeienAbunae)
 7 darkplaces: add clientside quakec (KrimZon, FrikaC)
 7 darkplaces: make it work on Savage4 again (Ender)
 7 darkplaces: should add quake3 shader support
 ? darkplaces: fix colormapping (Demonix)
 ? darkplaces: fix connecting to proquake servers through routers (Demonix)
+d darkplaces: figure out why quad is creating two coronas, one at player and one at 0 0 0 - answer: viewmodel dlight (Tomaz)
+d darkplaces: gl_flashblend 1 should disable dlighting of models (Tomaz)
 d darkplaces: 12bit color textures in 16bit mode?? (Tomaz)
 d darkplaces: TEXF_CLAMP needs to use GL_CLAMP_TO_EDGE (if not supported just use REPEAT as a fallback, not aware of any cards that lack this)
 d darkplaces: add DP_GFX_EXTERNALTEXTURES extension (Electro)
diff --git a/view.c b/view.c
index 93cbfdc4b1a7fcf143a53f98ddf932ef8ea02c34..94fe265d6490cac6a8b0c765cbaf8c00370dce02 100644 (file)
--- a/view.c
+++ b/view.c
@@ -122,7 +122,7 @@ Drifting is enabled when the center view key is hit, mlook is released and
 lookspring is non 0, or when
 ===============
 */
-static void V_DriftPitch (void)
+void V_DriftPitch (void)
 {
        float           delta, move;
 
@@ -291,9 +291,7 @@ static void V_BonusFlash_f (void)
 ==============================================================================
 */
 
-#define MAXVIEWMODELS 32
-extern int numviewmodels;
-extern entity_t *viewmodels[MAXVIEWMODELS];
+extern matrix4x4_t viewmodelmatrix;
 
 /*
 ==================
@@ -303,14 +301,11 @@ V_CalcRefdef
 */
 void V_CalcRefdef (void)
 {
-       float r, g, b, a, a2;
-       int j;
        entity_t *ent;
        if (cls.state == ca_connected && cls.signon == SIGNONS)
        {
                // ent is the player model (visible when out of body)
                ent = &cl_entities[cl.viewentity];
-               V_DriftPitch();
                if (cl.intermission)
                {
                        // entity is a fixed camera
@@ -349,57 +344,55 @@ void V_CalcRefdef (void)
                        // origin
                        VectorAdd(r_refdef.vieworg, cl.punchvector, r_refdef.vieworg);
                        r_refdef.vieworg[2] += cl.viewheight;
-                       if (cl.stats[STAT_HEALTH] > 0)
+                       if (cl.stats[STAT_HEALTH] > 0 && cl_bob.value && cl_bobcycle.value)
                        {
-                               if (cl_bob.value && cl_bobcycle.value)
-                               {
-                                       double bob, cycle;
-                                       // LordHavoc: this code is *weird*, but not replacable (I think it
-                                       // should be done in QC on the server, but oh well, quake is quake)
-                                       // LordHavoc: figured out bobup: the time at which the sin is at 180
-                                       // degrees (which allows lengthening or squishing the peak or valley)
-                                       cycle = cl.time / cl_bobcycle.value;
-                                       cycle -= (int) cycle;
-                                       if (cycle < cl_bobup.value)
-                                               cycle = sin(M_PI * cycle / cl_bobup.value);
-                                       else
-                                               cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
-                                       // bob is proportional to velocity in the xy plane
-                                       // (don't count Z, or jumping messes it up)
-                                       bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
-                                       bob = bob*0.3 + bob*0.7*cycle;
-                                       r_refdef.vieworg[2] += bound(-7, bob, 4);
-                               }
-                               // link the delayed viewmodel entities
-                               if (numviewmodels > 0 && r_drawviewmodel.integer && !chase_active.integer && !envmap && r_drawentities.integer && !(cl.items & IT_INVISIBILITY))
-                               {
-                                       int i;
-                                       entity_t *ent;
-                                       matrix4x4_t matrix, matrix2;
-                                       Matrix4x4_CreateFromQuakeEntity(&matrix, r_refdef.vieworg[0], r_refdef.vieworg[1], r_refdef.vieworg[2], r_refdef.viewangles[0] + v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value, r_refdef.viewangles[1] - v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value, r_refdef.viewangles[2] - v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value, 0.3);
-                                       for (i = 0;i < numviewmodels && r_refdef.numentities < r_refdef.maxentities;i++)
-                                       {
-                                               ent = viewmodels[i];
-                                               r_refdef.entities[r_refdef.numentities++] = &ent->render;
-                                               matrix2 = ent->render.matrix;
-                                               Matrix4x4_Concat(&ent->render.matrix, &matrix, &matrix2);
-                                               Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix);
-                                               CL_BoundingBoxForEntity(&ent->render);
-                                       }
-                               }
+                               double bob, cycle;
+                               // LordHavoc: this code is *weird*, but not replacable (I think it
+                               // should be done in QC on the server, but oh well, quake is quake)
+                               // LordHavoc: figured out bobup: the time at which the sin is at 180
+                               // degrees (which allows lengthening or squishing the peak or valley)
+                               cycle = cl.time / cl_bobcycle.value;
+                               cycle -= (int) cycle;
+                               if (cycle < cl_bobup.value)
+                                       cycle = sin(M_PI * cycle / cl_bobup.value);
+                               else
+                                       cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value));
+                               // bob is proportional to velocity in the xy plane
+                               // (don't count Z, or jumping messes it up)
+                               bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value;
+                               bob = bob*0.3 + bob*0.7*cycle;
+                               r_refdef.vieworg[2] += bound(-7, bob, 4);
                        }
                }
+               // calculate a viewmodel matrix for use in view-attached entities
+               Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, r_refdef.vieworg[0], r_refdef.vieworg[1], r_refdef.vieworg[2], r_refdef.viewangles[0] + v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value, r_refdef.viewangles[1] - v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value, r_refdef.viewangles[2] - v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value, 0.3);
+       }
+       else
+               Matrix4x4_CreateIdentity(&viewmodelmatrix);
+}
 
-               // drop the damage value
-               cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
-               if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
-                       cl.cshifts[CSHIFT_DAMAGE].percent = 0;
-
-               // drop the bonus value
-               cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
-               if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
-                       cl.cshifts[CSHIFT_BONUS].percent = 0;
+void V_FadeViewFlashs(void)
+{
+       // drop the damage value
+       cl.cshifts[CSHIFT_DAMAGE].percent -= (cl.time - cl.oldtime)*150;
+       if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
+               cl.cshifts[CSHIFT_DAMAGE].percent = 0;
+       // drop the bonus value
+       cl.cshifts[CSHIFT_BONUS].percent -= (cl.time - cl.oldtime)*100;
+       if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
+               cl.cshifts[CSHIFT_BONUS].percent = 0;
+}
 
+void V_CalcViewBlend(void)
+{
+       float a2;
+       int j;
+       r_refdef.viewblend[0] = 0;
+       r_refdef.viewblend[1] = 0;
+       r_refdef.viewblend[2] = 0;
+       r_refdef.viewblend[3] = 0;
+       if (cls.state == ca_connected && cls.signon == SIGNONS)
+       {
                // set contents color
                switch (CL_PointContents(r_refdef.vieworg))
                {
@@ -461,48 +454,26 @@ void V_CalcRefdef (void)
                        cl.cshifts[CSHIFT_POWERUP].percent = 0;
 
                // LordHavoc: fixed V_CalcBlend
-               r = 0;
-               g = 0;
-               b = 0;
-               a = 0;
-
-               for (j=0 ; j<NUM_CSHIFTS ; j++)
+               for (j = 0;j < NUM_CSHIFTS;j++)
                {
-                       a2 = cl.cshifts[j].percent * (1.0f / 255.0f);
-
-                       if (a2 < 0)
-                               continue;
-                       if (a2 > 1)
-                               a2 = 1;
-                       r += (cl.cshifts[j].destcolor[0]-r) * a2;
-                       g += (cl.cshifts[j].destcolor[1]-g) * a2;
-                       b += (cl.cshifts[j].destcolor[2]-b) * a2;
-                       a = 1 - (1 - a) * (1 - a2); // correct alpha multiply...  took a while to find it on the web
+                       a2 = bound(0.0f, cl.cshifts[j].percent * (1.0f / 255.0f), 1.0f);
+                       if (a2 > 0)
+                       {
+                               VectorLerp(r_refdef.viewblend, a2, cl.cshifts[j].destcolor, r_refdef.viewblend);
+                               r_refdef.viewblend[3] = 1 - (1 - r_refdef.viewblend[3]) * (1 - a2); // correct alpha multiply...  took a while to find it on the web
+                       }
                }
                // saturate color (to avoid blending in black)
-               if (a)
+               if (r_refdef.viewblend[3])
                {
-                       a2 = 1 / a;
-                       r *= a2;
-                       g *= a2;
-                       b *= a2;
+                       a2 = 1 / r_refdef.viewblend[3];
+                       VectorScale(r_refdef.viewblend, a2, r_refdef.viewblend);
                }
 
-               r_refdef.viewblend[0] = bound(0, r * (1.0/255.0), 1);
-               r_refdef.viewblend[1] = bound(0, g * (1.0/255.0), 1);
-               r_refdef.viewblend[2] = bound(0, b * (1.0/255.0), 1);
-               r_refdef.viewblend[3] = bound(0, a              , 1);
-       }
-       else
-       {
-               cl.cshifts[CSHIFT_DAMAGE].percent = 0;
-               cl.cshifts[CSHIFT_BONUS].percent = 0;
-               cl.cshifts[CSHIFT_CONTENTS].percent = 0;
-               cl.cshifts[CSHIFT_POWERUP].percent = 0;
-               r_refdef.viewblend[0] = 0;
-               r_refdef.viewblend[1] = 0;
-               r_refdef.viewblend[2] = 0;
-               r_refdef.viewblend[3] = 0;
+               r_refdef.viewblend[0] = bound(0.0f, r_refdef.viewblend[0] * (1.0f/255.0f), 1.0f);
+               r_refdef.viewblend[1] = bound(0.0f, r_refdef.viewblend[1] * (1.0f/255.0f), 1.0f);
+               r_refdef.viewblend[2] = bound(0.0f, r_refdef.viewblend[2] * (1.0f/255.0f), 1.0f);
+               r_refdef.viewblend[3] = bound(0.0f, r_refdef.viewblend[3]                , 1.0f);
        }
 }