2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #define MAX_PARTICLES 4096 // default max # of particles at one time
24 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
26 // LordHavoc: added dust, smoke, snow, bloodcloud, and many others
29 pt_static, pt_grav, pt_blob, pt_blob2, pt_smoke, pt_snow, pt_rain, pt_bloodcloud, pt_fallfadespark, pt_bubble, pt_fade, pt_smokecloud, pt_splash, pt_flame, pt_flamingdebris, pt_smokingdebris, pt_flamefall
33 typedef struct particle_s
42 short dynlight; // if set the particle will be dynamically lit (if r_dynamicparticles is on), used for smoke and blood
44 float time2; // used for various things (snow fluttering, for example)
46 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
50 float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);
52 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
53 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
54 int ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
57 int smokeparticletexture[8];
58 int rainparticletexture;
59 int bubbleparticletexture;
61 int explosiontexturefog;
63 particle_t *particles;
66 vec3_t r_pright, r_pup, r_ppn;
69 particle_t **freeparticles; // list used only in compacting particles array
71 // LordHavoc: reduced duplicate code, and allow particle allocation system independence
72 #define ALLOCPARTICLE(part) \
73 if (numparticles >= r_numparticles)\
75 (part) = &particles[numparticles++];
77 cvar_t r_particles = {"r_particles", "1"};
78 cvar_t r_drawparticles = {"r_drawparticles", "1"};
79 cvar_t r_dynamicparticles = {"r_dynamicparticles", "0", TRUE};
81 byte shadebubble(float dx, float dy, vec3_t light)
85 if ((dx*dx+dy*dy) < 1) // it does hit the sphere
87 dz = 1 - (dx*dx+dy*dy);
90 normal[0] = dx;normal[1] = dy;normal[2] = dz;
91 VectorNormalize(normal);
92 dot = DotProduct(normal, light);
93 if (dot > 0.5) // interior reflection
95 else if (dot < -0.5) // exterior reflection
96 f += ((dot * -2) - 1);
98 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
99 VectorNormalize(normal);
100 dot = DotProduct(normal, light);
101 if (dot > 0.5) // interior reflection
102 f += ((dot * 2) - 1);
103 else if (dot < -0.5) // exterior reflection
104 f += ((dot * -2) - 1);
106 f += 16; // just to give it a haze so you can see the outline
107 f = bound(0, f, 255);
114 void R_InitParticleTexture (void)
118 byte data[32][32][4], noise1[128][128], noise2[128][128];
121 for (y = 0;y < 32;y++)
124 for (x = 0;x < 32;x++)
126 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
128 d = (255 - (dx*dx+dy*dy));
130 data[y][x][3] = (byte) d;
133 particletexture = GL_LoadTexture ("particletexture", 32, 32, &data[0][0][0], true, true, 4);
135 for (i = 0;i < 8;i++)
139 fractalnoise(&noise1[0][0], 128, 8);
140 fractalnoise(&noise2[0][0], 128, 16);
142 for (y = 0;y < 32;y++)
145 for (x = 0;x < 32;x++)
148 j = (noise1[y][x] - 128) * 2 + 128;
150 if (j > 255) j = 255;
151 data[y][x][0] = data[y][x][1] = data[y][x][2] = j;
153 d = (noise2[y][x] - 128) * 4 + 128;
156 d = (d * (255 - (int) (dx*dx+dy*dy))) >> 8;
157 //j = (sqrt(dx*dx+dy*dy) * 2.0f - 16.0f);
159 // d = (d * (255 - j*j)) >> 8;
161 if (d > 255) d = 255;
162 data[y][x][3] = (byte) d;
171 smokeparticletexture[i] = GL_LoadTexture (va("smokeparticletexture%d", i), 32, 32, &data[0][0][0], true, true, 4);
174 light[0] = 1;light[1] = 1;light[2] = 1;
175 VectorNormalize(light);
176 for (x=0 ; x<32 ; x++)
178 for (y=0 ; y<32 ; y++)
180 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
181 data[y][x][3] = shadebubble((x - 16) * (1.0 / 8.0), y < 24 ? (y - 24) * (1.0 / 24.0) : (y - 24) * (1.0 / 8.0), light);
184 rainparticletexture = GL_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], true, true, 4);
186 light[0] = 1;light[1] = 1;light[2] = 1;
187 VectorNormalize(light);
188 for (x=0 ; x<32 ; x++)
190 for (y=0 ; y<32 ; y++)
192 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
193 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
196 bubbleparticletexture = GL_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], true, true, 4);
201 particles = (particle_t *) qmalloc(r_numparticles * sizeof(particle_t));
202 freeparticles = (void *) qmalloc(r_numparticles * sizeof(particle_t *));
204 R_InitParticleTexture ();
207 void r_part_shutdown()
211 qfree(freeparticles);
219 void R_ReadPointFile_f (void);
220 void R_Particles_Init (void)
224 i = COM_CheckParm ("-particles");
228 r_numparticles = (int)(atoi(com_argv[i+1]));
229 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
230 r_numparticles = ABSOLUTE_MIN_PARTICLES;
234 r_numparticles = MAX_PARTICLES;
237 Cmd_AddCommand ("pointfile", R_ReadPointFile_f);
239 Cvar_RegisterVariable (&r_particles);
240 Cvar_RegisterVariable (&r_drawparticles);
241 Cvar_RegisterVariable (&r_dynamicparticles);
243 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown);
246 #define particle(ptype, pcolor, ptex, plight, pscale, palpha, ptime, px, py, pz, pvx, pvy, pvz)\
250 part->type = (ptype);\
251 part->color = (pcolor);\
252 part->texnum = (ptex);\
253 part->dynlight = (plight);\
254 part->scale = (pscale);\
255 part->alpha = (palpha);\
256 part->die = cl.time + (ptime);\
257 part->org[0] = (px);\
258 part->org[1] = (py);\
259 part->org[2] = (pz);\
260 part->vel[0] = (pvx);\
261 part->vel[1] = (pvy);\
262 part->vel[2] = (pvz);\
264 #define particle2(ptype, pcolor, ptex, plight, pscale, palpha, ptime, pbase, poscale, pvscale)\
268 part->type = (ptype);\
269 part->color = (pcolor);\
270 part->texnum = (ptex);\
271 part->dynlight = (plight);\
272 part->scale = (pscale);\
273 part->alpha = (palpha);\
274 part->die = cl.time + (ptime);\
275 part->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
276 part->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
277 part->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
278 part->vel[0] = lhrandom(-(pvscale), (pvscale));\
279 part->vel[1] = lhrandom(-(pvscale), (pvscale));\
280 part->vel[2] = lhrandom(-(pvscale), (pvscale));\
282 #define particle3(ptype, pcolor, ptex, plight, pscale, palpha, ptime, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
286 part->type = (ptype);\
287 part->color = (pcolor);\
288 part->texnum = (ptex);\
289 part->dynlight = (plight);\
290 part->scale = (pscale);\
291 part->alpha = (palpha);\
292 part->die = cl.time + (ptime);\
293 part->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
294 part->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
295 part->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
296 part->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
297 part->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
298 part->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
307 #define NUMVERTEXNORMALS 162
308 extern float r_avertexnormals[NUMVERTEXNORMALS][3];
309 vec3_t avelocities[NUMVERTEXNORMALS];
310 float beamlength = 16;
311 vec3_t avelocity = {23, 7, 3};
312 float partstep = 0.01;
313 float timescale = 0.01;
315 void R_EntityParticles (entity_t *ent)
320 float sp, sy, cp, cy;
323 if (!r_particles.value) return; // LordHavoc: particles are optional
328 if (!avelocities[0][0])
329 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
330 avelocities[0][i] = (rand()&255) * 0.01;
332 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
334 angle = cl.time * avelocities[i][0];
337 angle = cl.time * avelocities[i][1];
345 particle(pt_static, 0x6f, particletexture, false, 2, 255, 0, ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0);
355 void R_ClearParticles (void)
358 // free_particles = &particles[0];
359 // active_particles = NULL;
361 // for (i=0 ;i<r_numparticles ; i++)
362 // particles[i].next = &particles[i+1];
363 // particles[r_numparticles-1].next = NULL;
369 void R_ReadPointFile_f (void)
375 char name[MAX_OSPATH];
377 sprintf (name,"maps/%s.pts", sv.name);
379 COM_FOpenFile (name, &f, false);
382 Con_Printf ("couldn't open %s\n", name);
386 Con_Printf ("Reading %s...\n", name);
390 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
395 if (numparticles >= r_numparticles)
397 Con_Printf ("Not enough free particles\n");
400 particle(pt_static, (-c)&15, particletexture, false, 2, 255, 99999, org[0], org[1], org[2], 0, 0, 0);
404 Con_Printf ("%i points read\n", c);
409 R_ParseParticleEffect
411 Parse an effect out of the server message
414 void R_ParseParticleEffect (void)
417 int i, count, msgcount, color;
419 for (i=0 ; i<3 ; i++)
420 org[i] = MSG_ReadCoord ();
421 for (i=0 ; i<3 ; i++)
422 dir[i] = MSG_ReadChar () * (1.0/16);
423 msgcount = MSG_ReadByte ();
424 color = MSG_ReadByte ();
431 R_RunParticleEffect (org, dir, color, count);
440 void R_ParticleExplosion (vec3_t org, int smoke)
443 if (!r_particles.value) return; // LordHavoc: particles are optional
445 // particle(pt_smoke, (rand()&7) + 8, smokeparticletexture[rand()&7], true, 30, 255, 2, org[0], org[1], org[2], 0, 0, 0);
447 i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
448 if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
450 for (i=0 ; i<128 ; i++)
451 particle2(pt_bubble, (rand()&3) + 12, bubbleparticletexture, false, lhrandom(1, 2), 255, 2, org, 16, 96);
460 float f, forg[3], fvel[3], fvel2[3];
461 // for (i = 0;i < 256;i++)
462 // particle(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1.5, lhrandom(128, 255), 5, lhrandom(-16, 16) + org[0], lhrandom(-16, 16) + org[1], lhrandom(-16, 16) + org[2], lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192) + 192);
463 // for (i = 0;i < 256;i++)
464 // particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1.5, lhrandom(128, 255), 5, org, 15, 150);
465 for (i = 0;i < 32;i++)
467 fvel[0] = lhrandom(-150, 150);
468 fvel[1] = lhrandom(-150, 150);
469 fvel[2] = lhrandom(-150, 150) + 80;
470 // particle(pt_flamefall, 106 + (rand()%6), particletexture, false, 3, 255, 5, forg[0] + lhrandom(-5, 5), forg[1] + lhrandom(-5, 5), forg[2] + lhrandom(-5, 5), fvel2[0], fvel2[1], fvel2[2]);
471 for (j = 0;j < 64;j++)
473 forg[0] = lhrandom(-20, 20) + org[0];
474 forg[1] = lhrandom(-20, 20) + org[1];
475 forg[2] = lhrandom(-20, 20) + org[2];
476 fvel2[0] = fvel[0] + lhrandom(-30, 30);
477 fvel2[1] = fvel[1] + lhrandom(-30, 30);
478 fvel2[2] = fvel[2] + lhrandom(-30, 30);
479 f = lhrandom(0.2, 1);
483 particle(pt_flamefall, 106 + (rand()%6), particletexture, false, 5, lhrandom(96, 192), 5, forg[0], forg[1], forg[2], fvel2[0], fvel2[1], fvel2[2]);
486 // for (i = 0;i < 16;i++)
487 // particle2(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], true, 20, 192, 99, org, 20, 0);
488 // for (i = 0;i < 50;i++)
489 // particle2(pt_flamingdebris, ramp3[rand()%6], particletexture, false, 3, 255, 99, org, 10, 200);
490 // for (i = 0;i < 30;i++)
491 // particle2(pt_smokingdebris, 10 + (rand()%6), particletexture, false, 2, 255, 99, org, 10, 100);
502 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
505 if (!r_particles.value) return; // LordHavoc: particles are optional
507 for (i = 0;i < 512;i++)
508 particle2(pt_fade, colorStart + (i % colorLength), particletexture, false, 1.5, 255, 0.3, org, 8, 192);
517 void R_BlobExplosion (vec3_t org)
520 if (!r_particles.value) return; // LordHavoc: particles are optional
522 for (i=0 ; i<512 ; i++)
523 particle3(pt_blob, 66+(rand()%6), particletexture, false, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
524 for (i=0 ; i<512 ; i++)
525 particle3(pt_blob2, 150+(rand()%6), particletexture, false, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
534 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
536 if (!r_particles.value) return; // LordHavoc: particles are optional
540 R_ParticleExplosion(org, false);
546 particle2(pt_fade, color + (rand()&7), particletexture, false, 6, (count & 7) * 16 + (rand()&15), 1, org, 8, 15);
551 particle2(pt_fade, color + (rand()&7), particletexture, false, 6, 128, 1, org, 8, 15);
554 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
560 void R_SparkShower (vec3_t org, vec3_t dir, int count)
562 if (!r_particles.value) return; // LordHavoc: particles are optional
565 particle(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], true, 8, 160, 99, org[0], org[1], org[2], 0, 0, 0);
568 // particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1, lhrandom(0, 255), 5, org, 4, 96);
569 particle(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1, lhrandom(0, 255), 5, lhrandom(-4, 4) + org[0], lhrandom(-4, 4) + org[1], lhrandom(-4, 4) + org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64) + 64);
572 void R_BloodPuff (vec3_t org)
574 if (!r_particles.value) return; // LordHavoc: particles are optional
576 particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], true, 12, 128, 99, org[0], org[1], org[2], 0, 0, 0);
577 particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], true, 10, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
578 particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], true, 8, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
581 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
588 if (!r_particles.value) return; // LordHavoc: particles are optional
590 VectorSubtract(maxs, mins, diff);
591 center[0] = (mins[0] + maxs[0]) * 0.5;
592 center[1] = (mins[1] + maxs[1]) * 0.5;
593 center[2] = (mins[2] + maxs[2]) * 0.5;
594 // FIXME: change velspeed back to 2.0x after fixing mod
595 velscale[0] = velspeed * 0.5 / diff[0];
596 velscale[1] = velspeed * 0.5 / diff[1];
597 velscale[2] = velspeed * 0.5 / diff[2];
603 p->texnum = smokeparticletexture[rand()&7];
605 p->scale = lhrandom(4, 6);
606 p->alpha = 96 + (rand()&63);
607 p->die = cl.time + 2;
608 p->type = pt_bloodcloud;
609 p->color = 251; //(rand()&3)+68;
610 for (j=0 ; j<3 ; j++)
612 p->org[j] = diff[j] * (float) (rand()%1024) * (1.0 / 1024.0) + mins[j];
613 p->vel[j] = (p->org[j] - center[j]) * velscale[j];
618 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
624 if (!r_particles.value) return; // LordHavoc: particles are optional
625 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
626 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
627 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
629 VectorSubtract(maxs, mins, diff);
635 p->texnum = particletexture;
639 p->die = cl.time + 1 + (rand()&15)*0.0625;
644 p->color = colorbase + (rand()&3);
645 for (j=0 ; j<3 ; j++)
647 p->org[j] = diff[j] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[j];
649 p->vel[j] = dir[j] + (rand()%randomvel)-(randomvel*0.5);
656 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
664 if (!r_particles.value) return; // LordHavoc: particles are optional
665 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
666 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
667 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
668 if (dir[2] < 0) // falling
670 t = (maxs[2] - mins[2]) / -dir[2];
675 t = (maxs[2] - mins[2]) / dir[2];
678 if (t < 0 || t > 2) // sanity check
682 VectorSubtract(maxs, mins, diff);
684 for (i=0 ; i<count ; i++)
688 vel[0] = dir[0] + (rand()&31) - 16;
689 vel[1] = dir[1] + (rand()&31) - 16;
690 vel[2] = dir[2] + (rand()&63) - 32;
691 org[0] = diff[0] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[0];
692 org[1] = diff[1] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[1];
700 p->texnum = particletexture;
707 p->texnum = rainparticletexture;
711 p->color = colorbase + (rand()&3);
712 VectorCopy(org, p->org);
713 VectorCopy(vel, p->vel);
714 VectorCopy(vel, p->vel2);
725 void R_LavaSplash (vec3_t org)
731 if (!r_particles.value) return; // LordHavoc: particles are optional
733 for (i=-128 ; i<128 ; i+=16)
734 for (j=-128 ; j<128 ; j+=16)
738 p->texnum = particletexture;
742 p->die = cl.time + 2 + (rand()&31) * 0.02;
743 p->color = 224 + (rand()&7);
746 dir[0] = j + (rand()&7);
747 dir[1] = i + (rand()&7);
750 p->org[0] = org[0] + dir[0];
751 p->org[1] = org[1] + dir[1];
752 p->org[2] = org[2] + (rand()&63);
754 VectorNormalize (dir);
755 vel = 50 + (rand()&63);
756 VectorScale (dir, vel, p->vel);
766 void R_TeleportSplash (vec3_t org)
770 if (!r_particles.value) return; // LordHavoc: particles are optional
772 for (i=-16 ; i<16 ; i+=8)
773 for (j=-16 ; j<16 ; j+=8)
774 for (k=-24 ; k<32 ; k+=8)
778 p->texnum = particletexture;
781 p->alpha = lhrandom(32,128);
782 p->die = cl.time + 5;
786 p->org[0] = org[0] + i + (rand()&7);
787 p->org[1] = org[1] + j + (rand()&7);
788 p->org[2] = org[2] + k + (rand()&7);
790 p->vel[0] = i*2 + (rand()%25) - 12;
791 p->vel[1] = j*2 + (rand()%25) - 12;
792 p->vel[2] = k*2 + (rand()%25) - 12 + 40;
796 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
799 float len, dec = 0, t, nt, speed;
800 int j, contents, bubbles;
802 if (!r_particles.value) return; // LordHavoc: particles are optional
806 if (ent->trail_leftover < 0)
807 ent->trail_leftover = 0;
808 t += ent->trail_leftover;
809 ent->trail_leftover -= (cl.time - cl.oldtime);
813 contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
814 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
817 VectorSubtract (end, start, vec);
818 len = VectorNormalizeLength (vec);
821 speed = len / (nt - t);
823 bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
829 p->vel[0] = p->vel[1] = p->vel[2] = 0;
830 p->die = cl.time + 2;
834 case 0: // rocket trail
835 case 1: // grenade trail
838 dec = type == 0 ? 0.01f : 0.02f;
839 p->texnum = bubbleparticletexture;
841 p->scale = lhrandom(1,2);
845 p->die = cl.time + 2;
846 for (j=0 ; j<3 ; j++)
848 p->vel[j] = (rand()&31)-16;
849 p->org[j] = start[j] + ((rand()&3)-2);
854 dec = type == 0 ? 0.02f : 0.04f;
855 p->texnum = smokeparticletexture[rand()&7];
857 p->scale = lhrandom(4, 8);
858 p->alpha = 160; //128 + (rand()&63);
861 p->die = cl.time + 10000;
862 VectorCopy(start, p->org);
866 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
867 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
868 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
869 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
876 case 1: // smoke smoke
878 p->texnum = smokeparticletexture;
880 p->scale = lhrandom(6,9);
882 if (r_smokecolor.value)
883 p->color = r_smokecolor.value;
885 p->color = (rand()&3)+12;
887 p->die = cl.time + 1;
888 VectorCopy(start, p->org);
893 case 4: // slight blood
895 p->texnum = smokeparticletexture[rand()&7];
897 p->scale = lhrandom(4, 6);
898 p->alpha = type == 4 ? 192 : 255;
899 p->color = 247; //(rand()&3)+68;
900 p->type = pt_bloodcloud;
901 p->die = cl.time + 9999;
902 for (j=0 ; j<3 ; j++)
904 p->vel[j] = (rand()&15)-8;
905 p->org[j] = start[j] + ((rand()&3)-2);
912 p->texnum = smokeparticletexture[rand()&7];
915 p->alpha = 64 + (rand()&31);
916 p->color = type == 3 ? 56 : 234;
918 p->die = cl.time + 10000;
919 VectorCopy(start, p->org);
922 case 6: // voor trail
923 dec = 0.05f; // sparse trail
924 p->texnum = smokeparticletexture[rand()&7];
926 p->scale = lhrandom(3, 5);
928 p->color = 9*16 + 8 + (rand()&3);
930 p->die = cl.time + 2;
931 for (j=0 ; j<3 ; j++)
933 p->vel[j] = (rand()&15)-8;
934 p->org[j] = start[j] + ((rand()&3)-2);
938 case 7: // Nehahra smoke tracer
940 p->texnum = smokeparticletexture[rand()&7];
942 p->scale = lhrandom(8, 12);
944 p->color = (rand()&3)+12;
946 p->die = cl.time + 10000;
947 for (j=0 ; j<3 ; j++)
948 p->org[j] = start[j] + ((rand()&3)-2);
954 VectorMA (start, dec, vec, start);
956 ent->trail_leftover = t - cl.time;
959 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
963 if (!r_particles.value) return; // LordHavoc: particles are optional
965 VectorSubtract (end, start, vec);
966 len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
967 VectorScale(vec, 3, vec);
970 particle(pt_smoke, color, particletexture, false, 8, 192, 99, start[0], start[1], start[2], 0, 0, 0);
971 VectorAdd (start, vec, start);
981 extern cvar_t sv_gravity;
983 void R_MoveParticles (void)
986 int i, activeparticles, maxparticle, j, a;
988 float gravity, dvel, frametime;
990 // LordHavoc: early out condition
994 frametime = cl.time - cl.oldtime;
995 gravity = frametime * sv_gravity.value;
996 dvel = 1+4*frametime;
1001 for (i = 0, p = particles;i < numparticles;i++, p++)
1003 if (p->die < cl.time)
1005 freeparticles[j++] = p;
1009 VectorCopy(p->org, p->oldorg);
1010 p->org[0] += p->vel[0]*frametime;
1011 p->org[1] += p->vel[1]*frametime;
1012 p->org[2] += p->vel[2]*frametime;
1019 // LordHavoc: drop-through because of shared code
1028 p->vel[2] -= gravity;
1031 if (cl.time > p->time2)
1033 p->time2 = cl.time + (rand() & 3) * 0.1;
1034 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1035 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1036 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1040 // if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1045 p->scale += frametime * 16;
1046 p->alpha -= frametime * 512;
1048 case pt_fallfadespark:
1049 p->alpha -= frametime * 256;
1050 p->vel[2] -= gravity;
1053 p->alpha -= frametime * 512;
1056 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1057 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1059 p->texnum = smokeparticletexture[rand()&7];
1060 p->type = pt_splash;
1063 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1064 p->die = cl.time + 1000;
1067 p->vel[2] += gravity * 0.25;
1068 p->vel[0] *= (1 - (frametime * 0.0625));
1069 p->vel[1] *= (1 - (frametime * 0.0625));
1070 p->vel[2] *= (1 - (frametime * 0.0625));
1071 if (cl.time > p->time2)
1073 p->time2 = cl.time + lhrandom(0, 0.5);
1074 p->vel[0] += lhrandom(-32,32);
1075 p->vel[1] += lhrandom(-32,32);
1076 p->vel[2] += lhrandom(-32,32);
1078 p->alpha -= frametime * 64;
1080 // LordHavoc: for smoke trails
1082 p->scale += frametime * 16;
1083 p->alpha -= frametime * 256;
1086 p->scale += frametime * 64;
1087 p->alpha -= frametime * 256;
1090 p->scale += frametime * 24;
1091 p->alpha -= frametime * 512;
1094 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1095 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1098 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1099 break; // still in solid
1100 p->die = cl.time + 1000;
1104 case CONTENTS_SLIME:
1105 p->texnum = smokeparticletexture[rand()&7];
1106 p->type = pt_smokecloud;
1110 case CONTENTS_WATER:
1111 p->texnum = smokeparticletexture[rand()&7];
1112 p->type = pt_splash;
1115 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1116 // p->texnum = bubbleparticletexture;
1117 // p->type = pt_bubble;
1118 // p->vel[2] *= 0.1;
1120 default: // CONTENTS_SOLID and any others
1121 TraceLine(p->oldorg, p->org, v, normal);
1122 VectorCopy(v, p->org);
1123 p->texnum = smokeparticletexture[rand()&7];
1124 p->type = pt_splash;
1127 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1128 p->die = cl.time + 1000;
1134 p->alpha -= frametime * 512;
1136 case pt_flamingdebris:
1137 if (cl.time >= p->time2)
1139 p->time2 = cl.time + 0.01;
1140 particle2(pt_flame, p->color, particletexture, false, 4, p->alpha, 999, p->org, 0, 50);
1142 p->alpha -= frametime * 512;
1143 p->vel[2] -= gravity * 0.5f;
1144 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1147 case pt_smokingdebris:
1148 if (cl.time >= p->time2)
1150 p->time2 = cl.time + 0.01;
1151 particle2(pt_flame, 15, smokeparticletexture[rand()&7], false, 4, p->alpha, 999, p->org, 0, 50);
1153 p->alpha -= frametime * 512;
1154 p->vel[2] -= gravity * 0.5f;
1155 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1159 p->alpha -= frametime * 512;
1160 p->vel[2] -= gravity * 0.5f;
1164 // LordHavoc: most particles did this check anyway, consistency...
1168 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1169 if (p->die < cl.time)
1170 freeparticles[j++] = p;
1177 // fill in gaps to compact the array
1179 while (maxparticle >= activeparticles)
1181 *freeparticles[i++] = particles[maxparticle--];
1182 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1185 numparticles = activeparticles;
1188 void R_CompleteLightPoint (vec3_t color, vec3_t p);
1190 void R_DrawParticles (void)
1193 int i, r,g,b,a, dynlight;
1194 float scale, scale2, minparticledist;
1196 vec3_t up, right, uprightangles, forward2, up2, right2, tempcolor;
1198 // LordHavoc: early out condition
1199 if ((!numparticles) || (!r_drawparticles.value))
1202 dynlight = r_dynamicparticles.value;
1203 if (!r_dynamic.value)
1205 c_particles += numparticles;
1207 VectorScale (vup, 1.5, up);
1208 VectorScale (vright, 1.5, right);
1210 uprightangles[0] = 0;
1211 uprightangles[1] = r_refdef.viewangles[1];
1212 uprightangles[2] = 0;
1213 AngleVectors (uprightangles, forward2, right2, up2);
1215 minparticledist = DotProduct(r_refdef.vieworg, vpn) + 16.0f;
1217 for (i = 0, p = particles;i < numparticles;i++, p++)
1219 // LordHavoc: unnecessary (array was already compacted)
1220 // if (p->die < cl.time)
1223 // LordHavoc: only render if not too close
1224 if (DotProduct(p->org, vpn) < minparticledist)
1227 color24 = (byte *) &d_8to24table[(int)p->color];
1232 if (dynlight && (p->dynlight || dynlight >= 2)) // LordHavoc: only light blood and smoke
1234 R_CompleteLightPoint(tempcolor, p->org);
1235 r = (r * (int) tempcolor[0]) >> 7;
1236 g = (g * (int) tempcolor[1]) >> 7;
1237 b = (b * (int) tempcolor[2]) >> 7;
1239 transpolybegin(p->texnum, 0, p->texnum, TPOLYTYPE_ALPHA);
1240 scale = p->scale * -0.5;scale2 = p->scale * 0.5;
1241 if (p->texnum == rainparticletexture) // rain streak
1243 transpolyvert(p->org[0] + up2[0]*scale + right2[0]*scale , p->org[1] + up2[1]*scale + right2[1]*scale , p->org[2] + up2[2]*scale + right2[2]*scale , 0,1,r,g,b,a);
1244 transpolyvert(p->org[0] + up2[0]*scale2 + right2[0]*scale , p->org[1] + up2[1]*scale2 + right2[1]*scale , p->org[2] + up2[2]*scale2 + right2[2]*scale , 0,0,r,g,b,a);
1245 transpolyvert(p->org[0] + up2[0]*scale2 + right2[0]*scale2, p->org[1] + up2[1]*scale2 + right2[1]*scale2, p->org[2] + up2[2]*scale2 + right2[2]*scale2, 1,0,r,g,b,a);
1246 transpolyvert(p->org[0] + up2[0]*scale + right2[0]*scale2, p->org[1] + up2[1]*scale + right2[1]*scale2, p->org[2] + up2[2]*scale + right2[2]*scale2, 1,1,r,g,b,a);
1250 transpolyvert(p->org[0] + up[0]*scale + right[0]*scale , p->org[1] + up[1]*scale + right[1]*scale , p->org[2] + up[2]*scale + right[2]*scale , 0,1,r,g,b,a);
1251 transpolyvert(p->org[0] + up[0]*scale2 + right[0]*scale , p->org[1] + up[1]*scale2 + right[1]*scale , p->org[2] + up[2]*scale2 + right[2]*scale , 0,0,r,g,b,a);
1252 transpolyvert(p->org[0] + up[0]*scale2 + right[0]*scale2, p->org[1] + up[1]*scale2 + right[1]*scale2, p->org[2] + up[2]*scale2 + right[2]*scale2, 1,0,r,g,b,a);
1253 transpolyvert(p->org[0] + up[0]*scale + right[0]*scale2, p->org[1] + up[1]*scale + right[1]*scale2, p->org[2] + up[2]*scale + right[2]*scale2, 1,1,r,g,b,a);