From: havoc Date: Tue, 22 Jan 2008 20:05:42 +0000 (+0000) Subject: merged CL_MoveParticles, CL_MoveDecals, and R_MoveExplosions into their X-Git-Tag: xonotic-v0.1.0preview~2513 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=21beb5fe4fa2e1f1b7591a4a1f6013b82186912f;p=xonotic%2Fdarkplaces.git merged CL_MoveParticles, CL_MoveDecals, and R_MoveExplosions into their respective Draw functions, this gave a small fps gain (due to better cache behavior) redesigned input packet timing (now based on cl.time instead of realtime, which required compensating for slowmo), this takes advantage of the time synchronization features of the cl.time code replaced cls.movesequence with cls.netcon->outgoing_unreliable_sequence git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@7996 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/cl_input.c b/cl_input.c index 06144935..1d5127db 100644 --- a/cl_input.c +++ b/cl_input.c @@ -1154,10 +1154,10 @@ extern cvar_t slowmo; void CL_UpdateMoveVars(void) { if (cls.protocol == PROTOCOL_QUAKEWORLD) - cl.movevars_ticrate = 1.0 / bound(1, cl_netinputpacketspersecond_qw.value, 100); + cl.movevars_packetinterval = 1.0 / bound(1, cl_netinputpacketspersecond_qw.value, 100); else if (cl.stats[STAT_MOVEVARS_TICRATE]) { - cl.movevars_ticrate = cl.statsf[STAT_MOVEVARS_TICRATE]; + cl.movevars_packetinterval = cl.statsf[STAT_MOVEVARS_TICRATE] * cl.statsf[STAT_MOVEVARS_TIMESCALE] / bound(1, cl_netinputpacketsperserverpacket.value, 10); cl.movevars_timescale = cl.statsf[STAT_MOVEVARS_TIMESCALE]; cl.movevars_gravity = cl.statsf[STAT_MOVEVARS_GRAVITY]; cl.movevars_stopspeed = cl.statsf[STAT_MOVEVARS_STOPSPEED] ; @@ -1179,7 +1179,7 @@ void CL_UpdateMoveVars(void) } else { - cl.movevars_ticrate = 1.0 / bound(1, cl_netinputpacketspersecond.value, 100); + cl.movevars_packetinterval = slowmo.value / bound(1, cl_netinputpacketspersecond.value, 100); cl.movevars_timescale = slowmo.value; cl.movevars_gravity = sv_gravity.value; cl.movevars_stopspeed = cl_movement_stopspeed.value; @@ -1357,7 +1357,6 @@ void CL_SendMove(void) int bits; sizebuf_t buf; unsigned char data[1024]; - static double lastsendtime = 0; double packettime; int msecdelta; @@ -1368,54 +1367,22 @@ void CL_SendMove(void) return; // don't send too often or else network connections can get clogged by a high renderer framerate - packettime = cl.movevars_ticrate; - if (cls.protocol != PROTOCOL_QUAKEWORLD) - packettime /= (double)bound(1, cl_netinputpacketsperserverpacket.value, 10); + packettime = cl.movevars_packetinterval; // send input every frame in singleplayer if (cl.islocalgame) packettime = 0; - // quakeworld servers take only frametimes - // predicted dp7 servers take current interpolation time - // unpredicted servers take an echo of the latest server timestamp + // send the current interpolation time cl.cmd.time = cl.time; - cl.cmd.sequence = cls.movesequence; - if (cls.protocol == PROTOCOL_QUAKEWORLD) - { - if (realtime < lastsendtime + packettime) - return; - cl.cmd.sequence = cls.netcon->qw.outgoing_sequence; - } + cl.cmd.sequence = cls.netcon->outgoing_unreliable_sequence; + if (cl.cmd.time < cl.lastpackettime + packettime && (cl.mtime[0] != cl.mtime[1] || !cl.movement_needupdate)) + return; + // try to round off the lastpackettime to a multiple of the packet interval + // (this causes it to emit packets at a steady beat, and takes advantage + // of the time drift compensation in the cl.time code) + if (packettime > 0) + cl.lastpackettime = floor(cl.cmd.time / packettime) * packettime; else - { - // movement should be sent immediately whenever a server - // packet is received, to minimize ping times - // - - if(cl_netinputpacketsperserverpacket.value > 1) - { - // FIXME: needupdate causes odd behaviour with movement reply as it - // seems, if cl_netinputpacketsperserverpackets is > 1. Don't know - // why. No idea if it ever gets fixed, but until it does... - if (realtime < lastsendtime + packettime) - return; - } - else if(cl_netinputpacketsperserverpacket.value == 1) - { - // the way it is meant to be - if (!cl.movement_needupdate && realtime < lastsendtime + packettime) - return; - } - else - { - // only ever send input as replies to server packets or if we REALLY got nothing else (FIXME may be the more sane default) - if (!cl.movement_needupdate && realtime < lastsendtime + packettime + 0.1) - return; - } - } - - // don't let it fall behind if CL_SendMove hasn't been called recently - // (such is the case when framerate is too low for instance) - lastsendtime = bound(realtime, lastsendtime + packettime, realtime + packettime); + cl.lastpackettime = cl.cmd.time; // set the flag indicating that we sent a packet recently cl.movement_needupdate = false; @@ -1432,9 +1399,6 @@ void CL_SendMove(void) if ((cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon == SIGNONS) && !NetConn_CanSend(cls.netcon) && !cl.islocalgame) return; - // increase the move counter since we intend to send a move - cls.movesequence++; - // send the movement message // PROTOCOL_QUAKE clc_move = 16 bytes total // PROTOCOL_QUAKEDP clc_move = 16 bytes total @@ -1528,19 +1492,19 @@ void CL_SendMove(void) QW_MSG_WriteDeltaUsercmd(&buf, &cl.movecmd[2], &cl.movecmd[1]); QW_MSG_WriteDeltaUsercmd(&buf, &cl.movecmd[1], &cl.movecmd[0]); // calculate the checksum - buf.data[checksumindex] = COM_BlockSequenceCRCByteQW(buf.data + checksumindex + 1, buf.cursize - checksumindex - 1, cls.netcon->qw.outgoing_sequence); + buf.data[checksumindex] = COM_BlockSequenceCRCByteQW(buf.data + checksumindex + 1, buf.cursize - checksumindex - 1, cls.netcon->outgoing_unreliable_sequence); // if delta compression history overflows, request no delta - if (cls.netcon->qw.outgoing_sequence - cl.qw_validsequence >= QW_UPDATE_BACKUP-1) + if (cls.netcon->outgoing_unreliable_sequence - cl.qw_validsequence >= QW_UPDATE_BACKUP-1) cl.qw_validsequence = 0; // request delta compression if appropriate if (cl.qw_validsequence && !cl_nodelta.integer && cls.state == ca_connected && !cls.demorecording) { - cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = cl.qw_validsequence; + cl.qw_deltasequence[cls.netcon->outgoing_unreliable_sequence & QW_UPDATE_MASK] = cl.qw_validsequence; MSG_WriteByte(&buf, qw_clc_delta); MSG_WriteByte(&buf, cl.qw_validsequence & 255); } else - cl.qw_deltasequence[cls.netcon->qw.outgoing_sequence & QW_UPDATE_MASK] = -1; + cl.qw_deltasequence[cls.netcon->outgoing_unreliable_sequence & QW_UPDATE_MASK] = -1; } else if (cls.signon == SIGNONS) { diff --git a/cl_main.c b/cl_main.c index d0a1010a..c5f31ffc 100644 --- a/cl_main.c +++ b/cl_main.c @@ -1801,10 +1801,7 @@ void CL_UpdateWorld(void) CL_RelinkLightFlashes(); CSQC_RelinkAllEntities(ENTMASK_ENGINE | ENTMASK_ENGINEVIEWMODELS); - // move decals, particles, and any other effects - CL_MoveDecals(); - CL_MoveParticles(); - R_MoveExplosions(); + // decals, particles, and explosions will be updated during rneder } r_refdef.scene.time = cl.time; diff --git a/cl_particles.c b/cl_particles.c index e9def1a5..55f99a59 100644 --- a/cl_particles.c +++ b/cl_particles.c @@ -1256,6 +1256,7 @@ void CL_EntityParticles (const entity_t *ent) float pitch, yaw, dist = 64, beamlength = 16, org[3], v[3]; static vec3_t avelocities[NUMVERTEXNORMALS]; if (!cl_particles.integer) return; + if (cl.time <= cl.oldtime) return; // don't spawn new entity particles while paused Matrix4x4_OriginFromMatrix(&ent->render.matrix, org); @@ -1533,295 +1534,6 @@ void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, in } } -/* -=============== -CL_MoveDecals -=============== -*/ -void CL_MoveDecals (void) -{ - decal_t *decal; - int i; - float decalfade; - - // LordHavoc: early out condition - if (!cl.num_decals) - { - cl.free_decal = 0; - return; - } - - decalfade = bound(0, cl.time - cl.oldtime, 0.1) * 255 / cl_decals_fadetime.value; - - for (i = 0, decal = cl.decals;i < cl.num_decals;i++, decal++) - { - if (!decal->typeindex) - continue; - - // heavily optimized decal case - // FIXME: this has fairly wacky handling of alpha - if (cl.time > decal->time2 + cl_decals_time.value) - { - decal->alpha -= decalfade; - if (decal->alpha <= 0) - { - decal->typeindex = 0; - if (cl.free_decal > i) - cl.free_decal = i; - continue; - } - } - - if (decal->owner) - { - if (cl.entities[decal->owner].render.model == decal->ownermodel) - { - Matrix4x4_Transform(&cl.entities[decal->owner].render.matrix, decal->relativeorigin, decal->org); - Matrix4x4_Transform3x3(&cl.entities[decal->owner].render.matrix, decal->relativenormal, decal->normal); - } - else - { - decal->typeindex = 0; - if (cl.free_decal > i) - cl.free_decal = i; - } - } - } - - // reduce cl.num_decals if possible - while (cl.num_decals > 0 && cl.decals[cl.num_decals - 1].typeindex == 0) - cl.num_decals--; -} - -/* -=============== -CL_MoveParticles -=============== -*/ -void CL_MoveParticles (void) -{ - particle_t *p; - int i, j, a, content; - float gravity, dvel, decalfade, frametime, f, dist, oldorg[3]; - int hitent; - trace_t trace; - - // LordHavoc: early out condition - if (!cl.num_particles) - { - cl.free_particle = 0; - return; - } - - frametime = bound(0, cl.time - cl.oldtime, 0.1); - gravity = frametime * cl.movevars_gravity; - dvel = 1+4*frametime; - decalfade = frametime * 255 / cl_decals_fadetime.value; - - j = 0; - for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++) - { - if (!p->typeindex) - { - if (cl.free_particle > i) - cl.free_particle = i; - continue; - } - - if (p->delayedspawn) - { - if (p->delayedspawn > cl.time) - continue; - p->delayedspawn = 0; - } - - content = 0; - - p->size += p->sizeincrease * frametime; - p->alpha -= p->alphafade * frametime; - - if (p->alpha <= 0 || p->die <= cl.time) - { - p->typeindex = 0; - if (cl.free_particle > i) - cl.free_particle = i; - continue; - } - - if (particletype[p->typeindex].orientation != PARTICLE_BEAM && frametime > 0) - { - if (p->liquidfriction && (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)) - { - if (p->typeindex == pt_blood) - p->size += frametime * 8; - else - p->vel[2] -= p->gravity * gravity; - f = 1.0f - min(p->liquidfriction * frametime, 1); - VectorScale(p->vel, f, p->vel); - } - else - { - p->vel[2] -= p->gravity * gravity; - if (p->airfriction) - { - f = 1.0f - min(p->airfriction * frametime, 1); - VectorScale(p->vel, f, p->vel); - } - } - - VectorCopy(p->org, oldorg); - VectorMA(p->org, frametime, p->vel, p->org); - if (p->bounce && cl.time >= p->delayedcollisions) - { - trace = CL_Move(oldorg, vec3_origin, vec3_origin, p->org, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | ((p->typeindex == pt_rain || p->typeindex == pt_snow) ? SUPERCONTENTS_LIQUIDSMASK : 0), true, false, &hitent, false); - // if the trace started in or hit something of SUPERCONTENTS_NODROP - // or if the trace hit something flagged as NOIMPACT - // then remove the particle - if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT || ((trace.startsupercontents | trace.hitsupercontents) & SUPERCONTENTS_NODROP) || (trace.startsupercontents & SUPERCONTENTS_SOLID)) - { - p->typeindex = 0; - if (cl.free_particle > i) - cl.free_particle = i; - continue; - } - VectorCopy(trace.endpos, p->org); - // react if the particle hit something - if (trace.fraction < 1) - { - VectorCopy(trace.endpos, p->org); - if (p->typeindex == pt_rain) - { - // raindrop - splash on solid/water/slime/lava - int count; - // convert from a raindrop particle to a rainsplash decal - VectorCopy(trace.plane.normal, p->vel); - VectorAdd(p->org, p->vel, p->org); - p->typeindex = pt_raindecal; - p->texnum = tex_rainsplash; - p->time2 = cl.time; - p->alphafade = p->alpha / 0.4; - p->bounce = 0; - p->airfriction = 0; - p->liquidfriction = 0; - p->gravity = 0; - p->size *= 1.0f; - p->sizeincrease = p->size * 20; - count = (int)lhrandom(1, 10); - while(count--) - CL_NewParticle(pt_spark, 0x000000, 0x707070, tex_particle, 0.25f, 0, lhrandom(64, 255), 512, 1, 0, p->org[0], p->org[1], p->org[2], p->vel[0]*16, p->vel[1]*16, cl.movevars_gravity * 0.04 + p->vel[2]*16, 0, 0, 0, 32); - continue; - } - else if (p->typeindex == pt_blood) - { - // blood - splash on solid - if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS) - { - p->typeindex = 0; - continue; - } - if (cl_stainmaps.integer) - R_Stain(p->org, 32, 32, 16, 16, (int)(p->alpha * p->size * (1.0f / 40.0f)), 192, 48, 48, (int)(p->alpha * p->size * (1.0f / 40.0f))); - if (!cl_decals.integer) - { - p->typeindex = 0; - continue; - } - // create a decal for the blood splat - CL_SpawnDecalParticleForSurface(hitent, p->org, trace.plane.normal, p->color[0] * 65536 + p->color[1] * 256 + p->color[2], p->color[0] * 65536 + p->color[1] * 256 + p->color[2], tex_blooddecal[rand()&7], p->size * 2, p->alpha); - p->typeindex = 0; - if (cl.free_particle > i) - cl.free_particle = i; - continue; - } - else if (p->bounce < 0) - { - // bounce -1 means remove on impact - p->typeindex = 0; - if (cl.free_particle > i) - cl.free_particle = i; - continue; - } - else - { - // anything else - bounce off solid - dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce; - VectorMA(p->vel, dist, trace.plane.normal, p->vel); - if (DotProduct(p->vel, p->vel) < 0.03) - VectorClear(p->vel); - } - } - } - } - - if (p->typeindex != pt_static) - { - switch (p->typeindex) - { - case pt_entityparticle: - // particle that removes itself after one rendered frame - if (p->time2) - { - p->typeindex = 0; - if (cl.free_particle > i) - cl.free_particle = i; - } - else - p->time2 = 1; - break; - case pt_blood: - a = CL_PointSuperContents(p->org); - if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP)) - { - p->typeindex = 0; - if (cl.free_particle > i) - cl.free_particle = i; - } - break; - case pt_bubble: - a = CL_PointSuperContents(p->org); - if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))) - { - p->typeindex = 0; - if (cl.free_particle > i) - cl.free_particle = i; - } - break; - case pt_rain: - a = CL_PointSuperContents(p->org); - if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK)) - { - p->typeindex = 0; - if (cl.free_particle > i) - cl.free_particle = i; - } - break; - case pt_snow: - if (cl.time > p->time2) - { - // snow flutter - p->time2 = cl.time + (rand() & 3) * 0.1; - p->vel[0] = p->vel[0] * 0.9f + lhrandom(-32, 32); - p->vel[1] = p->vel[0] * 0.9f + lhrandom(-32, 32); - } - a = CL_PointSuperContents(p->org); - if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK)) - { - p->typeindex = 0; - if (cl.free_particle > i) - cl.free_particle = i; - } - break; - default: - break; - } - } - } - - // reduce cl.num_particles if possible - while (cl.num_particles > 0 && cl.particles[cl.num_particles - 1].typeindex == 0) - cl.num_particles--; -} - #define MAX_PARTICLETEXTURES 64 // particletexture_t is a rectangle in the particlefonttexture typedef struct particletexture_s @@ -2177,7 +1889,6 @@ static void r_part_newmap(void) #define BATCHSIZE 256 int particle_element3i[BATCHSIZE*6]; -float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16]; void R_Particles_Init (void) { @@ -2200,11 +1911,12 @@ void R_Particles_Init (void) void R_DrawDecal_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist) { int surfacelistindex; - int batchstart, batchcount; const decal_t *d; - pblend_t blendmode; - rtexture_t *texture; float *v3f, *t2f, *c4f; + particletexture_t *tex; + float right[3], up[3], size, ca; + float alphascale = (1.0f / 65536.0f) * cl_particles_alpha.value * r_refdef.view.colorscale; + float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16]; r_refdef.stats.decals += numsurfaces; R_Mesh_Matrix(&identitymatrix); @@ -2218,118 +1930,107 @@ void R_DrawDecal_TransparentCallback(const entity_render_t *ent, const rtlight_t GL_DepthTest(true); GL_CullFace(GL_NONE); - // first generate all the vertices at once - for (surfacelistindex = 0, v3f = particle_vertex3f, t2f = particle_texcoord2f, c4f = particle_color4f;surfacelistindex < numsurfaces;surfacelistindex++, v3f += 3*4, t2f += 2*4, c4f += 4*4) + // generate all the vertices at once + for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++) { - particletexture_t *tex; - const float *org; - float right[3], up[3], fog, cr, cg, cb, ca, size; - d = cl.decals + surfacelist[surfacelistindex]; - //blendmode = particletype[d->typeindex].blendmode; - - cr = d->color[0] * (1.0f / 255.0f) * r_refdef.view.colorscale; - cg = d->color[1] * (1.0f / 255.0f) * r_refdef.view.colorscale; - cb = d->color[2] * (1.0f / 255.0f) * r_refdef.view.colorscale; - ca = d->alpha * (1.0f / 255.0f); - //if (blendmode == PBLEND_MOD) - { - cr *= ca; - cg *= ca; - cb *= ca; - cr = min(cr, 1); - cg = min(cg, 1); - cb = min(cb, 1); - ca = 1; - } - ca *= cl_particles_alpha.value; + // calculate color + c4f = particle_color4f + 16*surfacelistindex; + ca = d->alpha * alphascale; if (r_refdef.fogenabled) - { - fog = FogPoint_World(d->org); - cr = cr * fog; - cg = cg * fog; - cb = cb * fog; - //if (blendmode == PBLEND_ALPHA) - //{ - // fog = 1 - fog; - // cr += r_refdef.fogcolor[0] * fog; - // cg += r_refdef.fogcolor[1] * fog; - // cb += r_refdef.fogcolor[2] * fog; - //} - } - c4f[0] = c4f[4] = c4f[8] = c4f[12] = cr; - c4f[1] = c4f[5] = c4f[9] = c4f[13] = cg; - c4f[2] = c4f[6] = c4f[10] = c4f[14] = cb; - c4f[3] = c4f[7] = c4f[11] = c4f[15] = ca; + ca *= FogPoint_World(d->org); + Vector4Set(c4f, d->color[0] * ca, d->color[1] * ca, d->color[2] * ca, 1); + Vector4Copy(c4f, c4f + 4); + Vector4Copy(c4f, c4f + 8); + Vector4Copy(c4f, c4f + 12); + // calculate vertex positions size = d->size * cl_particles_size.value; - org = d->org; - tex = &particletexture[d->texnum]; - - // PARTICLE_ORIENTED_DOUBLESIDED VectorVectors(d->normal, right, up); VectorScale(right, size, right); VectorScale(up, size, up); - v3f[ 0] = org[0] - right[0] - up[0]; - v3f[ 1] = org[1] - right[1] - up[1]; - v3f[ 2] = org[2] - right[2] - up[2]; - v3f[ 3] = org[0] - right[0] + up[0]; - v3f[ 4] = org[1] - right[1] + up[1]; - v3f[ 5] = org[2] - right[2] + up[2]; - v3f[ 6] = org[0] + right[0] + up[0]; - v3f[ 7] = org[1] + right[1] + up[1]; - v3f[ 8] = org[2] + right[2] + up[2]; - v3f[ 9] = org[0] + right[0] - up[0]; - v3f[10] = org[1] + right[1] - up[1]; - v3f[11] = org[2] + right[2] - up[2]; + v3f = particle_vertex3f + 12*surfacelistindex; + v3f[ 0] = d->org[0] - right[0] - up[0]; + v3f[ 1] = d->org[1] - right[1] - up[1]; + v3f[ 2] = d->org[2] - right[2] - up[2]; + v3f[ 3] = d->org[0] - right[0] + up[0]; + v3f[ 4] = d->org[1] - right[1] + up[1]; + v3f[ 5] = d->org[2] - right[2] + up[2]; + v3f[ 6] = d->org[0] + right[0] + up[0]; + v3f[ 7] = d->org[1] + right[1] + up[1]; + v3f[ 8] = d->org[2] + right[2] + up[2]; + v3f[ 9] = d->org[0] + right[0] - up[0]; + v3f[10] = d->org[1] + right[1] - up[1]; + v3f[11] = d->org[2] + right[2] - up[2]; + + // calculate texcoords + tex = &particletexture[d->texnum]; + t2f = particle_texcoord2f + 8*surfacelistindex; t2f[0] = tex->s1;t2f[1] = tex->t2; t2f[2] = tex->s1;t2f[3] = tex->t1; t2f[4] = tex->s2;t2f[5] = tex->t1; t2f[6] = tex->s2;t2f[7] = tex->t2; } - // now render batches of particles based on blendmode and texture - blendmode = PBLEND_ADD; - GL_BlendFunc(GL_SRC_ALPHA, GL_ONE); - texture = particletexture[63].texture; - R_Mesh_TexBind(0, R_GetTexture(texture)); + // now render the decals all at once + // (this assumes they all use one particle font texture!) + GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + R_Mesh_TexBind(0, R_GetTexture(particletexture[63].texture)); GL_LockArrays(0, numsurfaces*4); - batchstart = 0; - batchcount = 0; - for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++) + R_Mesh_Draw(0, numsurfaces * 4, numsurfaces * 2, particle_element3i, 0, 0); + GL_LockArrays(0, 0); +} + +void R_DrawDecals (void) +{ + int i; + decal_t *decal; + float frametime; + float decalfade; + + frametime = bound(0, cl.time - cl.decals_updatetime, 1); + cl.decals_updatetime += frametime; + + // LordHavoc: early out conditions + if ((!cl.num_decals) || (!r_drawdecals.integer)) + return; + + decalfade = frametime * 256 / cl_decals_fadetime.value; + + for (i = 0, decal = cl.decals;i < cl.num_decals;i++, decal++) { - d = cl.decals + surfacelist[surfacelistindex]; + if (!decal->typeindex) + continue; - if (blendmode != particletype[d->typeindex].blendmode) + if (cl.time > decal->time2 + cl_decals_time.value) { - if (batchcount > 0) - R_Mesh_Draw(batchstart * 4, batchcount * 4, batchcount * 2, particle_element3i + batchstart * 6, 0, 0); - batchcount = 0; - batchstart = surfacelistindex; - blendmode = particletype[d->typeindex].blendmode; - if (blendmode == PBLEND_ALPHA) - GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - else if (blendmode == PBLEND_ADD) - GL_BlendFunc(GL_SRC_ALPHA, GL_ONE); - else //if (blendmode == PBLEND_MOD) - GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + decal->alpha -= decalfade; + if (decal->alpha <= 0) + goto killdecal; } - if (texture != particletexture[d->texnum].texture) + + if (decal->owner) { - if (batchcount > 0) - R_Mesh_Draw(batchstart * 4, batchcount * 4, batchcount * 2, particle_element3i + batchstart * 6, 0, 0); - batchcount = 0; - batchstart = surfacelistindex; - texture = particletexture[d->texnum].texture; - R_Mesh_TexBind(0, R_GetTexture(texture)); + if (cl.entities[decal->owner].render.model == decal->ownermodel) + { + Matrix4x4_Transform(&cl.entities[decal->owner].render.matrix, decal->relativeorigin, decal->org); + Matrix4x4_Transform3x3(&cl.entities[decal->owner].render.matrix, decal->relativenormal, decal->normal); + } + else + goto killdecal; } - - batchcount++; + R_MeshQueue_AddTransparent(decal->org, R_DrawDecal_TransparentCallback, NULL, i, NULL); + continue; +killdecal: + decal->typeindex = 0; + if (cl.free_decal > i) + cl.free_decal = i; } - if (batchcount > 0) - R_Mesh_Draw(batchstart * 4, batchcount * 4, batchcount * 2, particle_element3i + batchstart * 6, 0, 0); - GL_LockArrays(0, 0); + + // reduce cl.num_decals if possible + while (cl.num_decals > 0 && cl.decals[cl.num_decals - 1].typeindex == 0) + cl.num_decals--; } void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist) @@ -2340,6 +2041,13 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh pblend_t blendmode; rtexture_t *texture; float *v3f, *t2f, *c4f; + particletexture_t *tex; + float up2[3], v[3], right[3], up[3], fog, ifog, size; + float ambient[3], diffuse[3], diffusenormal[3]; + vec4_t colormultiplier; + float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16]; + + Vector4Set(colormultiplier, r_refdef.view.colorscale * (1.0 / 256.0f), r_refdef.view.colorscale * (1.0 / 256.0f), r_refdef.view.colorscale * (1.0 / 256.0f), cl_particles_alpha.value * (1.0 / 256.0f)); r_refdef.stats.particles += numsurfaces; R_Mesh_Matrix(&identitymatrix); @@ -2356,76 +2064,71 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh // first generate all the vertices at once for (surfacelistindex = 0, v3f = particle_vertex3f, t2f = particle_texcoord2f, c4f = particle_color4f;surfacelistindex < numsurfaces;surfacelistindex++, v3f += 3*4, t2f += 2*4, c4f += 4*4) { - particletexture_t *tex; - const float *org; - float up2[3], v[3], right[3], up[3], fog, cr, cg, cb, ca, size; - p = cl.particles + surfacelist[surfacelistindex]; blendmode = particletype[p->typeindex].blendmode; - cr = p->color[0] * (1.0f / 255.0f) * r_refdef.view.colorscale; - cg = p->color[1] * (1.0f / 255.0f) * r_refdef.view.colorscale; - cb = p->color[2] * (1.0f / 255.0f) * r_refdef.view.colorscale; - ca = p->alpha * (1.0f / 255.0f); - if (blendmode == PBLEND_MOD) - { - cr *= ca; - cg *= ca; - cb *= ca; - cr = min(cr, 1); - cg = min(cg, 1); - cb = min(cb, 1); - ca = 1; - } - ca *= cl_particles_alpha.value; - if (particletype[p->typeindex].lighting) + c4f[0] = p->color[0] * colormultiplier[0]; + c4f[1] = p->color[1] * colormultiplier[1]; + c4f[2] = p->color[2] * colormultiplier[2]; + c4f[3] = p->alpha * colormultiplier[3]; + switch (blendmode) { - float ambient[3], diffuse[3], diffusenormal[3]; - R_CompleteLightPoint(ambient, diffuse, diffusenormal, p->org, true); - cr *= (ambient[0] + 0.5 * diffuse[0]); - cg *= (ambient[1] + 0.5 * diffuse[1]); - cb *= (ambient[2] + 0.5 * diffuse[2]); - } - if (r_refdef.fogenabled) - { - fog = FogPoint_World(p->org); - cr = cr * fog; - cg = cg * fog; - cb = cb * fog; - if (blendmode == PBLEND_ALPHA) + case PBLEND_MOD: + case PBLEND_ADD: + // additive and modulate can just fade out in fog (this is correct) + if (r_refdef.fogenabled) + c4f[3] *= FogPoint_World(p->org); + // collapse alpha into color for these blends (so that the particlefont does not need alpha on most textures) + c4f[0] *= c4f[3]; + c4f[1] *= c4f[3]; + c4f[2] *= c4f[3]; + c4f[3] = 1; + break; + case PBLEND_ALPHA: + // note: lighting is not cheap! + if (particletype[p->typeindex].lighting) { - fog = 1 - fog; - cr += r_refdef.fogcolor[0] * fog; - cg += r_refdef.fogcolor[1] * fog; - cb += r_refdef.fogcolor[2] * fog; + R_CompleteLightPoint(ambient, diffuse, diffusenormal, p->org, true); + c4f[0] *= (ambient[0] + 0.5 * diffuse[0]); + c4f[1] *= (ambient[1] + 0.5 * diffuse[1]); + c4f[2] *= (ambient[2] + 0.5 * diffuse[2]); } + // mix in the fog color + if (r_refdef.fogenabled) + { + fog = FogPoint_World(p->org); + ifog = 1 - fog; + c4f[0] = c4f[0] * fog + r_refdef.fogcolor[0] * ifog; + c4f[1] = c4f[1] * fog + r_refdef.fogcolor[1] * ifog; + c4f[2] = c4f[2] * fog + r_refdef.fogcolor[2] * ifog; + } + break; } - c4f[0] = c4f[4] = c4f[8] = c4f[12] = cr; - c4f[1] = c4f[5] = c4f[9] = c4f[13] = cg; - c4f[2] = c4f[6] = c4f[10] = c4f[14] = cb; - c4f[3] = c4f[7] = c4f[11] = c4f[15] = ca; + // copy the color into the other three vertices + Vector4Copy(c4f, c4f + 4); + Vector4Copy(c4f, c4f + 8); + Vector4Copy(c4f, c4f + 12); size = p->size * cl_particles_size.value; - org = p->org; tex = &particletexture[p->texnum]; switch(particletype[p->typeindex].orientation) { case PARTICLE_BILLBOARD: VectorScale(r_refdef.view.left, -size, right); VectorScale(r_refdef.view.up, size, up); - v3f[ 0] = org[0] - right[0] - up[0]; - v3f[ 1] = org[1] - right[1] - up[1]; - v3f[ 2] = org[2] - right[2] - up[2]; - v3f[ 3] = org[0] - right[0] + up[0]; - v3f[ 4] = org[1] - right[1] + up[1]; - v3f[ 5] = org[2] - right[2] + up[2]; - v3f[ 6] = org[0] + right[0] + up[0]; - v3f[ 7] = org[1] + right[1] + up[1]; - v3f[ 8] = org[2] + right[2] + up[2]; - v3f[ 9] = org[0] + right[0] - up[0]; - v3f[10] = org[1] + right[1] - up[1]; - v3f[11] = org[2] + right[2] - up[2]; + v3f[ 0] = p->org[0] - right[0] - up[0]; + v3f[ 1] = p->org[1] - right[1] - up[1]; + v3f[ 2] = p->org[2] - right[2] - up[2]; + v3f[ 3] = p->org[0] - right[0] + up[0]; + v3f[ 4] = p->org[1] - right[1] + up[1]; + v3f[ 5] = p->org[2] - right[2] + up[2]; + v3f[ 6] = p->org[0] + right[0] + up[0]; + v3f[ 7] = p->org[1] + right[1] + up[1]; + v3f[ 8] = p->org[2] + right[2] + up[2]; + v3f[ 9] = p->org[0] + right[0] - up[0]; + v3f[10] = p->org[1] + right[1] - up[1]; + v3f[11] = p->org[2] + right[2] - up[2]; t2f[0] = tex->s1;t2f[1] = tex->t2; t2f[2] = tex->s1;t2f[3] = tex->t1; t2f[4] = tex->s2;t2f[5] = tex->t1; @@ -2435,26 +2138,26 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh VectorVectors(p->vel, right, up); VectorScale(right, size, right); VectorScale(up, size, up); - v3f[ 0] = org[0] - right[0] - up[0]; - v3f[ 1] = org[1] - right[1] - up[1]; - v3f[ 2] = org[2] - right[2] - up[2]; - v3f[ 3] = org[0] - right[0] + up[0]; - v3f[ 4] = org[1] - right[1] + up[1]; - v3f[ 5] = org[2] - right[2] + up[2]; - v3f[ 6] = org[0] + right[0] + up[0]; - v3f[ 7] = org[1] + right[1] + up[1]; - v3f[ 8] = org[2] + right[2] + up[2]; - v3f[ 9] = org[0] + right[0] - up[0]; - v3f[10] = org[1] + right[1] - up[1]; - v3f[11] = org[2] + right[2] - up[2]; + v3f[ 0] = p->org[0] - right[0] - up[0]; + v3f[ 1] = p->org[1] - right[1] - up[1]; + v3f[ 2] = p->org[2] - right[2] - up[2]; + v3f[ 3] = p->org[0] - right[0] + up[0]; + v3f[ 4] = p->org[1] - right[1] + up[1]; + v3f[ 5] = p->org[2] - right[2] + up[2]; + v3f[ 6] = p->org[0] + right[0] + up[0]; + v3f[ 7] = p->org[1] + right[1] + up[1]; + v3f[ 8] = p->org[2] + right[2] + up[2]; + v3f[ 9] = p->org[0] + right[0] - up[0]; + v3f[10] = p->org[1] + right[1] - up[1]; + v3f[11] = p->org[2] + right[2] - up[2]; t2f[0] = tex->s1;t2f[1] = tex->t2; t2f[2] = tex->s1;t2f[3] = tex->t1; t2f[4] = tex->s2;t2f[5] = tex->t1; t2f[6] = tex->s2;t2f[7] = tex->t2; break; case PARTICLE_SPARK: - VectorMA(org, -0.02, p->vel, v); - VectorMA(org, 0.02, p->vel, up2); + VectorMA(p->org, -0.02, p->vel, v); + VectorMA(p->org, 0.02, p->vel, up2); R_CalcBeam_Vertex3f(v3f, v, up2, size); t2f[0] = tex->s1;t2f[1] = tex->t2; t2f[2] = tex->s1;t2f[3] = tex->t1; @@ -2462,10 +2165,10 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh t2f[6] = tex->s2;t2f[7] = tex->t2; break; case PARTICLE_BEAM: - R_CalcBeam_Vertex3f(v3f, org, p->vel, size); - VectorSubtract(p->vel, org, up); + R_CalcBeam_Vertex3f(v3f, p->org, p->vel, size); + VectorSubtract(p->vel, p->org, up); VectorNormalize(up); - v[0] = DotProduct(org, up) * (1.0f / 64.0f); + v[0] = DotProduct(p->org, up) * (1.0f / 64.0f); v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f); t2f[0] = 1;t2f[1] = v[0]; t2f[2] = 0;t2f[3] = v[0]; @@ -2476,82 +2179,244 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh } // now render batches of particles based on blendmode and texture - blendmode = PBLEND_ADD; - GL_BlendFunc(GL_SRC_ALPHA, GL_ONE); - texture = particletexture[63].texture; - R_Mesh_TexBind(0, R_GetTexture(texture)); + blendmode = -1; + texture = NULL; GL_LockArrays(0, numsurfaces*4); batchstart = 0; batchcount = 0; - for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++) + for (surfacelistindex = 0;surfacelistindex < numsurfaces;) { p = cl.particles + surfacelist[surfacelistindex]; if (blendmode != particletype[p->typeindex].blendmode) { - if (batchcount > 0) - R_Mesh_Draw(batchstart * 4, batchcount * 4, batchcount * 2, particle_element3i + batchstart * 6, 0, 0); - batchcount = 0; - batchstart = surfacelistindex; blendmode = particletype[p->typeindex].blendmode; - if (blendmode == PBLEND_ALPHA) + switch(blendmode) + { + case PBLEND_ALPHA: GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - else if (blendmode == PBLEND_ADD) + break; + case PBLEND_ADD: GL_BlendFunc(GL_SRC_ALPHA, GL_ONE); - else //if (blendmode == PBLEND_MOD) + break; + case PBLEND_MOD: GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + break; + } } if (texture != particletexture[p->texnum].texture) { - if (batchcount > 0) - R_Mesh_Draw(batchstart * 4, batchcount * 4, batchcount * 2, particle_element3i + batchstart * 6, 0, 0); - batchcount = 0; - batchstart = surfacelistindex; texture = particletexture[p->texnum].texture; R_Mesh_TexBind(0, R_GetTexture(texture)); } - batchcount++; - } - if (batchcount > 0) - R_Mesh_Draw(batchstart * 4, batchcount * 4, batchcount * 2, particle_element3i + batchstart * 6, 0, 0); - GL_LockArrays(0, 0); -} - -void R_DrawDecals (void) -{ - int i; - const decal_t *d; - - // LordHavoc: early out conditions - if ((!cl.num_decals) || (!r_drawdecals.integer)) - return; - - // LordHavoc: only render if not too close - for (i = 0, d = cl.decals;i < cl.num_decals;i++, d++) - { - if (d->typeindex) + // iterate until we find a change in settings + batchstart = surfacelistindex++; + for (;surfacelistindex < numsurfaces;surfacelistindex++) { - r_refdef.stats.decals++; - R_MeshQueue_AddTransparent(d->org, R_DrawDecal_TransparentCallback, NULL, i, NULL); + p = cl.particles + surfacelist[surfacelistindex]; + if (blendmode != particletype[p->typeindex].blendmode || texture != particletexture[p->texnum].texture) + break; } + + batchcount = surfacelistindex - batchstart; + R_Mesh_Draw(batchstart * 4, batchcount * 4, batchcount * 2, particle_element3i + batchstart * 6, 0, 0); } + GL_LockArrays(0, 0); } void R_DrawParticles (void) { - int i; + int i, j, a, content; float minparticledist; particle_t *p; + float gravity, dvel, decalfade, frametime, f, dist, oldorg[3]; + int hitent; + trace_t trace; + qboolean update; + + frametime = bound(0, cl.time - cl.particles_updatetime, 1); + cl.particles_updatetime += frametime; // LordHavoc: early out conditions if ((!cl.num_particles) || (!r_drawparticles.integer)) return; minparticledist = DotProduct(r_refdef.view.origin, r_refdef.view.forward) + 4.0f; + gravity = frametime * cl.movevars_gravity; + dvel = 1+4*frametime; + decalfade = frametime * 255 / cl_decals_fadetime.value; + update = frametime > 0; - // LordHavoc: only render if not too close + j = 0; for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++) - if (p->typeindex && !p->delayedspawn && (DotProduct(p->org, r_refdef.view.forward) >= minparticledist || particletype[p->typeindex].orientation == PARTICLE_BEAM)) + { + if (!p->typeindex) + continue; + + if (update) + { + if (p->delayedspawn > cl.time) + continue; + p->delayedspawn = 0; + + content = 0; + + p->size += p->sizeincrease * frametime; + p->alpha -= p->alphafade * frametime; + + if (p->alpha <= 0 || p->die <= cl.time) + goto killparticle; + + if (particletype[p->typeindex].orientation != PARTICLE_BEAM && frametime > 0) + { + if (p->liquidfriction && (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)) + { + if (p->typeindex == pt_blood) + p->size += frametime * 8; + else + p->vel[2] -= p->gravity * gravity; + f = 1.0f - min(p->liquidfriction * frametime, 1); + VectorScale(p->vel, f, p->vel); + } + else + { + p->vel[2] -= p->gravity * gravity; + if (p->airfriction) + { + f = 1.0f - min(p->airfriction * frametime, 1); + VectorScale(p->vel, f, p->vel); + } + } + + VectorCopy(p->org, oldorg); + VectorMA(p->org, frametime, p->vel, p->org); + if (p->bounce && cl.time >= p->delayedcollisions) + { + trace = CL_Move(oldorg, vec3_origin, vec3_origin, p->org, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | ((p->typeindex == pt_rain || p->typeindex == pt_snow) ? SUPERCONTENTS_LIQUIDSMASK : 0), true, false, &hitent, false); + // if the trace started in or hit something of SUPERCONTENTS_NODROP + // or if the trace hit something flagged as NOIMPACT + // then remove the particle + if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT || ((trace.startsupercontents | trace.hitsupercontents) & SUPERCONTENTS_NODROP) || (trace.startsupercontents & SUPERCONTENTS_SOLID)) + goto killparticle; + VectorCopy(trace.endpos, p->org); + // react if the particle hit something + if (trace.fraction < 1) + { + VectorCopy(trace.endpos, p->org); + if (p->typeindex == pt_rain) + { + // raindrop - splash on solid/water/slime/lava + int count; + // convert from a raindrop particle to a rainsplash decal + VectorCopy(trace.plane.normal, p->vel); + VectorAdd(p->org, p->vel, p->org); + p->typeindex = pt_raindecal; + p->texnum = tex_rainsplash; + p->time2 = cl.time; + p->alphafade = p->alpha / 0.4; + p->bounce = 0; + p->airfriction = 0; + p->liquidfriction = 0; + p->gravity = 0; + p->size *= 1.0f; + p->sizeincrease = p->size * 20; + count = (int)lhrandom(1, 10); + while(count--) + CL_NewParticle(pt_spark, 0x000000, 0x707070, tex_particle, 0.25f, 0, lhrandom(64, 255), 512, 1, 0, p->org[0], p->org[1], p->org[2], p->vel[0]*16, p->vel[1]*16, cl.movevars_gravity * 0.04 + p->vel[2]*16, 0, 0, 0, 32); + continue; + } + else if (p->typeindex == pt_blood) + { + // blood - splash on solid + if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS) + goto killparticle; + if (cl_stainmaps.integer) + R_Stain(p->org, 32, 32, 16, 16, (int)(p->alpha * p->size * (1.0f / 40.0f)), 192, 48, 48, (int)(p->alpha * p->size * (1.0f / 40.0f))); + if (cl_decals.integer) + { + // create a decal for the blood splat + CL_SpawnDecalParticleForSurface(hitent, p->org, trace.plane.normal, p->color[0] * 65536 + p->color[1] * 256 + p->color[2], p->color[0] * 65536 + p->color[1] * 256 + p->color[2], tex_blooddecal[rand()&7], p->size * 2, p->alpha); + } + goto killparticle; + } + else if (p->bounce < 0) + { + // bounce -1 means remove on impact + goto killparticle; + } + else + { + // anything else - bounce off solid + dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce; + VectorMA(p->vel, dist, trace.plane.normal, p->vel); + if (DotProduct(p->vel, p->vel) < 0.03) + VectorClear(p->vel); + } + } + } + } + + if (p->typeindex != pt_static) + { + switch (p->typeindex) + { + case pt_entityparticle: + // particle that removes itself after one rendered frame + if (p->time2) + goto killparticle; + else + p->time2 = 1; + break; + case pt_blood: + a = CL_PointSuperContents(p->org); + if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP)) + goto killparticle; + break; + case pt_bubble: + a = CL_PointSuperContents(p->org); + if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))) + goto killparticle; + break; + case pt_rain: + a = CL_PointSuperContents(p->org); + if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK)) + goto killparticle; + break; + case pt_snow: + if (cl.time > p->time2) + { + // snow flutter + p->time2 = cl.time + (rand() & 3) * 0.1; + p->vel[0] = p->vel[0] * 0.9f + lhrandom(-32, 32); + p->vel[1] = p->vel[0] * 0.9f + lhrandom(-32, 32); + } + a = CL_PointSuperContents(p->org); + if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK)) + goto killparticle; + break; + default: + break; + } + } + } + else if (p->delayedspawn) + continue; + + // don't render particles too close to the view (they chew fillrate) + // also don't render particles behind the view (useless) + // further checks to cull to the frustum would be too slow here + if (DotProduct(p->org, r_refdef.view.forward) >= minparticledist || particletype[p->typeindex].orientation == PARTICLE_BEAM) R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL); + + continue; +killparticle: + p->typeindex = 0; + if (cl.free_particle > i) + cl.free_particle = i; + } + + // reduce cl.num_particles if possible + while (cl.num_particles > 0 && cl.particles[cl.num_particles - 1].typeindex == 0) + cl.num_particles--; } diff --git a/client.h b/client.h index a37bff27..6546773a 100644 --- a/client.h +++ b/client.h @@ -639,37 +639,44 @@ ptype_t; typedef struct decal_s { + // fields used by rendering: (40 bytes) unsigned short typeindex; unsigned short texnum; vec3_t org; vec3_t normal; float size; float alpha; // 0-255 - float time2; // used for snow fluttering and decal fade unsigned char color[4]; + + // fields not used by rendering: (36 bytes in 32bit, 40 bytes in 64bit) + float time2; // used for decal fade unsigned int owner; // decal stuck to this entity model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive) vec3_t relativeorigin; // decal at this location in entity's coordinate space vec3_t relativenormal; // decal oriented this way relative to entity's coordinate space + } decal_t; typedef struct particle_s { + // fields used by rendering: (40 bytes) unsigned short typeindex; unsigned short texnum; vec3_t org; vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam float size; - float sizeincrease; // rate of size change per second float alpha; // 0-255 + unsigned char color[4]; + + // fields not used by rendering: (40 bytes) + float sizeincrease; // rate of size change per second float alphafade; // how much alpha reduces per second float time2; // used for snow fluttering and decal fade float bounce; // how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide, 2 = keep bouncing forever, 1.5 is typical) float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none) float airfriction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction) float liquidfriction; // how much liquid friction affects this object (objects with a low mass/size ratio tend to get more liquid friction) - unsigned char color[4]; float delayedcollisions; // time that p->bounce becomes active float delayedspawn; // time that particle appears and begins moving float die; // time when this particle should be removed, regardless of alpha @@ -952,6 +959,8 @@ typedef struct client_state_s int num_decals; int num_showlmps; + double particles_updatetime; + double decals_updatetime; int free_particle; int free_decal; @@ -985,11 +994,14 @@ typedef struct client_state_s // use cl.scores[cl.playerentity-1].qw_spectator instead //qboolean qw_spectator; + // last time an input packet was sent + double lastpackettime; + // movement parameters for client prediction float movevars_wallfriction; float movevars_waterfriction; float movevars_friction; - float movevars_ticrate; + float movevars_packetinterval; // in game time (cl.time), not realtime float movevars_timescale; float movevars_gravity; float movevars_stopspeed; @@ -1300,9 +1312,6 @@ void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, in void CL_EntityParticles (const entity_t *ent); void CL_ParticleExplosion (const vec3_t org); void CL_ParticleExplosion2 (const vec3_t org, int colorStart, int colorLength); -void CL_MoveDecals(void); -void CL_MoveParticles(void); -void R_MoveExplosions(void); void R_NewExplosion(const vec3_t org); void Debug_PolygonBegin(const char *picname, int flags, qboolean draw2d, float linewidth); diff --git a/netconn.c b/netconn.c index 54f81fad..27f16c1d 100755 --- a/netconn.c +++ b/netconn.c @@ -567,18 +567,18 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers sendreliable = true; } // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1) - *((int *)(sendbuffer + 0)) = LittleLong((unsigned int)conn->qw.outgoing_sequence | ((unsigned int)sendreliable<<31)); + *((int *)(sendbuffer + 0)) = LittleLong((unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31)); // last received unreliable packet number, and last received reliable packet number (0 or 1) *((int *)(sendbuffer + 4)) = LittleLong((unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31)); packetLen = 8; - conn->qw.outgoing_sequence++; + conn->outgoing_unreliable_sequence++; // client sends qport in every packet if (conn == cls.netcon) { *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport); packetLen += 2; // also update cls.qw_outgoing_sequence - cls.qw_outgoing_sequence = conn->qw.outgoing_sequence; + cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence; } if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400) { @@ -594,7 +594,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers conn->outgoing_reliablesize[conn->outgoing_packetcounter] += conn->sendMessageLength; memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength); packetLen += conn->sendMessageLength; - conn->qw.last_reliable_sequence = conn->qw.outgoing_sequence; + conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence; } // add the unreliable message if possible @@ -715,10 +715,10 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers header = (unsigned int *)sendbuffer; header[0] = BigLong(packetLen | NETFLAG_UNRELIABLE); - header[1] = BigLong(conn->nq.unreliableSendSequence); + header[1] = BigLong(conn->outgoing_unreliable_sequence); memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize); - conn->nq.unreliableSendSequence++; + conn->outgoing_unreliable_sequence++; conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += packetLen; @@ -1208,7 +1208,6 @@ void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peer cls.signon = 0; // need all the signon messages before playing cls.protocol = initialprotocol; // reset move sequence numbering on this new connection - cls.movesequence = 1; cls.servermovesequence = 0; if (cls.protocol == PROTOCOL_QUAKEWORLD) Cmd_ForwardStringToServer("new"); @@ -2657,7 +2656,7 @@ static void Net_Heartbeat_f(void) void PrintStats(netconn_t *conn) { if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD)) - Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->qw.outgoing_sequence, conn->qw.incoming_sequence); + Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence); else Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence); } diff --git a/netconn.h b/netconn.h index 2ac31331..a755d286 100755 --- a/netconn.h +++ b/netconn.h @@ -142,11 +142,13 @@ typedef struct netconn_s int receiveMessageLength; unsigned char receiveMessage[NET_MAXMESSAGE]; + // used by both NQ and QW protocols + unsigned int outgoing_unreliable_sequence; + struct netconn_nq_s { unsigned int ackSequence; unsigned int sendSequence; - unsigned int unreliableSendSequence; unsigned int receiveSequence; unsigned int unreliableReceiveSequence; @@ -176,7 +178,6 @@ typedef struct netconn_s int incoming_reliable_sequence; // single bit, maintained local - int outgoing_sequence; int reliable_sequence; // single bit int last_reliable_sequence; // sequence number of last send } diff --git a/r_explosion.c b/r_explosion.c index 1d822eca..410fa67a 100644 --- a/r_explosion.c +++ b/r_explosion.c @@ -250,25 +250,23 @@ static void R_MoveExplosion(explosion_t *e) } } - -void R_MoveExplosions(void) -{ - int i; - for (i = 0;i < numexplosions;i++) - if (explosion[i].alpha) - R_MoveExplosion(&explosion[i]); - while (numexplosions > 0 && explosion[i-1].alpha <= 0) - numexplosions--; -} - void R_DrawExplosions(void) { int i; if (!r_drawexplosions.integer) return; + for (i = 0;i < numexplosions;i++) + { if (explosion[i].alpha) - R_MeshQueue_AddTransparent(explosion[i].origin, R_DrawExplosion_TransparentCallback, NULL, i, NULL); + { + R_MoveExplosion(&explosion[i]); + if (explosion[i].alpha) + R_MeshQueue_AddTransparent(explosion[i].origin, R_DrawExplosion_TransparentCallback, NULL, i, NULL); + } + } + while (numexplosions > 0 && explosion[i-1].alpha <= 0) + numexplosions--; }