X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=cl_particles.c;h=74925fef00e12c6bdbeb2c7669f1819e2c66c8d2;hp=72a280ed2e41eda07abf0abe63e8dab09c1c149e;hb=3dbf0ca46d1e54ba7bc028217ae444f30f37b5af;hpb=0a094efd4075f5a4adf37b391e047b150c902b17 diff --git a/cl_particles.c b/cl_particles.c index 72a280ed..74925fef 100644 --- a/cl_particles.c +++ b/cl_particles.c @@ -24,6 +24,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "image.h" #include "r_shadow.h" +#define ABSOLUTE_MAX_PARTICLES 1<<24 // upper limit on cl.max_particles +#define ABSOLUTE_MAX_DECALS 1<<24 // upper limit on cl.max_decals + // must match ptype_t values particletype_t particletype[pt_total] = { @@ -456,7 +459,7 @@ void CL_Particles_Shutdown (void) // px,py,pz - starting origin of particle // pvx,pvy,pvz - starting velocity of particle // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1) -static particle_t *CL_NewParticle(unsigned short ptypeindex, int pcolor1, int pcolor2, int ptex, float psize, float psizeincrease, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pairfriction, float pliquidfriction, float originjitter, float velocityjitter) +static particle_t *CL_NewParticle(unsigned short ptypeindex, int pcolor1, int pcolor2, int ptex, float psize, float psizeincrease, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pairfriction, float pliquidfriction, float originjitter, float velocityjitter, qboolean pqualityreduction, float lifetime) { int l1, l2; particle_t *part; @@ -466,6 +469,8 @@ static particle_t *CL_NewParticle(unsigned short ptypeindex, int pcolor1, int pc for (;cl.free_particle < cl.max_particles && cl.particles[cl.free_particle].typeindex;cl.free_particle++); if (cl.free_particle >= cl.max_particles) return NULL; + if (!lifetime) + lifetime = palpha / min(1, palphafade); part = &cl.particles[cl.free_particle++]; if (cl.num_particles < cl.free_particle) cl.num_particles = cl.free_particle; @@ -476,7 +481,6 @@ static particle_t *CL_NewParticle(unsigned short ptypeindex, int pcolor1, int pc part->color[0] = ((((pcolor1 >> 16) & 0xFF) * l1 + ((pcolor2 >> 16) & 0xFF) * l2) >> 8) & 0xFF; part->color[1] = ((((pcolor1 >> 8) & 0xFF) * l1 + ((pcolor2 >> 8) & 0xFF) * l2) >> 8) & 0xFF; part->color[2] = ((((pcolor1 >> 0) & 0xFF) * l1 + ((pcolor2 >> 0) & 0xFF) * l2) >> 8) & 0xFF; - part->color[3] = 0xFF; part->texnum = ptex; part->size = psize; part->sizeincrease = psizeincrease; @@ -494,8 +498,9 @@ static particle_t *CL_NewParticle(unsigned short ptypeindex, int pcolor1, int pc part->time2 = 0; part->airfriction = pairfriction; part->liquidfriction = pliquidfriction; - part->die = cl.time + part->alpha / (part->alphafade ? part->alphafade : 1); + part->die = cl.time + lifetime; part->delayedcollisions = 0; + part->qualityreduction = pqualityreduction; if (part->typeindex == pt_blood) part->gravity += 1; // FIXME: this is a legacy hack, effectinfo.txt doesn't have gravity on blood (nor do the particle calls in the engine) // if it is rain or snow, trace ahead and shut off collisions until an actual collision event needs to occur to improve performance @@ -512,14 +517,14 @@ static particle_t *CL_NewParticle(unsigned short ptypeindex, int pcolor1, int pc VectorMA(part->org, lifetime, part->vel, endvec); trace = CL_Move(part->org, vec3_origin, vec3_origin, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK, true, false, NULL, false); part->die = cl.time + lifetime * trace.fraction; - part2 = CL_NewParticle(pt_raindecal, pcolor1, pcolor2, tex_rainsplash, part->size, part->size * 20, part->alpha, part->alpha / 0.4, 0, 0, trace.endpos[0] + trace.plane.normal[0], trace.endpos[1] + trace.plane.normal[1], trace.endpos[2] + trace.plane.normal[2], trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2], 0, 0, 0, 0); + part2 = CL_NewParticle(pt_raindecal, pcolor1, pcolor2, tex_rainsplash, part->size, part->size * 20, part->alpha, part->alpha / 0.4, 0, 0, trace.endpos[0] + trace.plane.normal[0], trace.endpos[1] + trace.plane.normal[1], trace.endpos[2] + trace.plane.normal[2], trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2], 0, 0, 0, 0, pqualityreduction, 0); if (part2) { part2->delayedspawn = part->die; part2->die += part->die - cl.time; for (i = rand() & 7;i < 10;i++) { - part2 = CL_NewParticle(pt_spark, pcolor1, pcolor2, tex_particle, 0.25f, 0, part->alpha * 2, part->alpha * 4, 1, 0, trace.endpos[0] + trace.plane.normal[0], trace.endpos[1] + trace.plane.normal[1], trace.endpos[2] + trace.plane.normal[2], trace.plane.normal[0] * 16, trace.plane.normal[1] * 16, trace.plane.normal[2] * 16 + cl.movevars_gravity * 0.04, 0, 0, 0, 32); + part2 = CL_NewParticle(pt_spark, pcolor1, pcolor2, tex_particle, 0.25f, 0, part->alpha * 2, part->alpha * 4, 1, 0, trace.endpos[0] + trace.plane.normal[0], trace.endpos[1] + trace.plane.normal[1], trace.endpos[2] + trace.plane.normal[2], trace.plane.normal[0] * 16, trace.plane.normal[1] * 16, trace.plane.normal[2] * 16 + cl.movevars_gravity * 0.04, 0, 0, 0, 32, pqualityreduction, 0); if (part2) { part2->delayedspawn = part->die; @@ -565,7 +570,6 @@ void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t decal->color[0] = ((((color1 >> 16) & 0xFF) * l1 + ((color2 >> 16) & 0xFF) * l2) >> 8) & 0xFF; decal->color[1] = ((((color1 >> 8) & 0xFF) * l1 + ((color2 >> 8) & 0xFF) * l2) >> 8) & 0xFF; decal->color[2] = ((((color1 >> 0) & 0xFF) * l1 + ((color2 >> 0) & 0xFF) * l2) >> 8) & 0xFF; - decal->color[3] = 0xFF; decal->owner = hitent; if (hitent) { @@ -627,11 +631,11 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o { int k = particlepalette[palettecolor + (rand()&7)]; if (cl_particles_quake.integer) - CL_NewParticle(pt_alphastatic, k, k, tex_particle, 1.5, 0, lhrandom(51, 255), 512, 0.05, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 8, 0); + CL_NewParticle(pt_alphastatic, k, k, tex_particle, 1.5, 0, lhrandom(51, 255), 512, 0.05, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 8, 0, true, 0); else if (gamemode == GAME_GOODVSBAD2) - CL_NewParticle(pt_alphastatic, k, k, tex_particle, 5, 0, 255, 300, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 8, 10); + CL_NewParticle(pt_alphastatic, k, k, tex_particle, 5, 0, 255, 300, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 8, 10, true, 0); else - CL_NewParticle(pt_alphastatic, k, k, tex_particle, 1.5, 0, 255, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 8, 15); + CL_NewParticle(pt_alphastatic, k, k, tex_particle, 1.5, 0, 255, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 8, 15, true, 0); } } } @@ -729,7 +733,7 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o static double bloodaccumulator = 0; bloodaccumulator += count * 0.333 * cl_particles_quality.value; for (;bloodaccumulator > 0;bloodaccumulator--) - CL_NewParticle(pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, cl_particles_blood_alpha.value * 768, cl_particles_blood_alpha.value * 384, 0, -1, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 64); + CL_NewParticle(pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, cl_particles_blood_alpha.value * 768, cl_particles_blood_alpha.value * 384, 0, -1, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 64, true, 0); } } else if (effectnameindex == EFFECT_TE_SPARK) @@ -792,9 +796,9 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o for (i = 0;i < 1024 * cl_particles_quality.value;i++) { if (i & 1) - CL_NewParticle(pt_static, particlepalette[66], particlepalette[71], tex_particle, 1.5f, 0, lhrandom(182, 255), 182, 0, 0, center[0], center[1], center[2], 0, 0, 0, -4, -4, 16, 256); + CL_NewParticle(pt_static, particlepalette[66], particlepalette[71], tex_particle, 1.5f, 0, lhrandom(182, 255), 182, 0, 0, center[0], center[1], center[2], 0, 0, 0, -4, -4, 16, 256, true, 0); else - CL_NewParticle(pt_static, particlepalette[150], particlepalette[155], tex_particle, 1.5f, 0, lhrandom(182, 255), 182, 0, 0, center[0], center[1], center[2], 0, 0, lhrandom(-256, 256), 0, 0, 16, 0); + CL_NewParticle(pt_static, particlepalette[150], particlepalette[155], tex_particle, 1.5f, 0, lhrandom(182, 255), 182, 0, 0, center[0], center[1], center[2], 0, 0, lhrandom(-256, 256), 0, 0, 16, 0, true, 0); } } else @@ -807,7 +811,7 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o { count *= cl_particles_quality.value; while (count-- > 0) - CL_NewParticle(pt_smoke, 0x6f0f00, 0xe3974f, tex_particle, 4, 0, lhrandom(64, 128), 384, -1, 1.1, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 128); + CL_NewParticle(pt_smoke, 0x6f0f00, 0xe3974f, tex_particle, 4, 0, lhrandom(64, 128), 384, -1, 1.1, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 128, true, 0); } else if (effectnameindex == EFFECT_TE_LAVASPLASH) { @@ -826,7 +830,7 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o org[1] = center[1] + dir[1]; org[2] = center[2] + lhrandom(0, 64); vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale - CL_NewParticle(pt_alphastatic, particlepalette[224], particlepalette[231], tex_particle, 1.5f, 0, inc * lhrandom(24, 32), inc * 12, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0); + CL_NewParticle(pt_alphastatic, particlepalette[224], particlepalette[231], tex_particle, 1.5f, 0, inc * lhrandom(24, 32), inc * 12, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, true, 0); } } } @@ -845,22 +849,22 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o VectorSet(dir, i*8, j*8, k*8); VectorNormalize(dir); vel = lhrandom(50, 113); - CL_NewParticle(pt_alphastatic, particlepalette[7], particlepalette[14], tex_particle, 1.5f, 0, inc * lhrandom(37, 63), inc * 187, 0, 0, center[0] + i + lhrandom(0, inc), center[1] + j + lhrandom(0, inc), center[2] + k + lhrandom(0, inc), dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0); + CL_NewParticle(pt_alphastatic, particlepalette[7], particlepalette[14], tex_particle, 1.5f, 0, inc * lhrandom(37, 63), inc * 187, 0, 0, center[0] + i + lhrandom(0, inc), center[1] + j + lhrandom(0, inc), center[2] + k + lhrandom(0, inc), dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, true, 0); } } } - CL_NewParticle(pt_static, particlepalette[14], particlepalette[14], tex_particle, 30, 0, 256, 512, 0, 0, center[0], center[1], center[2], 0, 0, 0, 0, 0, 0, 0); + CL_NewParticle(pt_static, particlepalette[14], particlepalette[14], tex_particle, 30, 0, 256, 512, 0, 0, center[0], center[1], center[2], 0, 0, 0, 0, 0, 0, 0, false, 0); CL_AllocLightFlash(NULL, &tempmatrix, 200, 2.0f, 2.0f, 2.0f, 400, 99.0f, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_TE_TEI_G3) - CL_NewParticle(pt_beam, 0xFFFFFF, 0xFFFFFF, tex_beam, 8, 0, 256, 256, 0, 0, originmins[0], originmins[1], originmins[2], originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, 0); + CL_NewParticle(pt_beam, 0xFFFFFF, 0xFFFFFF, tex_beam, 8, 0, 256, 256, 0, 0, originmins[0], originmins[1], originmins[2], originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, 0, false, 0); else if (effectnameindex == EFFECT_TE_TEI_SMOKE) { if (cl_particles_smoke.integer) { count *= 0.25f * cl_particles_quality.value; while (count-- > 0) - CL_NewParticle(pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 0, 255, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 1.5f, 6.0f); + CL_NewParticle(pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 0, 255, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 1.5f, 6.0f, true, 0); } } else if (effectnameindex == EFFECT_TE_TEI_BIGEXPLOSION) @@ -876,24 +880,24 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o CL_SpawnDecalParticleForPoint(center, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF); if (cl_particles_smoke.integer) for (f = 0;f < count;f += 4.0f / cl_particles_quality.value) - CL_NewParticle(pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 0, 255, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 20, 155); + CL_NewParticle(pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 0, 255, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 20, 155, true, 0); if (cl_particles_sparks.integer) for (f = 0;f < count;f += 1.0f / cl_particles_quality.value) - CL_NewParticle(pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, 0, lhrandom(64, 255), 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 0, 465); + CL_NewParticle(pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, 0, lhrandom(64, 255), 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 0, 465, true, 0); CL_AllocLightFlash(NULL, &tempmatrix, 500, 0.6f, 1.2f, 2.0f, 2000, 9999, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_EF_FLAME) { count *= 300 * cl_particles_quality.value; while (count-- > 0) - CL_NewParticle(pt_smoke, 0x6f0f00, 0xe3974f, tex_particle, 4, 0, lhrandom(64, 128), 384, -1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 16, 128); + CL_NewParticle(pt_smoke, 0x6f0f00, 0xe3974f, tex_particle, 4, 0, lhrandom(64, 128), 384, -1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 16, 128, true, 0); CL_AllocLightFlash(NULL, &tempmatrix, 200, 2.0f, 1.5f, 0.5f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (effectnameindex == EFFECT_EF_STARDUST) { count *= 200 * cl_particles_quality.value; while (count-- > 0) - CL_NewParticle(pt_static, 0x903010, 0xFFD030, tex_particle, 4, 0, lhrandom(64, 128), 128, 1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0.2, 0.8, 16, 128); + CL_NewParticle(pt_static, 0x903010, 0xFFD030, tex_particle, 4, 0, lhrandom(64, 128), 128, 1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0.2, 0.8, 16, 128, true, 0); CL_AllocLightFlash(NULL, &tempmatrix, 200, 1.0f, 0.7f, 0.3f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } else if (!strncmp(particleeffectname[effectnameindex], "TR_", 3)) @@ -902,7 +906,7 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o float len, dec, qd; int smoke, blood, bubbles, r, color; - if (spawndlight && r_refdef.numlights < MAX_DLIGHTS) + if (spawndlight && r_refdef.scene.numlights < MAX_DLIGHTS) { vec4_t light; Vector4Set(light, 0, 0, 0, 0); @@ -923,7 +927,7 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o { matrix4x4_t tempmatrix; Matrix4x4_CreateFromQuakeEntity(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, light[3]); - R_RTLight_Update(&r_refdef.lights[r_refdef.numlights++], false, &tempmatrix, light, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + R_RTLight_Update(&r_refdef.scene.lights[r_refdef.scene.numlights++], false, &tempmatrix, light, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } } @@ -967,12 +971,12 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o if (cl_particles_quake.integer) { color = particlepalette[67 + (rand()&3)]; - CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 128, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0); + CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 128, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0); } else { dec = 16; - CL_NewParticle(pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 64); + CL_NewParticle(pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 64, true, 0); } } else if (effectnameindex == EFFECT_TR_SLIGHTBLOOD) @@ -981,12 +985,12 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o { dec = 6; color = particlepalette[67 + (rand()&3)]; - CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 128, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0); + CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 128, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0); } else { dec = 32; - CL_NewParticle(pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 64); + CL_NewParticle(pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 64, true, 0); } } } @@ -998,12 +1002,12 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o { r = rand()&3; color = particlepalette[ramp3[r]]; - CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 42*(6-r), 306, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0); + CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 42*(6-r), 306, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0); } else { - CL_NewParticle(pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*62, cl_particles_smoke_alphafade.value*62, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0); - CL_NewParticle(pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*288, cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 20); + CL_NewParticle(pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*62, cl_particles_smoke_alphafade.value*62, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0); + CL_NewParticle(pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*288, cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 20, true, 0); } } else if (effectnameindex == EFFECT_TR_GRENADE) @@ -1012,11 +1016,11 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o { r = 2 + (rand()%5); color = particlepalette[ramp3[r]]; - CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 42*(6-r), 306, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0); + CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 42*(6-r), 306, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0); } else { - CL_NewParticle(pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*50, cl_particles_smoke_alphafade.value*75, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0); + CL_NewParticle(pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*50, cl_particles_smoke_alphafade.value*75, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0); } } else if (effectnameindex == EFFECT_TR_WIZSPIKE) @@ -1025,18 +1029,18 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o { dec = 6; color = particlepalette[52 + (rand()&7)]; - CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0); - CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0); + CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, true, 0); + CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, true, 0); } else if (gamemode == GAME_GOODVSBAD2) { dec = 6; - CL_NewParticle(pt_static, 0x00002E, 0x000030, tex_particle, 6, 0, 128, 384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0); + CL_NewParticle(pt_static, 0x00002E, 0x000030, tex_particle, 6, 0, 128, 384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0); } else { color = particlepalette[20 + (rand()&7)]; - CL_NewParticle(pt_static, color, color, tex_particle, 2, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0); + CL_NewParticle(pt_static, color, color, tex_particle, 2, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0); } } else if (effectnameindex == EFFECT_TR_KNIGHTSPIKE) @@ -1045,13 +1049,13 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o { dec = 6; color = particlepalette[230 + (rand()&7)]; - CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0); - CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0); + CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, true, 0); + CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, true, 0); } else { color = particlepalette[226 + (rand()&7)]; - CL_NewParticle(pt_static, color, color, tex_particle, 2, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0); + CL_NewParticle(pt_static, color, color, tex_particle, 2, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0); } } else if (effectnameindex == EFFECT_TR_VORESPIKE) @@ -1059,40 +1063,40 @@ void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t o if (cl_particles_quake.integer) { color = particlepalette[152 + (rand()&3)]; - CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 850, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 8, 0); + CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 850, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 8, 0, true, 0); } else if (gamemode == GAME_GOODVSBAD2) { dec = 6; - CL_NewParticle(pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, 0, 255, 384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0); + CL_NewParticle(pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, 0, 255, 384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0); } else if (gamemode == GAME_PRYDON) { dec = 6; - CL_NewParticle(pt_static, 0x103040, 0x204050, tex_particle, 6, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0); + CL_NewParticle(pt_static, 0x103040, 0x204050, tex_particle, 6, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0); } else - CL_NewParticle(pt_static, 0x502030, 0x502030, tex_particle, 3, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0); + CL_NewParticle(pt_static, 0x502030, 0x502030, tex_particle, 3, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0); } else if (effectnameindex == EFFECT_TR_NEHAHRASMOKE) { dec = 7; - CL_NewParticle(pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, 0, 64, 320, 0, 0, pos[0], pos[1], pos[2], 0, 0, lhrandom(4, 12), 0, 0, 0, 4); + CL_NewParticle(pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, 0, 64, 320, 0, 0, pos[0], pos[1], pos[2], 0, 0, lhrandom(4, 12), 0, 0, 0, 4, false, 0); } else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA) { dec = 4; - CL_NewParticle(pt_static, 0x283880, 0x283880, tex_particle, 4, 0, 255, 1024, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 16); + CL_NewParticle(pt_static, 0x283880, 0x283880, tex_particle, 4, 0, 255, 1024, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 16, true, 0); } else if (effectnameindex == EFFECT_TR_GLOWTRAIL) - CL_NewParticle(pt_alphastatic, particlepalette[palettecolor], particlepalette[palettecolor], tex_particle, 5, 0, 128, 320, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0); + CL_NewParticle(pt_alphastatic, particlepalette[palettecolor], particlepalette[palettecolor], tex_particle, 5, 0, 128, 320, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0); } if (bubbles) { if (effectnameindex == EFFECT_TR_ROCKET) - CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(64, 255), 256, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, 0.0625, 0.25, 0, 16); + CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(64, 255), 256, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, 0.0625, 0.25, 0, 16, true, 0); else if (effectnameindex == EFFECT_TR_GRENADE) - CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(64, 255), 256, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, 0.0625, 0.25, 0, 16); + CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(64, 255), 256, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, 0.0625, 0.25, 0, 16, true, 0); } // advance to next time and position dec *= qd; @@ -1170,7 +1174,7 @@ void CL_ParticleTrail(int effectnameindex, float pcount, const vec3_t originmins // glowing entity // called by CL_LinkNetworkEntity Matrix4x4_Scale(&tempmatrix, info->lightradiusstart, 1); - R_RTLight_Update(&r_refdef.lights[r_refdef.numlights++], false, &tempmatrix, info->lightcolor, -1, info->lightcubemapnum > 0 ? va("cubemaps/%i", info->lightcubemapnum) : NULL, info->lightshadow, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + R_RTLight_Update(&r_refdef.scene.lights[r_refdef.scene.numlights++], false, &tempmatrix, info->lightcolor, -1, info->lightcubemapnum > 0 ? va("cubemaps/%i", info->lightcubemapnum) : NULL, info->lightshadow, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); } } @@ -1187,7 +1191,7 @@ void CL_ParticleTrail(int effectnameindex, float pcount, const vec3_t originmins if (info->particletype == pt_decal) CL_SpawnDecalParticleForPoint(center, info->originjitter[0], lhrandom(info->size[0], info->size[1]), lhrandom(info->alpha[0], info->alpha[1]), tex, info->color[0], info->color[1]); else if (info->particletype == pt_beam) - CL_NewParticle(info->particletype, info->color[0], info->color[1], tex, lhrandom(info->size[0], info->size[1]), info->size[2], lhrandom(info->alpha[0], info->alpha[1]), info->alpha[2], 0, 0, originmins[0], originmins[1], originmins[2], originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, 0); + CL_NewParticle(info->particletype, info->color[0], info->color[1], tex, lhrandom(info->size[0], info->size[1]), info->size[2], lhrandom(info->alpha[0], info->alpha[1]), info->alpha[2], 0, 0, originmins[0], originmins[1], originmins[2], originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, 0, false, 0); else { if (!cl_particles.integer) @@ -1228,7 +1232,7 @@ void CL_ParticleTrail(int effectnameindex, float pcount, const vec3_t originmins trailpos[2] = lhrandom(originmins[2], originmaxs[2]); } VectorRandom(rvec); - CL_NewParticle(info->particletype, info->color[0], info->color[1], tex, lhrandom(info->size[0], info->size[1]), info->size[2], lhrandom(info->alpha[0], info->alpha[1]), info->alpha[2], info->gravity, info->bounce, trailpos[0] + info->originoffset[0] + info->originjitter[0] * rvec[0], trailpos[1] + info->originoffset[1] + info->originjitter[1] * rvec[1], trailpos[2] + info->originoffset[2] + info->originjitter[2] * rvec[2], lhrandom(velocitymins[0], velocitymaxs[0]) * info->velocitymultiplier + info->velocityoffset[0] + info->velocityjitter[0] * rvec[0], lhrandom(velocitymins[1], velocitymaxs[1]) * info->velocitymultiplier + info->velocityoffset[1] + info->velocityjitter[1] * rvec[1], lhrandom(velocitymins[2], velocitymaxs[2]) * info->velocitymultiplier + info->velocityoffset[2] + info->velocityjitter[2] * rvec[2], info->airfriction, info->liquidfriction, 0, 0); + CL_NewParticle(info->particletype, info->color[0], info->color[1], tex, lhrandom(info->size[0], info->size[1]), info->size[2], lhrandom(info->alpha[0], info->alpha[1]), info->alpha[2], info->gravity, info->bounce, trailpos[0] + info->originoffset[0] + info->originjitter[0] * rvec[0], trailpos[1] + info->originoffset[1] + info->originjitter[1] * rvec[1], trailpos[2] + info->originoffset[2] + info->originjitter[2] * rvec[2], lhrandom(velocitymins[0], velocitymaxs[0]) * info->velocitymultiplier + info->velocityoffset[0] + info->velocityjitter[0] * rvec[0], lhrandom(velocitymins[1], velocitymaxs[1]) * info->velocitymultiplier + info->velocityoffset[1] + info->velocityjitter[1] * rvec[1], lhrandom(velocitymins[2], velocitymaxs[2]) * info->velocitymultiplier + info->velocityoffset[2] + info->velocityjitter[2] * rvec[2], info->airfriction, info->liquidfriction, 0, 0, info->countabsolute <= 0, 0); if (trailstep) VectorMA(trailpos, trailstep, traildir, trailpos); } @@ -1256,6 +1260,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); @@ -1270,7 +1275,7 @@ void CL_EntityParticles (const entity_t *ent) v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength; v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength; v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength; - CL_NewParticle(pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 1, 0, 255, 0, 0, 0, v[0], v[1], v[2], 0, 0, 0, 0, 0, 0, 0); + CL_NewParticle(pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 1, 0, 255, 0, 0, 0, v[0], v[1], v[2], 0, 0, 0, 0, 0, 0, 0, true, 0); } } @@ -1322,16 +1327,16 @@ void CL_ReadPointFile_f (void) if (cl.num_particles < cl.max_particles - 3) { s++; - CL_NewParticle(pt_static, particlepalette[(-c)&15], particlepalette[(-c)&15], tex_particle, 2, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0, 0, 0, 0); + CL_NewParticle(pt_static, particlepalette[(-c)&15], particlepalette[(-c)&15], tex_particle, 2, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0, 0, 0, 0, true, 1<<30); } } Mem_Free(pointfile); VectorCopy(leakorg, org); Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]); - CL_NewParticle(pt_beam, 0xFF0000, 0xFF0000, tex_beam, 64, 0, 255, 0, 0, 0, org[0] - 4096, org[1], org[2], org[0] + 4096, org[1], org[2], 0, 0, 0, 0); - CL_NewParticle(pt_beam, 0x00FF00, 0x00FF00, tex_beam, 64, 0, 255, 0, 0, 0, org[0], org[1] - 4096, org[2], org[0], org[1] + 4096, org[2], 0, 0, 0, 0); - CL_NewParticle(pt_beam, 0x0000FF, 0x0000FF, tex_beam, 64, 0, 255, 0, 0, 0, org[0], org[1], org[2] - 4096, org[0], org[1], org[2] + 4096, 0, 0, 0, 0); + CL_NewParticle(pt_beam, 0xFF0000, 0xFF0000, tex_beam, 64, 0, 255, 0, 0, 0, org[0] - 4096, org[1], org[2], org[0] + 4096, org[1], org[2], 0, 0, 0, 0, false, 1<<30); + CL_NewParticle(pt_beam, 0x00FF00, 0x00FF00, tex_beam, 64, 0, 255, 0, 0, 0, org[0], org[1] - 4096, org[2], org[0], org[1] + 4096, org[2], 0, 0, 0, 0, false, 1<<30); + CL_NewParticle(pt_beam, 0x0000FF, 0x0000FF, tex_beam, 64, 0, 255, 0, 0, 0, org[0], org[1], org[2] - 4096, org[0], org[1], org[2] + 4096, 0, 0, 0, 0, false, 1<<30); } /* @@ -1385,12 +1390,12 @@ void CL_ParticleExplosion (const vec3_t org) if (i & 1) { color = particlepalette[ramp1[r]]; - CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 32 * (8 - r), 318, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256); + CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 32 * (8 - r), 318, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256, true, 0); } else { color = particlepalette[ramp2[r]]; - CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 32 * (8 - r), 478, 0, 0, org[0], org[1], org[2], 0, 0, 0, 1, 1, 16, 256); + CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 32 * (8 - r), 478, 0, 0, org[0], org[1], org[2], 0, 0, 0, 1, 1, 16, 256, true, 0); } } } @@ -1401,7 +1406,7 @@ void CL_ParticleExplosion (const vec3_t org) { if (cl_particles.integer && cl_particles_bubbles.integer) for (i = 0;i < 128 * cl_particles_quality.value;i++) - CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(128, 255), 128, -0.125, 1.5, org[0], org[1], org[2], 0, 0, 0, 0.0625, 0.25, 16, 96); + CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(128, 255), 128, -0.125, 1.5, org[0], org[1], org[2], 0, 0, 0, 0.0625, 0.25, 16, 96, true, 0); } else { @@ -1421,7 +1426,7 @@ void CL_ParticleExplosion (const vec3_t org) } VectorSubtract(trace.endpos, org, v2); VectorScale(v2, 2.0f, v2); - CL_NewParticle(pt_spark, 0x903010, 0xFFD030, tex_particle, 1.0f, 0, lhrandom(0, 255), 512, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0, 0, 0, 0); + CL_NewParticle(pt_spark, 0x903010, 0xFFD030, tex_particle, 1.0f, 0, lhrandom(0, 255), 512, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0, 0, 0, 0, true, 0); } } } @@ -1446,9 +1451,9 @@ void CL_ParticleExplosion2 (const vec3_t org, int colorStart, int colorLength) { k = particlepalette[colorStart + (i % colorLength)]; if (cl_particles_quake.integer) - CL_NewParticle(pt_static, k, k, tex_particle, 1, 0, 255, 850, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 8, 256); + CL_NewParticle(pt_static, k, k, tex_particle, 1, 0, 255, 850, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 8, 256, true, 0); else - CL_NewParticle(pt_static, k, k, tex_particle, lhrandom(0.5, 1.5), 0, 255, 512, 0, 0, org[0], org[1], org[2], 0, 0, 0, lhrandom(1.5, 3), lhrandom(1.5, 3), 8, 192); + CL_NewParticle(pt_static, k, k, tex_particle, lhrandom(0.5, 1.5), 0, 255, 512, 0, 0, org[0], org[1], org[2], 0, 0, 0, lhrandom(1.5, 3), lhrandom(1.5, 3), 8, 192, true, 0); } } @@ -1458,7 +1463,7 @@ static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const ve { sparkcount *= cl_particles_quality.value; while(sparkcount-- > 0) - CL_NewParticle(pt_spark, particlepalette[0x68], particlepalette[0x6f], tex_particle, 0.5f, 0, lhrandom(64, 255), 512, 1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]) + cl.movevars_gravity * 0.1f, 0, 0, 0, 64); + CL_NewParticle(pt_spark, particlepalette[0x68], particlepalette[0x6f], tex_particle, 0.5f, 0, lhrandom(64, 255), 512, 1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]) + cl.movevars_gravity * 0.1f, 0, 0, 0, 64, true, 0); } } @@ -1468,7 +1473,7 @@ static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec { smokecount *= cl_particles_quality.value; while(smokecount-- > 0) - CL_NewParticle(pt_smoke, 0x101010, 0x101010, tex_smoke[rand()&7], 2, 2, 255, 256, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 0, smokecount > 0 ? 16 : 0); + CL_NewParticle(pt_smoke, 0x101010, 0x101010, tex_smoke[rand()&7], 2, 2, 255, 256, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 0, smokecount > 0 ? 16 : 0, true, 0); } } @@ -1481,24 +1486,29 @@ void CL_ParticleCube (const vec3_t mins, const vec3_t maxs, const vec3_t dir, in while (count--) { k = particlepalette[colorbase + (rand()&3)]; - CL_NewParticle(pt_alphastatic, k, k, tex_particle, 2, 0, 255, 128, gravity, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0], dir[1], dir[2], 0, 0, 0, randomvel); + CL_NewParticle(pt_alphastatic, k, k, tex_particle, 2, 0, 255, 128, gravity, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0], dir[1], dir[2], 0, 0, 0, randomvel, true, 0); } } void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, int count, int colorbase, int type) { int k; - float z, minz, maxz; + float minz, maxz, lifetime = 30; if (!cl_particles.integer) return; if (dir[2] < 0) // falling - z = maxs[2]; + { + minz = maxs[2] + dir[2] * 0.1; + maxz = maxs[2]; + if (cl.worldmodel) + lifetime = (maxz - cl.worldmodel->normalmins[2]) / max(1, -dir[2]); + } else // rising?? - z = mins[2]; - - minz = z - fabs(dir[2]) * 0.1; - maxz = z + fabs(dir[2]) * 0.1; - minz = bound(mins[2], minz, maxs[2]); - maxz = bound(mins[2], maxz, maxs[2]); + { + minz = mins[2]; + maxz = maxs[2] + dir[2] * 0.1; + if (cl.worldmodel) + lifetime = (cl.worldmodel->normalmaxs[2] - minz) / max(1, dir[2]); + } count = (int)(count * cl_particles_quality.value); @@ -1512,9 +1522,9 @@ void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, in { k = particlepalette[colorbase + (rand()&3)]; if (gamemode == GAME_GOODVSBAD2) - CL_NewParticle(pt_rain, k, k, tex_particle, 20, 0, lhrandom(8, 16), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0); + CL_NewParticle(pt_rain, k, k, tex_particle, 20, 0, lhrandom(32, 64), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime); else - CL_NewParticle(pt_rain, k, k, tex_particle, 0.5, 0, lhrandom(8, 16), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0); + CL_NewParticle(pt_rain, k, k, tex_particle, 0.5, 0, lhrandom(32, 64), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime); } break; case 1: @@ -1523,9 +1533,9 @@ void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, in { k = particlepalette[colorbase + (rand()&3)]; if (gamemode == GAME_GOODVSBAD2) - CL_NewParticle(pt_snow, k, k, tex_particle, 20, 0, lhrandom(64, 128), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0); + CL_NewParticle(pt_snow, k, k, tex_particle, 20, 0, lhrandom(64, 128), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime); else - CL_NewParticle(pt_snow, k, k, tex_particle, 1, 0, lhrandom(64, 128), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0); + CL_NewParticle(pt_snow, k, k, tex_particle, 1, 0, lhrandom(64, 128), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime); } break; default: @@ -1533,295 +1543,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 @@ -1836,7 +1557,9 @@ static rtexture_t *particlefonttexture; static particletexture_t particletexture[MAX_PARTICLETEXTURES]; static cvar_t r_drawparticles = {0, "r_drawparticles", "1", "enables drawing of particles"}; +static cvar_t r_drawparticles_drawdistance = {CVAR_SAVE, "r_drawparticles_drawdistance", "2000", "particles further than drawdistance*size will not be drawn"}; static cvar_t r_drawdecals = {0, "r_drawdecals", "1", "enables drawing of decals"}; +static cvar_t r_drawdecals_drawdistance = {CVAR_SAVE, "r_drawdecals_drawdistance", "500", "decals further than drawdistance*size will not be drawn"}; #define PARTICLETEXTURESIZE 64 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8) @@ -2177,7 +1900,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) { @@ -2193,18 +1915,21 @@ void R_Particles_Init (void) } Cvar_RegisterVariable(&r_drawparticles); + Cvar_RegisterVariable(&r_drawparticles_drawdistance); Cvar_RegisterVariable(&r_drawdecals); + Cvar_RegisterVariable(&r_drawdecals_drawdistance); R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap); } 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 +1943,126 @@ 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; + // used as if (i & qualitymask) to skip some less important particles + // according to cl_minfps + int qualitymask = (1 << r_refdef.view.qualityreduction) - 1; + float drawdist2 = r_drawdecals_drawdistance.value * r_drawdecals_drawdistance.value; + + 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++; + // skip some of the less important decals according to cl_minfps + if (i & qualitymask) + continue; + + if (DotProduct(r_refdef.view.origin, decal->normal) > DotProduct(decal->org, decal->normal) && VectorDistance2(decal->org, r_refdef.view.origin) < drawdist2 * (decal->size * decal->size)) + 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; + } + + // reduce cl.num_decals if possible + while (cl.num_decals > 0 && cl.decals[cl.num_decals - 1].typeindex == 0) + cl.num_decals--; + + if (cl.num_decals == cl.max_decals && cl.max_decals < ABSOLUTE_MAX_DECALS) + { + decal_t *olddecals = cl.decals; + cl.max_decals = min(cl.max_decals * 2, ABSOLUTE_MAX_DECALS); + cl.decals = (decal_t *) Mem_Alloc(cls.levelmempool, cl.max_decals * sizeof(decal_t)); + memcpy(cl.decals, olddecals, cl.num_decals * sizeof(decal_t)); + Mem_Free(olddecals); } - if (batchcount > 0) - R_Mesh_Draw(batchstart * 4, batchcount * 4, batchcount * 2, particle_element3i + batchstart * 6, 0, 0); - GL_LockArrays(0, 0); } void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist) @@ -2340,6 +2073,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 +2096,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) - { - 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) + 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) { - 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) + { + 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 = 1 - fog; - cr += r_refdef.fogcolor[0] * fog; - cg += r_refdef.fogcolor[1] * fog; - cb += r_refdef.fogcolor[2] * fog; + 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 +2170,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.04, p->vel, v); + VectorMA(p->org, 0.04, 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 +2197,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 +2211,252 @@ 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, a, content; float minparticledist; particle_t *p; + float gravity, dvel, decalfade, frametime, f, dist, oldorg[3]; + float drawdist2 = r_drawparticles_drawdistance.value * r_drawparticles_drawdistance.value; + int hitent; + trace_t trace; + qboolean update; + // used as if (i & qualitymask) to skip some less important particles + // according to cl_minfps + int qualitymask = (1 << r_refdef.view.qualityreduction) - 1; + + 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 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) + { + if (cl.free_particle > i) + cl.free_particle = i; + 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_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; + + // skip some of the less important particles according to cl_minfps + if ((i & qualitymask) && p->qualityreduction) + 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 + switch(p->typeindex) + { + case pt_beam: + // beams have no culling R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL); + break; + default: + // anything else just has to be in front of the viewer and visible at this distance + if (DotProduct(p->org, r_refdef.view.forward) >= minparticledist && VectorDistance2(p->org, r_refdef.view.origin) < drawdist2 * (p->size * p->size)) + R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL); + break; + } + + 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--; + + if (cl.num_particles == cl.max_particles && cl.max_particles < ABSOLUTE_MAX_PARTICLES) + { + particle_t *oldparticles = cl.particles; + cl.max_particles = min(cl.max_particles * 2, ABSOLUTE_MAX_PARTICLES); + cl.particles = (particle_t *) Mem_Alloc(cls.levelmempool, cl.max_particles * sizeof(particle_t)); + memcpy(cl.particles, oldparticles, cl.num_particles * sizeof(particle_t)); + Mem_Free(oldparticles); + } }