#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);
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)
{
int i;
float d;
- entity_t *ent;
cl.viewzoom = cl.viewzoomold + frac * (cl.viewzoomnew - cl.viewzoomold);
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
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;
DrawQ_Clear();
- if (!intimerefresh)
- V_CalcRefdef();
-
if (cls.signon == SIGNONS)
R_TimeReport("setup");
typedef struct entity_persistent_s
{
+ int linkframe;
+
+ vec3_t trail_origin;
+
// particle trail
float trail_time;
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[] =
{
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
int eval_punchvector;
int eval_viewzoom;
int eval_clientcolors;
+int eval_tag_entity;
+int eval_tag_index;
mfunction_t *SV_PlayerPhysicsQC;
mfunction_t *EndFrameQC;
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");
{ev_vector, "movement"},
{ev_float, "pmodel"},
{ev_vector, "punchvector"},
- {ev_float, "clientcolors"}
+ {ev_float, "clientcolors"},
+ {ev_entity, "tag_entity"},
+ {ev_float, "tag_index"}
};
/*
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)
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)
}
// (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)
{
}
}
-// (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)
{
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
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
{
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++)
ClearStateToDefault(&baseline);
- EntityFrame_Clear(f, NULL);
+ EntityFrame_Clear(f, NULL, -1);
// read the frame header info
f->time = cl.mtime[0];
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)
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;
qbyte glowsize;
qbyte glowcolor;
qbyte flags;
+ qbyte tagindex;
}
entity_state_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)
// (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
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);
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
*/
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++)
if (host_client->spawned)
{
+ if (!prepared)
+ {
+ prepared = true;
+ // only prepare entities once per frame
+ SV_PrepareEntitiesForSending();
+ }
if (!SV_SendClientDatagram (host_client))
continue;
}
}
}
-
// clear muzzle flashes
- SV_CleanupEnts ();
+ SV_CleanupEnts();
}
- 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)
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)
lookspring is non 0, or when
===============
*/
-static void V_DriftPitch (void)
+void V_DriftPitch (void)
{
float delta, move;
==============================================================================
*/
-#define MAXVIEWMODELS 32
-extern int numviewmodels;
-extern entity_t *viewmodels[MAXVIEWMODELS];
+extern matrix4x4_t viewmodelmatrix;
/*
==================
*/
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
// 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))
{
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);
}
}