#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] =
{
+ {PBLEND_INVALID, PARTICLE_INVALID, false}, //pt_dead (should never happen)
{PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
{PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
{PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
{PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
{PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
{PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
- {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
+ {PBLEND_INVMOD, PARTICLE_BILLBOARD, false}, //pt_blood
{PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
- {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
+ {PBLEND_INVMOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
{PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
};
float trailspacing;
// type of particle to spawn (defines some aspects of behavior)
ptype_t particletype;
+ // blending mode used on this particle type
+ pblend_t blendmode;
+ // orientation of this particle type (BILLBOARD, SPARK, BEAM, etc)
+ porientation_t orientation;
// range of colors to choose from in hex RRGGBB (like HTML color tags),
// randomly interpolated at spawn
unsigned int color[2];
// these offsets are added to the values given to particleeffect(), and
// then an ellipsoid-shaped jitter is added as defined by these
// (they are the 3 radii)
+ float stretchfactor;
+ // stretch velocity factor (used for sparks)
float originoffset[3];
float velocityoffset[3];
float originjitter[3];
float lightcolor[3];
qboolean lightshadow;
int lightcubemapnum;
+ unsigned int staincolor[2]; // note: 0x808080 = neutral (particle's own color), these are modding factors for the particle's original color!
+ int staintex[2];
}
particleeffectinfo_t;
particleeffectinfo_t particleeffectinfo[MAX_PARTICLEEFFECTINFO];
-static int particlepalette[256] =
-{
+static int particlepalette[256];
+/*
0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b, // 0-7
0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb, // 8-15
0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b, // 16-23
0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b, // 232-239
0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000, // 240-247
0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53 // 248-255
-};
+*/
int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1", "multiplies particle size"};
cvar_t cl_particles_quake = {CVAR_SAVE, "cl_particles_quake", "0", "makes particle effects look mostly like the ones in Quake"};
cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1", "enables blood effects"};
-cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5", "opacity of blood"};
+cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "1", "opacity of blood"};
cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1", "make certain quake particle() calls create blood effects instead"};
cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1", "enables bulletimpact effects"};
-cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0", "enables smoke from explosions"};
cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1", "enables sparks from explosions"};
cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0", "enables polygonal shell from explosions"};
cvar_t cl_particles_rain = {CVAR_SAVE, "cl_particles_rain", "1", "enables rain effects"};
cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55", "brightness fade per second"};
cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1", "enables sparks (used by multiple effects)"};
cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1", "enables bubbles (used by multiple effects)"};
-cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0", "enables decals (bullet holes, blood, etc)"};
-cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0", "how long before decals start to fade away"};
-cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20", "how long decals take to fade away"};
+cvar_t cl_particles_visculling = {CVAR_SAVE, "cl_particles_visculling", "0", "perform a costly check if each particle is visible before drawing"};
+cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "1", "enables decals (bullet holes, blood, etc)"};
+cvar_t cl_decals_visculling = {CVAR_SAVE, "cl_decals_visculling", "1", "perform a very cheap check if each decal is visible before drawing"};
+cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "20", "how long before decals start to fade away"};
+cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "1", "how long decals take to fade away"};
void CL_Particles_ParseEffectInfo(const char *textstart, const char *textend)
argv[arrayindex][0] = 0;
for (;;)
{
- if (!COM_ParseToken_Simple(&text, true))
+ if (!COM_ParseToken_Simple(&text, true, false))
return;
if (!strcmp(com_token, "\n"))
break;
info = particleeffectinfo + effectinfoindex;
info->effectnameindex = effectnameindex;
info->particletype = pt_alphastatic;
+ info->blendmode = particletype[info->particletype].blendmode;
+ info->orientation = particletype[info->particletype].orientation;
info->tex[0] = tex_particle;
info->tex[1] = tex_particle;
info->color[0] = 0xFFFFFF;
VectorSet(info->lightcolor, 1, 1, 1);
info->lightshadow = true;
info->lighttime = 9999;
+ info->stretchfactor = 1;
+ info->staincolor[0] = -1;
+ info->staincolor[1] = -1;
+ info->staintex[0] = -1;
+ info->staintex[1] = -1;
}
else if (info == NULL)
{
else if (!strcmp(argv[1], "raindecal")) info->particletype = pt_raindecal;
else if (!strcmp(argv[1], "snow")) info->particletype = pt_snow;
else if (!strcmp(argv[1], "bubble")) info->particletype = pt_bubble;
- else if (!strcmp(argv[1], "blood")) info->particletype = pt_blood;
+ else if (!strcmp(argv[1], "blood")) {info->particletype = pt_blood;info->gravity = 1;}
else if (!strcmp(argv[1], "smoke")) info->particletype = pt_smoke;
else if (!strcmp(argv[1], "decal")) info->particletype = pt_decal;
else if (!strcmp(argv[1], "entityparticle")) info->particletype = pt_entityparticle;
else Con_Printf("effectinfo.txt:%i: unrecognized particle type %s\n", linenumber, argv[1]);
+ info->blendmode = particletype[info->particletype].blendmode;
+ info->orientation = particletype[info->particletype].orientation;
+ }
+ else if (!strcmp(argv[0], "blend"))
+ {
+ checkparms(2);
+ if (!strcmp(argv[1], "alpha")) info->blendmode = PBLEND_ALPHA;
+ else if (!strcmp(argv[1], "add")) info->blendmode = PBLEND_ADD;
+ else if (!strcmp(argv[1], "invmod")) info->blendmode = PBLEND_INVMOD;
+ else Con_Printf("effectinfo.txt:%i: unrecognized blendmode %s\n", linenumber, argv[1]);
+ }
+ else if (!strcmp(argv[0], "orientation"))
+ {
+ checkparms(2);
+ if (!strcmp(argv[1], "billboard")) info->orientation = PARTICLE_BILLBOARD;
+ else if (!strcmp(argv[1], "spark")) info->orientation = PARTICLE_SPARK;
+ else if (!strcmp(argv[1], "oriented")) info->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
+ else if (!strcmp(argv[1], "beam")) info->orientation = PARTICLE_BEAM;
+ else Con_Printf("effectinfo.txt:%i: unrecognized orientation %s\n", linenumber, argv[1]);
}
else if (!strcmp(argv[0], "color")) {readints(info->color, 2);}
else if (!strcmp(argv[0], "tex")) {readints(info->tex, 2);}
else if (!strcmp(argv[0], "size")) {readfloats(info->size, 2);}
else if (!strcmp(argv[0], "sizeincrease")) {readfloat(info->size[2]);}
else if (!strcmp(argv[0], "alpha")) {readfloats(info->alpha, 3);}
- else if (!strcmp(argv[0], "time")) {readints(info->time, 2);}
+ else if (!strcmp(argv[0], "time")) {readfloats(info->time, 2);}
else if (!strcmp(argv[0], "gravity")) {readfloat(info->gravity);}
else if (!strcmp(argv[0], "bounce")) {readfloat(info->bounce);}
else if (!strcmp(argv[0], "airfriction")) {readfloat(info->airfriction);}
else if (!strcmp(argv[0], "underwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_UNDERWATER;}
else if (!strcmp(argv[0], "notunderwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_NOTUNDERWATER;}
else if (!strcmp(argv[0], "trailspacing")) {readfloat(info->trailspacing);if (info->trailspacing > 0) info->countmultiplier = 1.0f / info->trailspacing;}
+ else if (!strcmp(argv[0], "stretchfactor")) {readfloat(info->stretchfactor);}
+ else if (!strcmp(argv[0], "staincolor")) {readints(info->staincolor, 2);}
+ else if (!strcmp(argv[0], "staintex")) {readints(info->staintex, 2);}
+ else if (!strcmp(argv[0], "stainless")) {info->staintex[0] = -2; info->staincolor[0] = -1; info->staincolor[1] = -1;}
else
Con_Printf("effectinfo.txt:%i: skipping unknown command %s\n", linenumber, argv[0]);
#undef checkparms
CL_Particles_ParseEffectInfo((const char *)filedata, (const char *)filedata + filesize);
Mem_Free(filedata);
}
-};
+}
/*
===============
Cvar_RegisterVariable (&cl_particles_blood);
Cvar_RegisterVariable (&cl_particles_blood_alpha);
Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
- Cvar_RegisterVariable (&cl_particles_explosions_smoke);
Cvar_RegisterVariable (&cl_particles_explosions_sparks);
Cvar_RegisterVariable (&cl_particles_explosions_shell);
Cvar_RegisterVariable (&cl_particles_bulletimpacts);
Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
Cvar_RegisterVariable (&cl_particles_sparks);
Cvar_RegisterVariable (&cl_particles_bubbles);
+ Cvar_RegisterVariable (&cl_particles_visculling);
Cvar_RegisterVariable (&cl_decals);
+ Cvar_RegisterVariable (&cl_decals_visculling);
Cvar_RegisterVariable (&cl_decals_time);
Cvar_RegisterVariable (&cl_decals_fadetime);
}
// 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 *particle(particletype_t *ptype, 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)
+// blendmode - one of the PBLEND_ values
+// orientation - one of the PARTICLE_ values
+// staincolor1, staincolor2: minimum and maximum ranges of stain color, randomly interpolated to decide particle color (-1 to use none)
+// staintex: any of the tex_ values such as tex_smoke[rand()&7] or tex_particle (-1 to use none)
+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, float stretch, pblend_t blendmode, porientation_t orientation, int staincolor1, int staincolor2, int staintex)
{
- int l1, l2;
+ int l1, l2, r, g, b;
particle_t *part;
vec3_t v;
- for (;cl.free_particle < cl.max_particles && cl.particles[cl.free_particle].type;cl.free_particle++);
+ if (!cl_particles.integer)
+ return NULL;
+ 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;
memset(part, 0, sizeof(*part));
- part->type = ptype;
+ part->typeindex = ptypeindex;
+ part->blendmode = blendmode;
+ part->orientation = orientation;
l2 = (int)lhrandom(0.5, 256.5);
l1 = 256 - l2;
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->staintexnum = staintex;
+ if(staincolor1 >= 0 && staincolor2 >= 0)
+ {
+ l2 = (int)lhrandom(0.5, 256.5);
+ l1 = 256 - l2;
+ if(blendmode == PBLEND_INVMOD)
+ {
+ r = ((((staincolor1 >> 16) & 0xFF) * l1 + ((staincolor2 >> 16) & 0xFF) * l2) * (255 - part->color[0])) / 0x8000; // staincolor 0x808080 keeps color invariant
+ g = ((((staincolor1 >> 8) & 0xFF) * l1 + ((staincolor2 >> 8) & 0xFF) * l2) * (255 - part->color[1])) / 0x8000;
+ b = ((((staincolor1 >> 0) & 0xFF) * l1 + ((staincolor2 >> 0) & 0xFF) * l2) * (255 - part->color[2])) / 0x8000;
+ }
+ else
+ {
+ r = ((((staincolor1 >> 16) & 0xFF) * l1 + ((staincolor2 >> 16) & 0xFF) * l2) * part->color[0]) / 0x8000; // staincolor 0x808080 keeps color invariant
+ g = ((((staincolor1 >> 8) & 0xFF) * l1 + ((staincolor2 >> 8) & 0xFF) * l2) * part->color[1]) / 0x8000;
+ b = ((((staincolor1 >> 0) & 0xFF) * l1 + ((staincolor2 >> 0) & 0xFF) * l2) * part->color[2]) / 0x8000;
+ }
+ if(r > 0xFF) r = 0xFF;
+ if(g > 0xFF) g = 0xFF;
+ if(b > 0xFF) b = 0xFF;
+ }
+ else
+ {
+ r = part->color[0]; // -1 is shorthand for stain = particle color
+ g = part->color[1];
+ b = part->color[2];
+ }
+ part->staincolor = r * 65536 + g * 256 + b;
part->texnum = ptex;
part->size = psize;
part->sizeincrease = psizeincrease;
part->alphafade = palphafade;
part->gravity = pgravity;
part->bounce = pbounce;
+ part->stretch = stretch;
VectorRandom(v);
part->org[0] = px + originjitter * v[0];
part->org[1] = py + originjitter * v[1];
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 it is rain or snow, trace ahead and shut off collisions until an actual collision event needs to occur to improve performance
- if (part->type == particletype + pt_rain)
+ if (part->typeindex == pt_rain)
{
int i;
particle_t *part2;
vec3_t endvec;
trace_t trace;
// turn raindrop into simple spark and create delayedspawn splash effect
- part->type = particletype + pt_spark;
+ part->typeindex = pt_spark;
part->bounce = 0;
VectorMA(part->org, lifetime, part->vel, endvec);
- trace = CL_Move(part->org, vec3_origin, vec3_origin, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | ((part->type == particletype + pt_rain || part->type == particletype + pt_snow) ? SUPERCONTENTS_LIQUIDSMASK : 0), true, false, NULL, false);
+ 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 = particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, -1, -1, -1);
if (part2)
{
part2->delayedspawn = part->die;
part2->die += part->die - cl.time;
for (i = rand() & 7;i < 10;i++)
{
- part2 = particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
if (part2)
{
part2->delayedspawn = part->die;
}
}
}
- else if (part->bounce != 0 && part->gravity == 0)
+ else if (part->bounce != 0 && part->gravity == 0 && part->typeindex != pt_snow)
{
float lifetime = part->alpha / (part->alphafade ? part->alphafade : 1);
vec3_t endvec;
trace_t trace;
VectorMA(part->org, lifetime, part->vel, endvec);
- trace = CL_Move(part->org, vec3_origin, vec3_origin, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | ((part->type == particletype + pt_rain || part->type == particletype + pt_snow) ? SUPERCONTENTS_LIQUIDSMASK : 0), true, false, NULL, false);
- part->delayedcollisions = cl.time + lifetime * trace.fraction;
+ trace = CL_Move(part->org, vec3_origin, vec3_origin, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY, true, false, NULL, false);
+ part->delayedcollisions = cl.time + lifetime * trace.fraction - 0.1;
}
return part;
}
void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
{
- particle_t *p;
+ int l1, l2;
+ decal_t *decal;
if (!cl_decals.integer)
return;
- p = particle(particletype + pt_decal, color1, color2, texnum, size, 0, alpha, 0, 0, 0, org[0] + normal[0], org[1] + normal[1], org[2] + normal[2], normal[0], normal[1], normal[2], 0, 0, 0, 0);
- if (p)
+ for (;cl.free_decal < cl.max_decals && cl.decals[cl.free_decal].typeindex;cl.free_decal++);
+ if (cl.free_decal >= cl.max_decals)
+ return;
+ decal = &cl.decals[cl.free_decal++];
+ if (cl.num_decals < cl.free_decal)
+ cl.num_decals = cl.free_decal;
+ memset(decal, 0, sizeof(*decal));
+ decal->typeindex = pt_decal;
+ decal->texnum = texnum;
+ VectorAdd(org, normal, decal->org);
+ VectorCopy(normal, decal->normal);
+ decal->size = size;
+ decal->alpha = alpha;
+ decal->time2 = cl.time;
+ l2 = (int)lhrandom(0.5, 256.5);
+ l1 = 256 - l2;
+ 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->owner = hitent;
+ decal->clusterindex = -1000; // no vis culling unless we're sure
+ if (hitent)
{
- p->time2 = cl.time;
- p->owner = hitent;
- p->ownermodel = cl.entities[p->owner].render.model;
- VectorAdd(org, normal, p->org);
- VectorCopy(normal, p->vel);
- // these relative things are only used to regenerate p->org and p->vel if p->owner is not world (0)
- Matrix4x4_Transform(&cl.entities[p->owner].render.inversematrix, org, p->relativeorigin);
- Matrix4x4_Transform3x3(&cl.entities[p->owner].render.inversematrix, normal, p->relativedirection);
+ // these relative things are only used to regenerate p->org and p->vel if decal->owner is not world (0)
+ decal->ownermodel = cl.entities[decal->owner].render.model;
+ Matrix4x4_Transform(&cl.entities[decal->owner].render.inversematrix, org, decal->relativeorigin);
+ Matrix4x4_Transform3x3(&cl.entities[decal->owner].render.inversematrix, normal, decal->relativenormal);
+ }
+ else
+ {
+ if(r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.PointInLeaf)
+ {
+ mleaf_t *leaf = r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, decal->org);
+ if(leaf)
+ decal->clusterindex = leaf->clusterindex;
+ }
}
}
if (count == 1024)
CL_ParticleExplosion(center);
else if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer && (palettecolor == 73 || palettecolor == 225))
- CL_ParticleEffect(EFFECT_TE_BLOOD, count / 6.0f, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
+ CL_ParticleEffect(EFFECT_TE_BLOOD, count / 2.0f, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
else
{
count *= cl_particles_quality.value;
for (;count > 0;count--)
{
- int k = particlepalette[palettecolor + (rand()&7)];
- if (cl_particles_quake.integer)
- particle(particletype + 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);
- else if (gamemode == GAME_GOODVSBAD2)
- particle(particletype + 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);
- else
- particle(particletype + 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);
+ int k = particlepalette[(palettecolor & ~7) + (rand()&7)];
+ CL_NewParticle(pt_alphastatic, k, k, tex_particle, 1.5, 0, 255, 0, 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, lhrandom(0.1, 0.5), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
}
{
CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
+ CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
// bullet hole
- if (cl_stainmaps.integer) R_Stain(center, 32, 96, 96, 96, 24, 128, 128, 128, 24);
+ R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
}
else if (effectnameindex == EFFECT_TE_SPIKEQUAD)
{
CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
+ CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
// bullet hole
- if (cl_stainmaps.integer) R_Stain(center, 32, 96, 96, 96, 24, 128, 128, 128, 24);
+ R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
}
{
CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
+ CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
// bullet hole
- if (cl_stainmaps.integer) R_Stain(center, 32, 96, 96, 96, 24, 128, 128, 128, 24);
+ R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
}
else if (effectnameindex == EFFECT_TE_SUPERSPIKEQUAD)
{
CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
+ CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
// bullet hole
- if (cl_stainmaps.integer) R_Stain(center, 32, 96, 96, 96, 24, 128, 128, 128, 24);
+ R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
}
if (!cl_particles_blood.integer)
return;
if (cl_particles_quake.integer)
- CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 73);
+ CL_ParticleEffect(EFFECT_SVC_PARTICLE, 2*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 73);
else
{
static double bloodaccumulator = 0;
+ //CL_NewParticle(pt_alphastatic, 0x4f0000,0x7f0000, tex_particle, 2.5, 0, 256, 256, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 1, 4, 0, 0, true, 0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD);
bloodaccumulator += count * 0.333 * cl_particles_quality.value;
for (;bloodaccumulator > 0;bloodaccumulator--)
- particle(particletype + 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, 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, 64, true, 0, 1, PBLEND_INVMOD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
else if (effectnameindex == EFFECT_TE_SPARK)
else if (effectnameindex == EFFECT_TE_PLASMABURN)
{
// plasma scorch mark
- if (cl_stainmaps.integer) R_Stain(center, 48, 96, 96, 96, 32, 128, 128, 128, 32);
+ R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
CL_SpawnDecalParticleForPoint(center, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
CL_AllocLightFlash(NULL, &tempmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
}
{
CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
+ CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
// bullet hole
- if (cl_stainmaps.integer) R_Stain(center, 32, 96, 96, 96, 24, 128, 128, 128, 24);
+ R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
}
else if (effectnameindex == EFFECT_TE_GUNSHOTQUAD)
{
CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
+ CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
// bullet hole
- if (cl_stainmaps.integer) R_Stain(center, 32, 96, 96, 96, 24, 128, 128, 128, 24);
+ R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
}
for (i = 0;i < 1024 * cl_particles_quality.value;i++)
{
if (i & 1)
- particle(particletype + 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_alphastatic, particlepalette[66], particlepalette[71], tex_particle, 1.5f, 0, 255, 0, 0, 0, center[0], center[1], center[2], 0, 0, 0, -4, -4, 16, 256, true, (rand() & 1) ? 1.4 : 1.0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
else
- particle(particletype + 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_alphastatic, particlepalette[150], particlepalette[155], tex_particle, 1.5f, 0, 255, 0, 0, 0, center[0], center[1], center[2], 0, 0, lhrandom(-256, 256), 0, 0, 16, 0, true, (rand() & 1) ? 1.4 : 1.0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
else
{
count *= cl_particles_quality.value;
while (count-- > 0)
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
else if (effectnameindex == EFFECT_TE_LAVASPLASH)
{
org[1] = center[1] + dir[1];
org[2] = center[2] + lhrandom(0, 64);
vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
- particle(particletype + 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, 255, 0, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, true, lhrandom(2, 2.62), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
}
float i, j, k, inc, vel;
vec3_t dir;
- inc = 8 / cl_particles_quality.value;
+ if (cl_particles_quake.integer)
+ inc = 4 / cl_particles_quality.value;
+ else
+ inc = 8 / cl_particles_quality.value;
for (i = -16;i < 16;i += inc)
{
for (j = -16;j < 16;j += inc)
VectorSet(dir, i*8, j*8, k*8);
VectorNormalize(dir);
vel = lhrandom(50, 113);
- particle(particletype + 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);
+ if (cl_particles_quake.integer)
+ CL_NewParticle(pt_alphastatic, particlepalette[7], particlepalette[14], tex_particle, 1.5f, 0, 255, 0, 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, lhrandom(0.2, 0.34), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
+ else
+ 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
}
- particle(particletype + 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);
+ if (!cl_particles_quake.integer)
+ CL_NewParticle(pt_static, 0xffffff, 0xffffff, tex_particle, 30, 0, 256, 512, 0, 0, center[0], center[1], center[2], 0, 0, 0, 0, 0, 0, 0, false, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
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)
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BEAM, -1, -1, -1);
else if (effectnameindex == EFFECT_TE_TEI_SMOKE)
{
if (cl_particles_smoke.integer)
{
count *= 0.25f * cl_particles_quality.value;
while (count-- > 0)
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
else if (effectnameindex == EFFECT_TE_TEI_BIGEXPLOSION)
else if (effectnameindex == EFFECT_TE_TEI_PLASMAHIT)
{
float f;
- if (cl_stainmaps.integer)
- R_Stain(center, 40, 96, 96, 96, 40, 128, 128, 128, 40);
+ R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
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)
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
if (cl_particles_sparks.integer)
for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
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)
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
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)
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
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))
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);
{
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.templights[r_refdef.scene.numlights], false, &tempmatrix, light, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
+ r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights++];
}
}
if (cl_particles_quake.integer)
{
color = particlepalette[67 + (rand()&3)];
- particle(particletype + 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, 0, 0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 2, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
else
{
dec = 16;
- particle(particletype + 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, 1, -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, 1, PBLEND_INVMOD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
else if (effectnameindex == EFFECT_TR_SLIGHTBLOOD)
{
dec = 6;
color = particlepalette[67 + (rand()&3)];
- particle(particletype + 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, 0, 0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 2, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
else
{
dec = 32;
- particle(particletype + 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, 1, -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, 1, PBLEND_INVMOD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
}
{
r = rand()&3;
color = particlepalette[ramp3[r]];
- particle(particletype + 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, 255, 0, -0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0.1372549*(6-r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
else
{
- particle(particletype + 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);
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
+ 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
else if (effectnameindex == EFFECT_TR_GRENADE)
{
r = 2 + (rand()%5);
color = particlepalette[ramp3[r]];
- particle(particletype + 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, 255, 0, -0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0.1372549*(6-r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
else
{
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
else if (effectnameindex == EFFECT_TR_WIZSPIKE)
{
dec = 6;
color = particlepalette[52 + (rand()&7)];
- particle(particletype + 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);
- particle(particletype + 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, 0, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
+ CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
else if (gamemode == GAME_GOODVSBAD2)
{
dec = 6;
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
else
{
color = particlepalette[20 + (rand()&7)];
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
else if (effectnameindex == EFFECT_TR_KNIGHTSPIKE)
{
dec = 6;
color = particlepalette[230 + (rand()&7)];
- particle(particletype + 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);
- particle(particletype + 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, 0, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
+ CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
else
{
color = particlepalette[226 + (rand()&7)];
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
else if (effectnameindex == EFFECT_TR_VORESPIKE)
if (cl_particles_quake.integer)
{
color = particlepalette[152 + (rand()&3)];
- particle(particletype + 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, 0, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 8, 0, true, 0.3, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
else if (gamemode == GAME_GOODVSBAD2)
{
dec = 6;
- particle(particletype + 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
else if (gamemode == GAME_PRYDON)
{
dec = 6;
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
else
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
else if (effectnameindex == EFFECT_TR_NEHAHRASMOKE)
{
dec = 7;
- particle(particletype + 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
{
dec = 4;
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
else if (effectnameindex == EFFECT_TR_GLOWTRAIL)
- particle(particletype + 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
if (bubbles)
{
if (effectnameindex == EFFECT_TR_ROCKET)
- particle(particletype + 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(128, 512), 512, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, 0.0625, 0.25, 0, 16, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
else if (effectnameindex == EFFECT_TR_GRENADE)
- particle(particletype + 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(128, 512), 512, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, 0.0625, 0.25, 0, 16, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
// advance to next time and position
dec *= qd;
{
vec3_t center;
qboolean found = false;
- if (effectnameindex < 1 || effectnameindex >= MAX_PARTICLEEFFECTNAME)
- return; // invalid effect index
- if (!particleeffectname[effectnameindex][0])
+ if (effectnameindex < 1 || effectnameindex >= MAX_PARTICLEEFFECTNAME || !particleeffectname[effectnameindex][0])
+ {
+ Con_DPrintf("Unknown effect number %i received from server\n", effectnameindex);
return; // no such effect
+ }
VectorLerp(originmins, 0.5, originmaxs, center);
if (!cl_particles_quake.integer && particleeffectinfo[0].effectnameindex)
{
int effectinfoindex;
int supercontents;
- int tex;
+ int tex, staintex;
particleeffectinfo_t *info;
vec3_t center;
vec3_t centervelocity;
// 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.templights[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);
+ r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights++];
}
}
tex = (int)lhrandom(info->tex[0], info->tex[1]);
tex = min(tex, info->tex[1] - 1);
}
+ if(info->staintex[0] < 0)
+ staintex = info->staintex[0];
+ else
+ {
+ staintex = (int)lhrandom(info->staintex[0], info->staintex[1]);
+ staintex = min(staintex, info->staintex[1] - 1);
+ }
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)
- particle(particletype + 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);
+ else if (info->orientation == PARTICLE_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, false, lhrandom(info->time[0], info->time[1]), info->stretchfactor, info->blendmode, info->orientation, info->staincolor[0], info->staincolor[1], staintex);
else
{
if (!cl_particles.integer)
trailpos[2] = lhrandom(originmins[2], originmaxs[2]);
}
VectorRandom(rvec);
- particle(particletype + 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, lhrandom(info->time[0], info->time[1]), info->stretchfactor, info->blendmode, info->orientation, info->staincolor[0], info->staincolor[1], staintex);
if (trailstep)
VectorMA(trailpos, trailstep, traildir, trailpos);
}
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);
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;
- particle(particletype + 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
t++;
tchar = *t;
*t = 0;
+#if _MSC_VER >= 1400
+#define sscanf sscanf_s
+#endif
r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
*t = tchar;
pointfilepos = t;
if (cl.num_particles < cl.max_particles - 3)
{
s++;
- particle(particletype + 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_alphastatic, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
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]);
- particle(particletype + 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);
- particle(particletype + 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);
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BEAM, -1, -1, -1);
+ 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, 1, PBLEND_ADD, PARTICLE_BEAM, -1, -1, -1);
+ 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, 1, PBLEND_ADD, PARTICLE_BEAM, -1, -1, -1);
}
/*
MSG_ReadVector(org, cls.protocol);
for (i=0 ; i<3 ; i++)
- dir[i] = MSG_ReadChar ();
+ dir[i] = MSG_ReadChar () * (1.0 / 16.0);
msgcount = MSG_ReadByte ();
color = MSG_ReadByte ();
trace_t trace;
//vec3_t v;
//vec3_t v2;
- if (cl_stainmaps.integer)
- R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
+ R_Stain(org, 96, 40, 40, 40, 64, 88, 88, 88, 64);
CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
if (cl_particles_quake.integer)
if (i & 1)
{
color = particlepalette[ramp1[r]];
- particle(particletype + 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, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256, true, 0.1006 * (8 - r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
else
{
color = particlepalette[ramp2[r]];
- particle(particletype + 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, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 1, 1, 16, 256, true, 0.0669 * (8 - r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
}
{
if (cl_particles.integer && cl_particles_bubbles.integer)
for (i = 0;i < 128 * cl_particles_quality.value;i++)
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
else
{
- // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
- // smoke puff
- if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
- {
- for (i = 0;i < 32;i++)
- {
- int k;
- vec3_t v, v2;
- for (k = 0;k < 16;k++)
- {
- v[0] = org[0] + lhrandom(-48, 48);
- v[1] = org[1] + lhrandom(-48, 48);
- v[2] = org[2] + lhrandom(-48, 48);
- trace = CL_Move(org, vec3_origin, vec3_origin, v, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
- if (trace.fraction >= 0.1)
- break;
- }
- VectorSubtract(trace.endpos, org, v2);
- VectorScale(v2, 2.0f, v2);
- particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 12, 0, 32, 64, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0, 0, 0, 0);
- }
- }
-
if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
{
for (i = 0;i < 512 * cl_particles_quality.value;i++)
}
VectorSubtract(trace.endpos, org, v2);
VectorScale(v2, 2.0f, v2);
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
}
}
}
{
k = particlepalette[colorStart + (i % colorLength)];
if (cl_particles_quake.integer)
- particle(particletype + 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_alphastatic, k, k, tex_particle, 1, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256, true, 0.3, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
else
- particle(particletype + 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_alphastatic, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
{
sparkcount *= cl_particles_quality.value;
while(sparkcount-- > 0)
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
}
}
{
smokecount *= cl_particles_quality.value;
while(smokecount-- > 0)
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
while (count--)
{
k = particlepalette[colorbase + (rand()&3)];
- particle(particletype + 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
}
}
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;
- particle_t *p;
+ 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);
{
k = particlepalette[colorbase + (rand()&3)];
if (gamemode == GAME_GOODVSBAD2)
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
else
- particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
}
break;
case 1:
{
k = particlepalette[colorbase + (rand()&3)];
if (gamemode == GAME_GOODVSBAD2)
- p = particle(particletype + 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
else
- p = particle(particletype + 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);
- if (p)
- VectorCopy(p->vel, p->relativedirection);
+ 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
}
break;
default:
}
}
-/*
-===============
-CL_MoveParticles
-===============
-*/
-void CL_MoveParticles (void)
-{
- particle_t *p;
- int i, maxparticle, j, a, content;
- float gravity, dvel, decalfade, frametime, f, dist, org[3], oldorg[3];
- particletype_t *decaltype, *bloodtype;
- 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;
- decaltype = particletype + pt_decal;
- bloodtype = particletype + pt_blood;
-
- maxparticle = -1;
- j = 0;
- for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++)
- {
- if (!p->type)
- {
- if (cl.free_particle > i)
- cl.free_particle = i;
- continue;
- }
- maxparticle = i;
-
- // heavily optimized decal case
- if (p->type == decaltype)
- {
- // FIXME: this has fairly wacky handling of alpha
- if (cl.time > p->time2 + cl_decals_time.value)
- {
- p->alpha -= decalfade;
- if (p->alpha <= 0)
- {
- p->type = NULL;
- if (cl.free_particle > i)
- cl.free_particle = i;
- continue;
- }
- }
- if (p->owner)
- {
- if (cl.entities[p->owner].render.model == p->ownermodel)
- {
- Matrix4x4_Transform(&cl.entities[p->owner].render.matrix, p->relativeorigin, p->org);
- Matrix4x4_Transform3x3(&cl.entities[p->owner].render.matrix, p->relativedirection, p->vel);
- }
- else
- {
- p->type = NULL;
- 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->type = NULL;
- if (cl.free_particle > i)
- cl.free_particle = i;
- continue;
- }
-
- if (p->type->orientation != PARTICLE_BEAM)
- {
- VectorCopy(p->org, oldorg);
- VectorMA(p->org, frametime, p->vel, p->org);
- VectorCopy(p->org, 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->type == particletype + pt_rain ? 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))
- {
- p->type = NULL;
- continue;
- }
- // react if the particle hit something
- if (trace.fraction < 1)
- {
- VectorCopy(trace.endpos, p->org);
- if (p->type == particletype + 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->type = particletype + 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--)
- particle(particletype + 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);
- }
- else if (p->type == bloodtype)
- {
- // blood - splash on solid
- if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS)
- {
- p->type = NULL;
- 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->type = NULL;
- continue;
- }
- // convert from a blood particle to a blood decal
- VectorCopy(trace.plane.normal, p->vel);
- VectorAdd(p->org, p->vel, p->org);
-
- p->type = particletype + pt_decal;
- p->texnum = tex_blooddecal[rand()&7];
- p->owner = hitent;
- p->ownermodel = cl.entities[hitent].render.model;
- // these relative things are only used to regenerate p->org and p->vel if p->owner is not world (0)
- Matrix4x4_Transform(&cl.entities[hitent].render.inversematrix, p->org, p->relativeorigin);
- Matrix4x4_Transform3x3(&cl.entities[hitent].render.inversematrix, p->vel, p->relativedirection);
- p->time2 = cl.time;
- p->alphafade = 0;
- p->bounce = 0;
- p->airfriction = 0;
- p->liquidfriction = 0;
- p->gravity = 0;
- p->size *= 2.0f;
- }
- else if (p->bounce < 0)
- {
- // bounce -1 means remove on impact
- p->type = NULL;
- 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);
- }
- }
- }
- p->vel[2] -= p->gravity * gravity;
-
- if (p->liquidfriction && CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
- {
- f = 1.0f - min(p->liquidfriction * frametime, 1);
- VectorScale(p->vel, f, p->vel);
- }
- else if (p->airfriction)
- {
- f = 1.0f - min(p->airfriction * frametime, 1);
- VectorScale(p->vel, f, p->vel);
- }
- }
-
- if (p->type != particletype + pt_static)
- {
- switch (p->type - particletype)
- {
- case pt_entityparticle:
- // particle that removes itself after one rendered frame
- if (p->time2)
- p->type = NULL;
- else
- p->time2 = 1;
- break;
- case pt_blood:
- a = CL_PointSuperContents(p->org);
- if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
- {
- p->size += frametime * 8;
- //p->alpha -= bloodwaterfade;
- }
- else
- p->vel[2] -= gravity;
- if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
- p->type = NULL;
- break;
- case pt_bubble:
- a = CL_PointSuperContents(p->org);
- if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
- {
- p->type = NULL;
- break;
- }
- break;
- case pt_rain:
- a = CL_PointSuperContents(p->org);
- if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
- p->type = NULL;
- break;
- case pt_snow:
- if (cl.time > p->time2)
- {
- // snow flutter
- p->time2 = cl.time + (rand() & 3) * 0.1;
- p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
- p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
- //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
- }
- a = CL_PointSuperContents(p->org);
- if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
- p->type = NULL;
- break;
- default:
- break;
- }
- }
- }
- cl.num_particles = maxparticle + 1;
-}
-
#define MAX_PARTICLETEXTURES 64
// particletexture_t is a rectangle in the particlefonttexture
typedef struct particletexture_s
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)
f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
if (f > 0)
{
+ if (f > 1)
+ f = 1;
d = data + (y * PARTICLETEXTURESIZE + x) * 4;
- d[0] += (int)(f * (red - d[0]));
+ d[0] += (int)(f * (blue - d[0]));
d[1] += (int)(f * (green - d[1]));
- d[2] += (int)(f * (blue - d[2]));
+ d[2] += (int)(f * (red - d[2]));
}
}
}
int i;
for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
{
- data[0] = bound(minr, data[0], maxr);
+ data[0] = bound(minb, data[0], maxb);
data[1] = bound(ming, data[1], maxg);
- data[2] = bound(minb, data[2], maxb);
+ data[2] = bound(minr, data[2], maxr);
}
}
m = 8;
for (j = 1;j < 10;j++)
for (k = min(j, m - 1);k < m;k++)
- particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
+ particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 320 - j * 8);
//particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
particletextureinvert(&data[0][0][0]);
setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
// we invert it again during the blendfunc to make it work...
#ifndef DUMPPARTICLEFONT
- particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
+ particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", false, TEXF_ALPHA | TEXF_PRECACHE | TEXF_FORCELINEAR, true);
if (!particlefonttexture)
#endif
{
}
#ifdef DUMPPARTICLEFONT
- Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
+ Image_WriteTGABGRA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
#endif
- particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
+ particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE | TEXF_FORCELINEAR, NULL);
Mem_Free(particletexturedata);
}
}
#ifndef DUMPPARTICLEFONT
- particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
+ particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", false, TEXF_ALPHA | TEXF_PRECACHE | TEXF_FORCELINEAR, true);
if (!particletexture[tex_beam].texture)
#endif
{
}
#ifdef DUMPPARTICLEFONT
- Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
+ Image_WriteTGABGRA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
#endif
- particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
+ particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE | TEXF_FORCELINEAR, NULL);
}
particletexture[tex_beam].s1 = 0;
particletexture[tex_beam].t1 = 0;
static void r_part_start(void)
{
+ int i;
+ // generate particlepalette for convenience from the main one
+ for (i = 0;i < 256;i++)
+ particlepalette[i] = palette_rgb[i][0] * 65536 + palette_rgb[i][1] * 256 + palette_rgb[i][2];
particletexturepool = R_AllocTexturePool();
R_InitParticleTexture ();
CL_Particles_LoadEffectInfo();
static void r_part_newmap(void)
{
+ CL_Particles_LoadEffectInfo();
}
#define BATCHSIZE 256
-int particle_element3i[BATCHSIZE*6];
-float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16];
+unsigned short particle_elements[BATCHSIZE*6];
void R_Particles_Init (void)
{
int i;
for (i = 0;i < BATCHSIZE;i++)
{
- particle_element3i[i*6+0] = i*4+0;
- particle_element3i[i*6+1] = i*4+1;
- particle_element3i[i*6+2] = i*4+2;
- particle_element3i[i*6+3] = i*4+0;
- particle_element3i[i*6+4] = i*4+2;
- particle_element3i[i*6+5] = i*4+3;
+ particle_elements[i*6+0] = i*4+0;
+ particle_elements[i*6+1] = i*4+1;
+ particle_elements[i*6+2] = i*4+2;
+ particle_elements[i*6+3] = i*4+0;
+ particle_elements[i*6+4] = i*4+2;
+ particle_elements[i*6+5] = i*4+3;
}
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;
+ const decal_t *d;
+ 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);
+ R_Mesh_ResetTextureState();
+ R_Mesh_VertexPointer(particle_vertex3f, 0, 0);
+ R_Mesh_TexCoordPointer(0, 2, particle_texcoord2f, 0, 0);
+ R_Mesh_ColorPointer(particle_color4f, 0, 0);
+ R_SetupGenericShader(true);
+ GL_DepthMask(false);
+ GL_DepthRange(0, 1);
+ GL_PolygonOffset(0, 0);
+ GL_DepthTest(true);
+ GL_CullFace(GL_NONE);
+
+ // generate all the vertices at once
+ for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
+ {
+ d = cl.decals + surfacelist[surfacelistindex];
+
+ // calculate color
+ c4f = particle_color4f + 16*surfacelistindex;
+ ca = d->alpha * alphascale;
+ if (r_refdef.fogenabled)
+ 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;
+ VectorVectors(d->normal, right, up);
+ VectorScale(right, size, right);
+ VectorScale(up, size, up);
+ 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 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);
+ R_Mesh_Draw(0, numsurfaces * 4, 0, numsurfaces * 2, NULL, particle_elements, 0, 0);
+ GL_LockArrays(0, 0);
+}
+
+void R_DrawDecals (void)
+{
+ int i;
+ decal_t *decal;
+ float frametime;
+ float decalfade;
+ float drawdist2;
+
+ frametime = bound(0, cl.time - cl.decals_updatetime, 1);
+ cl.decals_updatetime = bound(cl.time - 1, cl.decals_updatetime + frametime, cl.time + 1);
+
+ // LordHavoc: early out conditions
+ if ((!cl.num_decals) || (!r_drawdecals.integer))
+ return;
+
+ decalfade = frametime * 256 / cl_decals_fadetime.value;
+ drawdist2 = r_drawdecals_drawdistance.value * r_refdef.view.quality;
+ drawdist2 = drawdist2*drawdist2;
+
+ for (i = 0, decal = cl.decals;i < cl.num_decals;i++, decal++)
+ {
+ if (!decal->typeindex)
+ continue;
+
+ if (cl.time > decal->time2 + cl_decals_time.value)
+ {
+ decal->alpha -= decalfade;
+ if (decal->alpha <= 0)
+ goto killdecal;
+ }
+
+ 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
+ goto killdecal;
+ }
+
+ if(cl_decals_visculling.integer && decal->clusterindex > -1000 && !CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, decal->clusterindex))
+ 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);
+ }
+}
+
void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
{
int surfacelistindex;
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, len, lenfactor;
+ 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);
R_Mesh_ResetTextureState();
R_Mesh_VertexPointer(particle_vertex3f, 0, 0);
R_Mesh_TexCoordPointer(0, 2, particle_texcoord2f, 0, 0);
R_Mesh_ColorPointer(particle_color4f, 0, 0);
+ R_SetupGenericShader(true);
GL_DepthMask(false);
GL_DepthRange(0, 1);
+ GL_PolygonOffset(0, 0);
GL_DepthTest(true);
- GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
+ 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)
{
- 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 = p->type->blendmode;
+ blendmode = p->blendmode;
- cr = p->color[0] * (1.0f / 255.0f) * r_view.colorscale;
- cg = p->color[1] * (1.0f / 255.0f) * r_view.colorscale;
- cb = p->color[2] * (1.0f / 255.0f) * r_view.colorscale;
- ca = p->alpha * (1.0f / 255.0f);
- if (blendmode == PBLEND_MOD)
+ 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)
{
- 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 (p->type->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)
- {
- fog = FogPoint_World(p->org);
- cr = cr * fog;
- cg = cg * fog;
- cb = cb * fog;
- if (blendmode == PBLEND_ALPHA)
+ case PBLEND_INVALID:
+ case PBLEND_INVMOD:
+ case PBLEND_ADD:
+ // additive and modulate can just fade out in fog (this is correct)
+ if (r_refdef.fogenabled)
+ c4f[3] *= FogPoint_World(p->org);
+ // collapse alpha into color for these blends (so that the particlefont does not need alpha on most textures)
+ c4f[0] *= c4f[3];
+ c4f[1] *= c4f[3];
+ c4f[2] *= c4f[3];
+ c4f[3] = 1;
+ break;
+ case PBLEND_ALPHA:
+ // note: lighting is not cheap!
+ if (particletype[p->typeindex].lighting)
{
- fog = 1 - fog;
- cr += r_refdef.fogcolor[0] * fog * r_view.colorscale;
- cg += r_refdef.fogcolor[1] * fog * r_view.colorscale;
- cb += r_refdef.fogcolor[2] * fog * r_view.colorscale;
+ R_CompleteLightPoint(ambient, diffuse, diffusenormal, p->org, true);
+ c4f[0] *= (ambient[0] + 0.5 * diffuse[0]);
+ c4f[1] *= (ambient[1] + 0.5 * diffuse[1]);
+ c4f[2] *= (ambient[2] + 0.5 * diffuse[2]);
}
+ // mix in the fog color
+ if (r_refdef.fogenabled)
+ {
+ fog = FogPoint_World(p->org);
+ ifog = 1 - fog;
+ c4f[0] = c4f[0] * fog + r_refdef.fogcolor[0] * ifog;
+ c4f[1] = c4f[1] * fog + r_refdef.fogcolor[1] * ifog;
+ c4f[2] = c4f[2] * fog + r_refdef.fogcolor[2] * ifog;
+ }
+ break;
}
- c4f[0] = c4f[4] = c4f[8] = c4f[12] = cr;
- c4f[1] = c4f[5] = c4f[9] = c4f[13] = cg;
- c4f[2] = c4f[6] = c4f[10] = c4f[14] = cb;
- c4f[3] = c4f[7] = c4f[11] = c4f[15] = ca;
+ // copy the color into the other three vertices
+ Vector4Copy(c4f, c4f + 4);
+ Vector4Copy(c4f, c4f + 8);
+ Vector4Copy(c4f, c4f + 12);
size = p->size * cl_particles_size.value;
- org = p->org;
tex = &particletexture[p->texnum];
- if (p->type->orientation == PARTICLE_BILLBOARD)
+ switch(p->orientation)
{
- VectorScale(r_view.left, -size, right);
- VectorScale(r_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];
+ case PARTICLE_INVALID:
+ case PARTICLE_BILLBOARD:
+ VectorScale(r_refdef.view.left, -size * p->stretch, right);
+ VectorScale(r_refdef.view.up, size, up);
+ 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;
- }
- else if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
- {
- // double-sided
- if (DotProduct(p->vel, r_view.origin) > DotProduct(p->vel, org))
- {
- VectorNegate(p->vel, v);
- VectorVectors(v, right, up);
- }
- else
- VectorVectors(p->vel, right, up);
- VectorScale(right, size, right);
+ break;
+ case PARTICLE_ORIENTED_DOUBLESIDED:
+ VectorVectors(p->vel, right, up);
+ VectorScale(right, size * p->stretch, 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;
- }
- else if (p->type->orientation == PARTICLE_SPARK)
- {
- VectorMA(org, -0.02, p->vel, v);
- VectorMA(org, 0.02, p->vel, up2);
+ break;
+ case PARTICLE_SPARK:
+ len = VectorLength(p->vel);
+ VectorNormalize2(p->vel, up);
+ lenfactor = p->stretch * 0.04 * len;
+ if(lenfactor < size * 0.5)
+ lenfactor = size * 0.5;
+ VectorMA(p->org, -lenfactor, up, v);
+ VectorMA(p->org, lenfactor, up, up2);
R_CalcBeam_Vertex3f(v3f, v, up2, size);
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;
- }
- else if (p->type->orientation == PARTICLE_BEAM)
- {
- R_CalcBeam_Vertex3f(v3f, org, p->vel, size);
- VectorSubtract(p->vel, org, up);
+ break;
+ case PARTICLE_BEAM:
+ 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[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
+ v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) * p->stretch;
+ v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f) * p->stretch;
t2f[0] = 1;t2f[1] = v[0];
t2f[2] = 0;t2f[3] = v[0];
t2f[4] = 0;t2f[5] = v[1];
t2f[6] = 1;t2f[7] = v[1];
- }
- else
- {
- Con_Printf("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
- return;
+ break;
}
}
// 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 = PBLEND_INVALID;
+ 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 != p->type->blendmode)
+ if (blendmode != p->blendmode)
{
- if (batchcount > 0)
- R_Mesh_Draw(batchstart * 4, batchcount * 4, batchcount * 2, particle_element3i + batchstart * 6, 0, 0);
- batchcount = 0;
- batchstart = surfacelistindex;
- blendmode = p->type->blendmode;
- if (blendmode == PBLEND_ALPHA)
+ blendmode = p->blendmode;
+ switch(blendmode)
+ {
+ case PBLEND_ALPHA:
GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- else if (blendmode == PBLEND_ADD)
+ break;
+ case PBLEND_INVALID:
+ case PBLEND_ADD:
GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
- else //if (blendmode == PBLEND_MOD)
+ break;
+ case PBLEND_INVMOD:
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++;
+ // iterate until we find a change in settings
+ batchstart = surfacelistindex++;
+ for (;surfacelistindex < numsurfaces;surfacelistindex++)
+ {
+ p = cl.particles + surfacelist[surfacelistindex];
+ if (blendmode != p->blendmode || texture != particletexture[p->texnum].texture)
+ break;
+ }
+
+ batchcount = surfacelistindex - batchstart;
+ R_Mesh_Draw(batchstart * 4, batchcount * 4, batchstart * 2, batchcount * 2, NULL, particle_elements, 0, 0);
}
- if (batchcount > 0)
- 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;
+ int hitent;
+ trace_t trace;
+ qboolean update;
+
+ frametime = bound(0, cl.time - cl.particles_updatetime, 1);
+ cl.particles_updatetime = bound(cl.time - 1, cl.particles_updatetime + frametime, cl.time + 1);
// LordHavoc: early out conditions
if ((!cl.num_particles) || (!r_drawparticles.integer))
return;
- minparticledist = DotProduct(r_view.origin, r_view.forward) + 4.0f;
+ 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;
+ drawdist2 = r_drawparticles_drawdistance.value * r_refdef.view.quality;
+ drawdist2 = drawdist2*drawdist2;
- // LordHavoc: only render if not too close
for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++)
{
- if (p->type && !p->delayedspawn)
+ 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 (p->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->staintexnum >= 0)
+ {
+ // blood - splash on solid
+ if (!(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
+ {
+ R_Stain(p->org, 16,
+ (p->staincolor >> 16) & 0xFF, (p->staincolor >> 8) & 0xFF, p->staincolor & 0xFF, (int)(p->alpha * p->size * (1.0f / 80.0f)),
+ (p->staincolor >> 16) & 0xFF, (p->staincolor >> 8) & 0xFF, p->staincolor & 0xFF, (int)(p->alpha * p->size * (1.0f / 80.0f)));
+ if (cl_decals.integer)
+ {
+ // create a decal for the blood splat
+ CL_SpawnDecalParticleForSurface(hitent, p->org, trace.plane.normal, 0xFFFFFF ^ p->staincolor, 0xFFFFFF ^ p->staincolor, p->staintexnum, p->size * 2, p->alpha); // staincolor needs to be inverted for decals!
+ }
+ }
+ }
+
+ if (p->typeindex == pt_blood)
+ {
+ // blood - splash on solid
+ if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS)
+ goto killparticle;
+ if(p->staintexnum == -1) // staintex < -1 means no stains at all
+ {
+ R_Stain(p->org, 16, 64, 16, 16, (int)(p->alpha * p->size * (1.0f / 80.0f)), 64, 32, 32, (int)(p->alpha * p->size * (1.0f / 80.0f)));
+ if (cl_decals.integer)
+ {
+ // create a decal for the blood splat
+ CL_SpawnDecalParticleForSurface(hitent, p->org, trace.plane.normal, p->color[0] * 65536 + p->color[1] * 256 + p->color[2], p->color[0] * 65536 + p->color[1] * 256 + p->color[2], tex_blooddecal[rand()&7], p->size * 2, p->alpha);
+ }
+ }
+ goto killparticle;
+ }
+ else if (p->bounce < 0)
+ {
+ // bounce -1 means remove on impact
+ goto killparticle;
+ }
+ else
+ {
+ // anything else - bounce off solid
+ dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
+ VectorMA(p->vel, dist, trace.plane.normal, p->vel);
+ if (DotProduct(p->vel, p->vel) < 0.03)
+ VectorClear(p->vel);
+ }
+ }
+ }
+ }
+
+ if (p->typeindex != pt_static)
+ {
+ switch (p->typeindex)
+ {
+ case pt_entityparticle:
+ // particle that removes itself after one rendered frame
+ if (p->time2)
+ goto killparticle;
+ else
+ p->time2 = 1;
+ break;
+ case pt_blood:
+ a = CL_PointSuperContents(p->org);
+ if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
+ goto killparticle;
+ break;
+ case pt_bubble:
+ a = CL_PointSuperContents(p->org);
+ if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
+ goto killparticle;
+ break;
+ case pt_rain:
+ a = CL_PointSuperContents(p->org);
+ if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
+ goto killparticle;
+ break;
+ case pt_snow:
+ if (cl.time > p->time2)
+ {
+ // snow flutter
+ p->time2 = cl.time + (rand() & 3) * 0.1;
+ p->vel[0] = p->vel[0] * 0.9f + lhrandom(-32, 32);
+ p->vel[1] = p->vel[0] * 0.9f + lhrandom(-32, 32);
+ }
+ a = CL_PointSuperContents(p->org);
+ if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
+ goto killparticle;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else if (p->delayedspawn)
+ continue;
+
+ // don't render particles too close to the view (they chew fillrate)
+ // also don't render particles behind the view (useless)
+ // further checks to cull to the frustum would be too slow here
+ switch(p->typeindex)
{
- r_refdef.stats.particles++;
- if (DotProduct(p->org, r_view.forward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
+ case pt_beam:
+ // beams have no culling
+ R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL);
+ break;
+ default:
+ if(cl_particles_visculling.integer)
+ if (!r_refdef.viewcache.world_novis)
+ if(r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.PointInLeaf)
+ {
+ mleaf_t *leaf = r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, p->org);
+ if(leaf)
+ if(!CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, leaf->clusterindex))
+ continue;
+ }
+ // 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);
+ }
+}