From: havoc Date: Fri, 25 Jan 2008 08:48:57 +0000 (+0000) Subject: implemented framerate-dependent particle quality reduction to try to X-Git-Tag: xonotic-v0.1.0preview~2506 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=commitdiff_plain;h=3dbf0ca46d1e54ba7bc028217ae444f30f37b5af;hp=2573a080d1428ca682e70e1999ac52d4447f8e6e implemented framerate-dependent particle quality reduction to try to smooth out fps swings in Ruiner in Nexuiz added r_drawparticles_drawdistance (and one for decals) particles and decals arrays now grow dynamically and have smaller defaults than before fixed bug with rain particles that made them live for about 4 minutes if they fell out of the map git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8006 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/cl_main.c b/cl_main.c index c5f31ffc..336d2883 100644 --- a/cl_main.c +++ b/cl_main.c @@ -89,10 +89,6 @@ cvar_t cl_locs_show = {0, "locs_show", "0", "shows defined locations for editing client_static_t cls; client_state_t cl; -#define MAX_PARTICLES 32768 // default max # of particles at one time -#define MAX_DECALS 32768 // default max # of decals at one time -#define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line - /* ===================== CL_ClearState @@ -134,19 +130,10 @@ void CL_ClearState(void) cl.max_dlights = MAX_DLIGHTS; cl.max_lightstyle = MAX_LIGHTSTYLES; cl.max_brushmodel_entities = MAX_EDICTS; - cl.max_particles = MAX_PARTICLES; - cl.max_decals = MAX_DECALS; + cl.max_particles = 8192; // grows dynamically + cl.max_decals = 2048; // grows dynamically cl.max_showlmps = 0; -// COMMANDLINEOPTION: Client: -particles changes maximum number of particles at once, default 32768 - i = COM_CheckParm ("-particles"); - if (i && i < com_argc - 1) - { - cl.max_particles = (int)(atoi(com_argv[i+1])); - if (cl.max_particles < ABSOLUTE_MIN_PARTICLES) - cl.max_particles = ABSOLUTE_MIN_PARTICLES; - } - cl.num_dlights = 0; cl.num_effects = 0; cl.num_beams = 0; @@ -1766,6 +1753,7 @@ void CL_UpdateWorld(void) r_refdef.scene.numentities = 0; r_refdef.scene.numlights = 0; r_refdef.view.matrix = identitymatrix; + r_refdef.view.qualityreduction = 0; cl.num_brushmodel_entities = 0; @@ -1866,6 +1854,7 @@ static void CL_TimeRefresh_f (void) for (i = 0;i < 128;i++) { Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], 0, i / 128.0 * 360.0, 0, 1); + r_refdef.view.qualityreduction = 0; CL_UpdateScreen(); } timedelta = Sys_DoubleTime() - timestart; diff --git a/cl_particles.c b/cl_particles.c index 55f99a59..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)) @@ -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; @@ -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); } @@ -1271,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); } } @@ -1323,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); } /* @@ -1386,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); } } } @@ -1402,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 { @@ -1422,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); } } } @@ -1447,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); } } @@ -1459,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); } } @@ -1469,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); } } @@ -1482,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); @@ -1513,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: @@ -1524,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: @@ -1548,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) @@ -1904,7 +1915,9 @@ 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); } @@ -1988,6 +2001,10 @@ void R_DrawDecals (void) 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; @@ -2020,7 +2037,13 @@ void R_DrawDecals (void) else goto killdecal; } - R_MeshQueue_AddTransparent(decal->org, R_DrawDecal_TransparentCallback, NULL, i, NULL); + + // 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; @@ -2031,6 +2054,15 @@ killdecal: // 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); + } } void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist) @@ -2156,8 +2188,8 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh t2f[6] = tex->s2;t2f[7] = tex->t2; break; case PARTICLE_SPARK: - VectorMA(p->org, -0.02, p->vel, v); - VectorMA(p->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; @@ -2227,13 +2259,17 @@ void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtligh void R_DrawParticles (void) { - int i, j, a, content; + 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; @@ -2248,11 +2284,14 @@ void R_DrawParticles (void) decalfade = frametime * 255 / cl_decals_fadetime.value; update = frametime > 0; - 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 (update) { @@ -2304,29 +2343,7 @@ void R_DrawParticles (void) 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) + if (p->typeindex == pt_blood) { // blood - splash on solid if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS) @@ -2403,11 +2420,25 @@ void R_DrawParticles (void) 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 - if (DotProduct(p->org, r_refdef.view.forward) >= minparticledist || particletype[p->typeindex].orientation == PARTICLE_BEAM) + 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: @@ -2419,4 +2450,13 @@ killparticle: // 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); + } } diff --git a/cl_screen.c b/cl_screen.c index 1cacf769..0534d333 100644 --- a/cl_screen.c +++ b/cl_screen.c @@ -32,7 +32,7 @@ cvar_t scr_screenshot_gammaboost = {CVAR_SAVE, "scr_screenshot_gammaboost","1", cvar_t cl_capturevideo = {0, "cl_capturevideo", "0", "enables saving of video to a .avi file using uncompressed I420 colorspace and PCM audio, note that scr_screenshot_gammaboost affects the brightness of the output)"}; cvar_t cl_capturevideo_width = {0, "cl_capturevideo_width", "0", "scales all frames to this resolution before saving the video"}; cvar_t cl_capturevideo_height = {0, "cl_capturevideo_height", "0", "scales all frames to this resolution before saving the video"}; -cvar_t cl_capturevideo_realtime = {0, "cl_capturevideo_realtime", "0", "causes video saving to operate in realtime (mostly useful while playing, not while capturing demos), this can produce a much lower quality video due to poor sound/video sync and will abort saving if your machine stalls for over 1 second"}; +cvar_t cl_capturevideo_realtime = {0, "cl_capturevideo_realtime", "0", "causes video saving to operate in realtime (mostly useful while playing, not while capturing demos), this can produce a much lower quality video due to poor sound/video sync and will abort saving if your machine stalls for over a minute"}; cvar_t cl_capturevideo_fps = {0, "cl_capturevideo_fps", "30", "how many frames per second to save (29.97 for NTSC, 30 for typical PC video, 15 can be useful)"}; cvar_t cl_capturevideo_number = {CVAR_SAVE, "cl_capturevideo_number", "1", "number to append to video filename, incremented each time a capture begins"}; cvar_t r_letterbox = {0, "r_letterbox", "0", "reduces vertical height of view to simulate a letterboxed movie effect (can be used by mods for cutscenes)"}; @@ -629,7 +629,7 @@ void SCR_BeginLoadingPlaque (void) //============================================================================= -char r_speeds_timestring[1024]; +char r_speeds_timestring[4096]; int speedstringcount, r_timereport_active; double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0; int r_speeds_longestitem = 0; @@ -686,7 +686,7 @@ void R_TimeReport_EndFrame(void) { int i, j, lines, y; cl_locnode_t *loc; - char string[2048]; + char string[1024+4096]; string[0] = 0; if (r_speeds.integer && cls.signon == SIGNONS && cls.state == ca_connected) @@ -696,9 +696,9 @@ void R_TimeReport_EndFrame(void) loc = CL_Locs_FindNearest(cl.movement_origin); if (loc) sprintf(string + strlen(string), "Location: %s\n", loc->name); - sprintf(string + strlen(string), "org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n", r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], r_refdef.view.forward[0], r_refdef.view.forward[1], r_refdef.view.forward[2]); + sprintf(string + strlen(string), "%3i renders org:'%+8.2f %+8.2f %+8.2f' dir:'%+2.3f %+2.3f %+2.3f'\n", r_refdef.stats.renders, r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], r_refdef.view.forward[0], r_refdef.view.forward[1], r_refdef.view.forward[2]); sprintf(string + strlen(string), "%7i surfaces%7i triangles %5i entities (%7i surfaces%7i triangles)\n", r_refdef.stats.world_surfaces, r_refdef.stats.world_triangles, r_refdef.stats.entities, r_refdef.stats.entities_surfaces, r_refdef.stats.entities_triangles); - sprintf(string + strlen(string), "%5i leafs%5i portals%6i particles%6i decals\n", r_refdef.stats.world_leafs, r_refdef.stats.world_portals, r_refdef.stats.particles, r_refdef.stats.decals); + sprintf(string + strlen(string), "%5i leafs%5i portals%6i/%6i particles%6i/%6i decals %3i%% quality\n", r_refdef.stats.world_leafs, r_refdef.stats.world_portals, r_refdef.stats.particles, cl.num_particles, r_refdef.stats.decals, cl.num_decals, 100 / (1 << r_refdef.view.qualityreduction)); sprintf(string + strlen(string), "%7i lightmap updates (%7i pixels)\n", r_refdef.stats.lightmapupdates, r_refdef.stats.lightmapupdatepixels); sprintf(string + strlen(string), "%4i lights%4i clears%4i scissored%7i light%7i shadow%7i dynamic\n", r_refdef.stats.lights, r_refdef.stats.lights_clears, r_refdef.stats.lights_scissored, r_refdef.stats.lights_lighttriangles, r_refdef.stats.lights_shadowtriangles, r_refdef.stats.lights_dynamicshadowtriangles); if (r_refdef.stats.bloom) @@ -1631,7 +1631,7 @@ void SCR_CaptureVideo(void) else newframenum = cls.capturevideo.frame + 1; // if falling behind more than one second, stop - if (newframenum - cls.capturevideo.frame > (int)ceil(cls.capturevideo.framerate)) + if (newframenum - cls.capturevideo.frame > 60 * (int)ceil(cls.capturevideo.framerate)) { Cvar_SetValueQuick(&cl_capturevideo, 0); Con_Printf("video saving failed on frame %i, your machine is too slow for this capture speed.\n", cls.capturevideo.frame); @@ -1731,6 +1731,7 @@ static void R_Envmap_f (void) { sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name); Matrix4x4_CreateFromQuakeEntity(&r_refdef.view.matrix, r_refdef.view.origin[0], r_refdef.view.origin[1], r_refdef.view.origin[2], envmapinfo[j].angles[0], envmapinfo[j].angles[1], envmapinfo[j].angles[2], 1); + r_refdef.view.qualityreduction = 0; r_refdef.view.clear = true; R_Mesh_Start(); R_RenderView(); @@ -2092,8 +2093,15 @@ void SCR_UpdateLoadingScreen (qboolean clear) qglFinish(); } +extern cvar_t cl_minfps; +extern cvar_t cl_minfps_expbase; +extern cvar_t cl_minfps_fade; +extern cvar_t cl_minfps_maxqualityreduction; +static double cl_updatescreen_rendertime = 0; +static double cl_updatescreen_qualityreduction = 0; void CL_UpdateScreen(void) { + double rendertime1; float conwidth, conheight; if (vid_hidden || !scr_refresh.integer) @@ -2102,6 +2110,8 @@ void CL_UpdateScreen(void) if (!scr_initialized || !con_initialized) return; // not initialized yet + rendertime1 = Sys_DoubleTime(); + conwidth = bound(320, vid_conwidth.value, 2048); conheight = bound(200, vid_conheight.value, 1536); if (vid_conwidth.value != conwidth) @@ -2159,6 +2169,7 @@ void CL_UpdateScreen(void) qglClearColor(0,0,0,0);CHECKGLERROR R_ClearScreen(false); r_refdef.view.clear = false; + r_refdef.view.qualityreduction = (int)floor(cl_updatescreen_qualityreduction + 0.5); if(scr_stipple.integer) { @@ -2229,6 +2240,15 @@ void CL_UpdateScreen(void) SCR_CaptureVideo(); + // quality adjustment according to render time + qglFlush(); + cl_updatescreen_rendertime += ((Sys_DoubleTime() - rendertime1) - cl_updatescreen_rendertime) * bound(0.01, cl_minfps_fade.value, 1); + if (cl_minfps.value > 0 && !cls.timedemo && (!cls.capturevideo.active || !cls.capturevideo.realtime)) + cl_updatescreen_qualityreduction = invpow(cl_minfps_expbase.value, cl_minfps.value * cl_updatescreen_rendertime); + else + cl_updatescreen_qualityreduction = 0; + cl_updatescreen_qualityreduction = bound(0, cl_updatescreen_qualityreduction, bound(0, cl_minfps_maxqualityreduction.value, 30)); + VID_Finish(true); } diff --git a/client.h b/client.h index 6546773a..245d590e 100644 --- a/client.h +++ b/client.h @@ -646,7 +646,8 @@ typedef struct decal_s vec3_t normal; float size; float alpha; // 0-255 - unsigned char color[4]; + unsigned char color[3]; + unsigned char unused1; // fields not used by rendering: (36 bytes in 32bit, 40 bytes in 64bit) float time2; // used for decal fade @@ -654,7 +655,6 @@ typedef struct decal_s 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; @@ -667,7 +667,8 @@ typedef struct particle_s vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam float size; float alpha; // 0-255 - unsigned char color[4]; + unsigned char color[3]; + unsigned char qualityreduction; // enables skipping of this particle according to r_refdef.view.qualityreduction // fields not used by rendering: (40 bytes) float sizeincrease; // rate of size change per second @@ -1328,6 +1329,7 @@ float FogForDistance(vec_t dist); typedef struct r_refdef_stats_s { + int renders; int entities; int entities_surfaces; int entities_triangles; @@ -1407,6 +1409,12 @@ typedef struct r_refdef_view_s // these define which values to use in GL_CullFace calls to request frontface or backface culling int cullface_front; int cullface_back; + + // reduces render quality: + // 0 = full quality + // 1 = skip every other particle and some lights + // 2 = skip 75% of particles and some lights + int qualityreduction; } r_refdef_view_t; diff --git a/gl_rmain.c b/gl_rmain.c index 97224e13..c2f6ad2f 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -3478,6 +3478,8 @@ static void R_DrawLocs(void); static void R_DrawEntityBBoxes(void); void R_RenderScene(qboolean addwaterplanes) { + r_refdef.stats.renders++; + R_UpdateFogColor(); if (addwaterplanes) diff --git a/host.c b/host.c index 7862e440..fac4fa46 100644 --- a/host.c +++ b/host.c @@ -59,7 +59,10 @@ jmp_buf host_abortframe; cvar_t host_framerate = {0, "host_framerate","0", "locks frame timing to this value in seconds, 0.05 is 20fps for example, note that this can easily run too fast, use cl_maxfps if you want to limit your framerate instead, or sys_ticrate to limit server speed"}; // shows time used by certain subsystems cvar_t host_speeds = {0, "host_speeds","0", "reports how much time is used in server/graphics/sound"}; -// LordHavoc: framerate upper cap +cvar_t cl_minfps = {CVAR_SAVE, "cl_minfps", "40", "minimum fps target - while the rendering performance is below this, it will drift toward lower quality"}; +cvar_t cl_minfps_expbase = {CVAR_SAVE, "cl_minfps_expbase", "1.2", "base for log() function in calculating quality reduction, should be in the range 1.1 to 2.0"}; +cvar_t cl_minfps_fade = {CVAR_SAVE, "cl_minfps_fade", "0.2", "how fast the quality reduction adapts to varying framerate"}; +cvar_t cl_minfps_maxqualityreduction = {CVAR_SAVE, "cl_minfps_maxqualityreduction", "3", "how much particle quality can be reduced (as a power of 2) when framerate is staying below cl_minfps, 0 = no reduction, 1 = 50% quality, 2 = 25% quality, 3 = 12.5% quality, ..."}; cvar_t cl_maxfps = {CVAR_SAVE, "cl_maxfps", "1000000", "maximum fps cap, if game is running faster than this it will wait before running another frame (useful to make cpu time available to other programs)"}; cvar_t cl_maxidlefps = {CVAR_SAVE, "cl_maxidlefps", "20", "maximum fps cap when the game is not the active window (makes cpu time available to other programs"}; @@ -202,6 +205,10 @@ static void Host_InitLocal (void) Cvar_RegisterVariable (&host_framerate); Cvar_RegisterVariable (&host_speeds); + Cvar_RegisterVariable (&cl_minfps); + Cvar_RegisterVariable (&cl_minfps_expbase); + Cvar_RegisterVariable (&cl_minfps_fade); + Cvar_RegisterVariable (&cl_minfps_maxqualityreduction); Cvar_RegisterVariable (&cl_maxfps); Cvar_RegisterVariable (&cl_maxidlefps); diff --git a/menu.c b/menu.c index c7ca69c1..a48e89aa 100644 --- a/menu.c +++ b/menu.c @@ -5073,6 +5073,7 @@ void MP_Draw (void) // declarations that are needed right now extern r_refdef_scene_t menu_scene; + int oldqualityreduction; static r_refdef_scene_t clientscene; clientscene = r_refdef.scene; r_refdef.scene = menu_scene; @@ -5080,6 +5081,10 @@ void MP_Draw (void) // reset the temp entities each frame r_refdef.scene.numtempentities = 0; + // menu scenes do not use reduced rendering quality + oldqualityreduction = r_refdef.view.qualityreduction; + r_refdef.view.qualityreduction = 0; + PRVM_Begin; PRVM_SetProg(PRVM_MENUPROG); @@ -5089,6 +5094,8 @@ void MP_Draw (void) PRVM_End; + r_refdef.view.qualityreduction = oldqualityreduction; + menu_scene = r_refdef.scene; r_refdef.scene = clientscene; }