From b3895a134a65c7be9ccfe2db7cf7132d5934f316 Mon Sep 17 00:00:00 2001 From: havoc Date: Thu, 24 Jul 2003 19:30:54 +0000 Subject: [PATCH] md3 tag attachments (implemented but untested), also the capability to attach any entity to any other entity without tags (I.E. Q2 vwep models should work if attached to tag "") 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 | 630 +++++++++++++++++++++++++++------------------------- cl_screen.c | 3 - client.h | 4 + pr_cmds.c | 36 ++- pr_edict.c | 8 +- progs.h | 2 + protocol.c | 50 ++--- protocol.h | 15 +- sv_main.c | 476 ++++++++++++++++++++------------------- todo | 26 ++- view.c | 149 +++++-------- 11 files changed, 750 insertions(+), 649 deletions(-) diff --git a/cl_main.c b/cl_main.c index d47902f8..54704530 100644 --- 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; diff --git a/cl_screen.c b/cl_screen.c index 17e8e70c..19e72e96 100644 --- a/cl_screen.c +++ b/cl_screen.c @@ -972,9 +972,6 @@ void CL_UpdateScreen(void) DrawQ_Clear(); - if (!intimerefresh) - V_CalcRefdef(); - if (cls.signon == SIGNONS) R_TimeReport("setup"); diff --git a/client.h b/client.h index 92516fbe..7682a957 100644 --- 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; diff --git a/pr_cmds.c b/pr_cmds.c index f49b2d20..141e9b60 100644 --- 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 diff --git a/pr_edict.c b/pr_edict.c index 4e1ed0d7..324ad388 100644 --- a/pr_edict.c +++ b/pr_edict.c @@ -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 5934f45d..0ee48852 100644 --- 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) diff --git a/protocol.c b/protocol.c index 0e50aaeb..c24fea97 100644 --- a/protocol.c +++ b/protocol.c @@ -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) diff --git a/protocol.h b/protocol.h index 35e51a17..0256ea3d 100644 --- a/protocol.h +++ b/protocol.h @@ -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 diff --git a/sv_main.c b/sv_main.c index bf05f17f..d9fb0507 100644 --- 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 ; ispawned) { + 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 82e3c60e..17f5316a 100644 --- 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) @@ -188,12 +208,14 @@ 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 93cbfdc4..94fe265d 100644 --- 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 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); } } -- 2.39.2