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
28 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
31 typedef struct particle_s
41 float time2; // used for various things (snow fluttering, for example)
43 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
46 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
47 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
48 int ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
51 int smokeparticletexture[8];
52 int rainparticletexture;
53 int bubbleparticletexture;
55 particle_t *particles;
58 vec3_t r_pright, r_pup, r_ppn;
61 particle_t **freeparticles; // list used only in compacting particles array
63 // LordHavoc: reduced duplicate code, and allow particle allocation system independence
64 #define ALLOCPARTICLE \
65 if (numparticles >= r_numparticles)\
67 p = &particles[numparticles++];
69 cvar_t r_particles = {"r_particles", "1"};
70 cvar_t r_dynamicparticles = {"r_dynamicparticles", "0", TRUE};
72 byte shadebubble(float dx, float dy, vec3_t light)
76 if ((dx*dx+dy*dy) < 1) // it does hit the sphere
78 dz = 1 - (dx*dx+dy*dy);
81 normal[0] = dx;normal[1] = dy;normal[2] = dz;
82 VectorNormalize(normal);
83 dot = DotProduct(normal, light);
84 if (dot > 0.5) // interior reflection
86 else if (dot < -0.5) // exterior reflection
87 f += ((dot * -2) - 1);
89 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
90 VectorNormalize(normal);
91 dot = DotProduct(normal, light);
92 if (dot > 0.5) // interior reflection
94 else if (dot < -0.5) // exterior reflection
95 f += ((dot * -2) - 1);
97 f += 16; // just to give it a haze so you can see the outline
105 void R_InitParticleTexture (void)
109 byte data[32][32][4], noise1[128][128], noise2[128][128];
112 for (y = 0;y < 32;y++)
115 for (x = 0;x < 32;x++)
117 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
119 d = (255 - (dx*dx+dy*dy));
121 data[y][x][3] = (byte) d;
124 particletexture = GL_LoadTexture ("particletexture", 32, 32, &data[0][0][0], true, true, 4);
126 for (i = 0;i < 8;i++)
130 fractalnoise(&noise1[0][0], 128, 8);
131 //memset(noise1, 255, 32*32);
132 fractalnoise(&noise2[0][0], 128, 16);
134 for (y = 0;y < 32;y++)
137 for (x = 0;x < 32;x++)
141 k = (noise1[y][x] >> 1) + 128;
142 // data[y][x][0] = data[y][x][1] = data[y][x][2] = (noise1[y][x] >> 1) + 128;
143 // data[y][x][3] = 0;
144 data[y][x][0] = data[y][x][1] = data[y][x][2] = data[y][x][3] = 0;
146 d = ((noise2[y][x] * 448) >> 8) - 128;
147 // d = ((noise2[y][x] * 384) >> 8) - 128;
153 //d = (d * (255 - (int) (dx*dx+dy*dy))) >> 7;
154 j = (sqrt(dx*dx+dy*dy) * 2.0f - 16.0f);
156 d = (d * (255 - j*j)) >> 8;
158 if (d > 255) d = 255;
159 data[y][x][3] = (byte) d;
162 data[y][x][0] = data[y][x][1] = data[y][x][2] = ((k * d) >> 8);
169 smokeparticletexture[i] = GL_LoadTexture (va("smokeparticletexture%d", i), 32, 32, &data[0][0][0], true, true, 4);
172 light[0] = 1;light[1] = 1;light[2] = 1;
173 VectorNormalize(light);
174 for (x=0 ; x<32 ; x++)
176 for (y=0 ; y<32 ; y++)
178 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
179 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);
182 rainparticletexture = GL_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], true, true, 4);
184 light[0] = 1;light[1] = 1;light[2] = 1;
185 VectorNormalize(light);
186 for (x=0 ; x<32 ; x++)
188 for (y=0 ; y<32 ; y++)
190 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
191 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
194 bubbleparticletexture = GL_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], true, true, 4);
199 particles = (particle_t *) malloc (r_numparticles * sizeof(particle_t));
200 freeparticles = (void *) malloc (r_numparticles * sizeof(particle_t *));
201 R_InitParticleTexture ();
204 void r_part_shutdown()
215 void R_Particles_Init (void)
219 i = COM_CheckParm ("-particles");
223 r_numparticles = (int)(atoi(com_argv[i+1]));
224 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
225 r_numparticles = ABSOLUTE_MIN_PARTICLES;
229 r_numparticles = MAX_PARTICLES;
232 Cvar_RegisterVariable (&r_particles);
233 Cvar_RegisterVariable (&r_dynamicparticles);
235 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown);
238 #define particle(ptype, pcolor, ptex, pscale, palpha, ptime, px, py, pz, pvx, pvy, pvz)\
243 p->color = (pcolor);\
245 p->scale = (pscale);\
246 p->alpha = (palpha);\
247 p->die = cl.time + (ptime);\
255 #define particle2(ptype, pcolor, ptex, pscale, palpha, ptime, pbase, poscale, pvscale)\
260 p->color = (pcolor);\
262 p->scale = (pscale);\
263 p->alpha = (palpha);\
264 p->die = cl.time + (ptime);\
265 p->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
266 p->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
267 p->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
268 p->vel[0] = lhrandom(-(pvscale), (pvscale));\
269 p->vel[1] = lhrandom(-(pvscale), (pvscale));\
270 p->vel[2] = lhrandom(-(pvscale), (pvscale));\
272 #define particle3(ptype, pcolor, ptex, pscale, palpha, ptime, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
277 p->color = (pcolor);\
279 p->scale = (pscale);\
280 p->alpha = (palpha);\
281 p->die = cl.time + (ptime);\
282 p->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
283 p->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
284 p->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
285 p->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
286 p->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
287 p->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
290 void particle(int type, int color, int tex, float scale, int alpha, float time, float x, float y, float z, float vx, float vy, float vz)
300 p->die = cl.time + time;
308 void particle2(int type, int color, int tex, float scale, int alpha, float time, vec3_t base, float oscale, float vscale)
318 p->die = cl.time + time;
319 p->org[0] = lhrandom(-oscale, oscale) + base[0];
320 p->org[1] = lhrandom(-oscale, oscale) + base[1];
321 p->org[2] = lhrandom(-oscale, oscale) + base[2];
322 p->vel[0] = lhrandom(-vscale, vscale);
323 p->vel[1] = lhrandom(-vscale, vscale);
324 p->vel[2] = lhrandom(-vscale, vscale);
326 void particle3(int type, int color, int tex, float scale, int alpha, float time, vec3_t base, float scalex, float scaley, float scalez, float vscalex, float vscaley, float vscalez)
336 p->die = cl.time + time;
337 p->org[0] = lhrandom(-scalex, scalex) + base[0];
338 p->org[1] = lhrandom(-scaley, scaley) + base[1];
339 p->org[2] = lhrandom(-scalez, scalez) + base[2];
340 p->vel[0] = lhrandom(-vscalex, vscalex);
341 p->vel[1] = lhrandom(-vscaley, vscaley);
342 p->vel[2] = lhrandom(-vscalez, vscalez);
352 #define NUMVERTEXNORMALS 162
353 extern float r_avertexnormals[NUMVERTEXNORMALS][3];
354 vec3_t avelocities[NUMVERTEXNORMALS];
355 float beamlength = 16;
356 vec3_t avelocity = {23, 7, 3};
357 float partstep = 0.01;
358 float timescale = 0.01;
360 void R_EntityParticles (entity_t *ent)
365 float sp, sy, cp, cy;
368 if (!r_particles.value) return; // LordHavoc: particles are optional
373 if (!avelocities[0][0])
374 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
375 avelocities[0][i] = (rand()&255) * 0.01;
377 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
379 angle = cl.time * avelocities[i][0];
382 angle = cl.time * avelocities[i][1];
390 particle(pt_static, 0x6f, particletexture, 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);
400 void R_ClearParticles (void)
403 // free_particles = &particles[0];
404 // active_particles = NULL;
406 // for (i=0 ;i<r_numparticles ; i++)
407 // particles[i].next = &particles[i+1];
408 // particles[r_numparticles-1].next = NULL;
414 void R_ReadPointFile_f (void)
420 char name[MAX_OSPATH];
422 sprintf (name,"maps/%s.pts", sv.name);
424 COM_FOpenFile (name, &f, false);
427 Con_Printf ("couldn't open %s\n", name);
431 Con_Printf ("Reading %s...\n", name);
435 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
440 if (numparticles >= r_numparticles)
442 Con_Printf ("Not enough free particles\n");
445 particle(pt_static, (-c)&15, particletexture, 2, 255, 99999, org[0], org[1], org[2], 0, 0, 0);
449 Con_Printf ("%i points read\n", c);
454 R_ParseParticleEffect
456 Parse an effect out of the server message
459 void R_ParseParticleEffect (void)
462 int i, count, msgcount, color;
464 for (i=0 ; i<3 ; i++)
465 org[i] = MSG_ReadCoord ();
466 for (i=0 ; i<3 ; i++)
467 dir[i] = MSG_ReadChar () * (1.0/16);
468 msgcount = MSG_ReadByte ();
469 color = MSG_ReadByte ();
476 R_RunParticleEffect (org, dir, color, count);
485 void R_ParticleExplosion (vec3_t org, int smoke)
488 if (!r_particles.value) return; // LordHavoc: particles are optional
490 particle(pt_smokecloud, (rand()&7) + 8, smokeparticletexture[rand()&7], 30, 255, 2, org[0], org[1], org[2], 0, 0, 0);
492 i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
493 if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
495 for (i=0 ; i<128 ; i++)
496 particle2(pt_bubble, (rand()&3) + 12, bubbleparticletexture, lhrandom(1, 2), 255, 2, org, 16, 96);
500 for (i = 0;i < 256;i++)
501 particle(pt_fallfadespark, ramp3[rand()%6], particletexture, 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);
512 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
515 if (!r_particles.value) return; // LordHavoc: particles are optional
517 for (i = 0;i < 512;i++)
518 particle2(pt_fade, colorStart + (i % colorLength), particletexture, 1.5, 255, 0.3, org, 8, 192);
527 void R_BlobExplosion (vec3_t org)
530 if (!r_particles.value) return; // LordHavoc: particles are optional
532 for (i=0 ; i<512 ; i++)
533 particle3(pt_blob, 66+(rand()%6), particletexture, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
534 for (i=0 ; i<512 ; i++)
535 particle3(pt_blob2, 150+(rand()%6), particletexture, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
544 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
546 if (!r_particles.value) return; // LordHavoc: particles are optional
550 R_ParticleExplosion(org, false);
556 particle2(pt_fade, color + (rand()&7), particletexture, 6, (count & 7) * 16 + (rand()&15), 1, org, 8, 15);
561 particle2(pt_fade, color + (rand()&7), particletexture, 6, 128, 1, org, 8, 15);
564 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
570 void R_SparkShower (vec3_t org, vec3_t dir, int count)
572 if (!r_particles.value) return; // LordHavoc: particles are optional
575 particle(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], 8, 160, 99, org[0], org[1], org[2], 0, 0, 0);
578 // particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, 1, lhrandom(0, 255), 5, org, 4, 96);
579 particle(pt_fallfadespark, ramp3[rand()%6], particletexture, 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);
582 void R_BloodPuff (vec3_t org)
584 if (!r_particles.value) return; // LordHavoc: particles are optional
586 particle(pt_bloodcloud, 68+(rand()&3), smokeparticletexture[rand()&7], 12, 128, 99, org[0], org[1], org[2], 0, 0, 0);
587 particle(pt_bloodcloud, 68+(rand()&3), smokeparticletexture[rand()&7], 10, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
588 particle(pt_bloodcloud, 68+(rand()&3), smokeparticletexture[rand()&7], 8, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
591 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
598 if (!r_particles.value) return; // LordHavoc: particles are optional
600 VectorSubtract(maxs, mins, diff);
601 center[0] = (mins[0] + maxs[0]) * 0.5;
602 center[1] = (mins[1] + maxs[1]) * 0.5;
603 center[2] = (mins[2] + maxs[2]) * 0.5;
604 // FIXME: change velspeed back to 2.0x after fixing mod
605 velscale[0] = velspeed * 0.5 / diff[0];
606 velscale[1] = velspeed * 0.5 / diff[1];
607 velscale[2] = velspeed * 0.5 / diff[2];
613 p->texnum = smokeparticletexture[rand()&7];
614 p->scale = lhrandom(4, 6);
615 p->alpha = 96 + (rand()&63);
616 p->die = cl.time + 2;
617 p->type = pt_bloodcloud;
618 p->color = (rand()&3)+68;
619 for (j=0 ; j<3 ; j++)
621 p->org[j] = diff[j] * (float) (rand()%1024) * (1.0 / 1024.0) + mins[j];
622 p->vel[j] = (p->org[j] - center[j]) * velscale[j];
627 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
633 if (!r_particles.value) return; // LordHavoc: particles are optional
634 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
635 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
636 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
638 VectorSubtract(maxs, mins, diff);
644 p->texnum = particletexture;
647 p->die = cl.time + 1 + (rand()&15)*0.0625;
652 p->color = colorbase + (rand()&3);
653 for (j=0 ; j<3 ; j++)
655 p->org[j] = diff[j] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[j];
657 p->vel[j] = dir[j] + (rand()%randomvel)-(randomvel*0.5);
664 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
672 if (!r_particles.value) return; // LordHavoc: particles are optional
673 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
674 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
675 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
676 if (dir[2] < 0) // falling
678 t = (maxs[2] - mins[2]) / -dir[2];
683 t = (maxs[2] - mins[2]) / dir[2];
686 if (t < 0 || t > 2) // sanity check
690 VectorSubtract(maxs, mins, diff);
692 for (i=0 ; i<count ; i++)
696 vel[0] = dir[0] + (rand()&31) - 16;
697 vel[1] = dir[1] + (rand()&31) - 16;
698 vel[2] = dir[2] + (rand()&63) - 32;
699 org[0] = diff[0] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[0];
700 org[1] = diff[1] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[1];
708 p->texnum = particletexture;
714 p->texnum = rainparticletexture;
717 p->color = colorbase + (rand()&3);
718 VectorCopy(org, p->org);
719 VectorCopy(vel, p->vel);
720 VectorCopy(vel, p->vel2);
731 void R_LavaSplash (vec3_t org)
737 if (!r_particles.value) return; // LordHavoc: particles are optional
739 for (i=-128 ; i<128 ; i+=16)
740 for (j=-128 ; j<128 ; j+=16)
744 p->texnum = particletexture;
747 p->die = cl.time + 2 + (rand()&31) * 0.02;
748 p->color = 224 + (rand()&7);
751 dir[0] = j + (rand()&7);
752 dir[1] = i + (rand()&7);
755 p->org[0] = org[0] + dir[0];
756 p->org[1] = org[1] + dir[1];
757 p->org[2] = org[2] + (rand()&63);
759 VectorNormalize (dir);
760 vel = 50 + (rand()&63);
761 VectorScale (dir, vel, p->vel);
771 void R_TeleportSplash (vec3_t org)
775 if (!r_particles.value) return; // LordHavoc: particles are optional
777 for (i=-16 ; i<16 ; i+=8)
778 for (j=-16 ; j<16 ; j+=8)
779 for (k=-24 ; k<32 ; k+=8)
783 p->texnum = particletexture;
785 p->alpha = lhrandom(32,128);
786 p->die = cl.time + 5;
790 p->org[0] = org[0] + i + (rand()&7);
791 p->org[1] = org[1] + j + (rand()&7);
792 p->org[2] = org[2] + k + (rand()&7);
794 p->vel[0] = i*2 + (rand()%25) - 12;
795 p->vel[1] = j*2 + (rand()%25) - 12;
796 p->vel[2] = k*2 + (rand()%25) - 12 + 40;
800 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
803 float len, dec = 0, t, nt, speed;
804 int j, contents, bubbles;
806 if (!r_particles.value) return; // LordHavoc: particles are optional
810 if (ent->trail_leftover < 0)
811 ent->trail_leftover = 0;
812 t += ent->trail_leftover;
813 ent->trail_leftover -= (cl.time - cl.oldtime);
817 contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
818 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
821 VectorSubtract (end, start, vec);
822 len = VectorNormalizeLength (vec);
825 speed = len / (nt - t);
827 bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
833 p->vel[0] = p->vel[1] = p->vel[2] = 0;
834 p->die = cl.time + 2;
838 case 0: // rocket trail
839 case 1: // grenade trail
843 p->texnum = bubbleparticletexture;
844 p->scale = lhrandom(1,2);
848 p->die = cl.time + 2;
849 for (j=0 ; j<3 ; j++)
851 p->vel[j] = (rand()&31)-16;
852 p->org[j] = start[j] + ((rand()&3)-2);
858 p->texnum = smokeparticletexture[rand()&7];
859 p->scale = lhrandom(4, 8);
860 p->alpha = 255; //128 + (rand()&63);
863 p->die = cl.time + 10000;
864 VectorCopy(start, p->org);
865 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
866 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, 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, 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, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
873 case 1: // smoke smoke
875 p->texnum = smokeparticletexture;
876 p->scale = lhrandom(6,9);
878 if (r_smokecolor.value)
879 p->color = r_smokecolor.value;
881 p->color = (rand()&3)+12;
883 p->die = cl.time + 1;
884 VectorCopy(start, p->org);
889 case 4: // slight blood
891 p->texnum = smokeparticletexture[rand()&7];
892 p->scale = lhrandom(4, 6);
893 p->alpha = type == 4 ? 192 : 255;
894 p->color = (rand()&3)+68;
895 p->type = pt_bloodcloud;
896 p->die = cl.time + 9999;
897 for (j=0 ; j<3 ; j++)
899 p->vel[j] = (rand()&15)-8;
900 p->org[j] = start[j] + ((rand()&3)-2);
907 p->texnum = smokeparticletexture[rand()&7];
909 p->alpha = 64 + (rand()&31);
910 p->color = type == 3 ? 56 : 234;
912 p->die = cl.time + 10000;
913 VectorCopy(start, p->org);
916 case 6: // voor trail
917 dec = 0.05f; // sparse trail
918 p->texnum = smokeparticletexture[rand()&7];
919 p->scale = lhrandom(3, 5);
921 p->color = 9*16 + 8 + (rand()&3);
923 p->die = cl.time + 2;
924 for (j=0 ; j<3 ; j++)
926 p->vel[j] = (rand()&15)-8;
927 p->org[j] = start[j] + ((rand()&3)-2);
931 case 7: // Nehahra smoke tracer
933 p->texnum = smokeparticletexture[rand()&7];
934 p->scale = lhrandom(8, 12);
936 p->color = (rand()&3)+12;
938 p->die = cl.time + 10000;
939 for (j=0 ; j<3 ; j++)
940 p->org[j] = start[j] + ((rand()&3)-2);
946 VectorMA (start, dec, vec, start);
948 ent->trail_leftover = t - cl.time;
951 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
955 if (!r_particles.value) return; // LordHavoc: particles are optional
957 VectorSubtract (end, start, vec);
958 len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
959 VectorScale(vec, 3, vec);
962 particle(pt_smoke, color, particletexture, 8, 192, 99, start[0], start[1], start[2], 0, 0, 0);
963 VectorAdd (start, vec, start);
973 extern cvar_t sv_gravity;
974 void R_CompleteLightPoint (vec3_t color, vec3_t p);
976 void TraceLine (vec3_t start, vec3_t end, vec3_t impact);
978 void R_DrawParticles (void)
982 float gravity, dvel, frametime, scale, scale2, minparticledist;
984 vec3_t up, right, uprightangles, forward2, up2, right2, tempcolor, v;
985 int activeparticles, maxparticle, j, k;
987 // LordHavoc: early out condition
991 VectorScale (vup, 1.5, up);
992 VectorScale (vright, 1.5, right);
994 uprightangles[0] = 0;
995 uprightangles[1] = r_refdef.viewangles[1];
996 uprightangles[2] = 0;
997 AngleVectors (uprightangles, forward2, right2, up2);
999 frametime = cl.time - cl.oldtime;
1000 gravity = frametime * sv_gravity.value;
1001 dvel = 1+4*frametime;
1003 minparticledist = DotProduct(r_refdef.vieworg, vpn) + 16.0f;
1005 activeparticles = 0;
1008 for (k = 0, p = particles;k < numparticles;k++, p++)
1010 if (p->die < cl.time)
1012 freeparticles[j++] = p;
1018 // LordHavoc: only render if not too close
1019 if (DotProduct(p->org, vpn) >= minparticledist)
1021 color24 = (byte *) &d_8to24table[(int)p->color];
1026 if (r_dynamicparticles.value)
1028 R_CompleteLightPoint(tempcolor, p->org);
1029 r = (r * (int) tempcolor[0]) >> 7;
1030 g = (g * (int) tempcolor[1]) >> 7;
1031 b = (b * (int) tempcolor[2]) >> 7;
1033 transpolybegin(p->texnum, 0, p->texnum, TPOLYTYPE_ALPHA);
1034 scale = p->scale * -0.5;scale2 = p->scale * 0.5;
1035 if (p->texnum == rainparticletexture) // rain streak
1037 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);
1038 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);
1039 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);
1040 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);
1044 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);
1045 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);
1046 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);
1047 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);
1052 VectorCopy(p->org, p->oldorg);
1053 p->org[0] += p->vel[0]*frametime;
1054 p->org[1] += p->vel[1]*frametime;
1055 p->org[2] += p->vel[2]*frametime;
1063 for (i=0 ; i<3 ; i++)
1068 for (i=0 ; i<2 ; i++)
1073 p->vel[2] -= gravity;
1076 if (cl.time > p->time2)
1078 p->time2 = cl.time + (rand() & 3) * 0.1;
1079 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1080 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1081 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1085 // if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1090 p->scale += frametime * 16;
1091 p->alpha -= frametime * 512;
1092 if (p->alpha < 1 || p->scale < 1)
1095 case pt_fallfadespark:
1096 p->alpha -= frametime * 256;
1097 p->vel[2] -= gravity;
1102 p->alpha -= frametime * 512;
1107 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1108 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1110 p->texnum = smokeparticletexture[rand()&7];
1111 p->type = pt_splash;
1114 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1115 p->die = cl.time + 1000;
1118 p->vel[2] += gravity * 0.25;
1119 p->vel[0] *= (1 - (frametime * 0.0625));
1120 p->vel[1] *= (1 - (frametime * 0.0625));
1121 p->vel[2] *= (1 - (frametime * 0.0625));
1122 if (cl.time > p->time2)
1124 p->time2 = cl.time + lhrandom(0, 0.5);
1125 p->vel[0] += lhrandom(-32,32);
1126 p->vel[1] += lhrandom(-32,32);
1127 p->vel[2] += lhrandom(-32,32);
1129 p->alpha -= frametime * 64;
1133 // LordHavoc: for smoke trails
1135 p->vel[2] += gravity * 0.08f;
1136 p->scale += frametime * 16;
1137 p->alpha -= frametime * 384;
1142 p->scale += frametime * 64;
1143 p->alpha -= frametime * 384;
1148 p->scale += frametime * 24;
1149 p->alpha -= frametime * 512;
1154 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1155 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1157 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1158 break; // still in solid
1159 p->die = cl.time + 1000;
1163 case CONTENTS_SLIME:
1164 p->texnum = smokeparticletexture[rand()&7];
1165 p->type = pt_smokecloud;
1169 case CONTENTS_WATER:
1170 p->texnum = smokeparticletexture[rand()&7];
1171 p->type = pt_splash;
1174 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1175 // p->texnum = bubbleparticletexture;
1176 // p->type = pt_bubble;
1177 // p->vel[2] *= 0.1;
1179 default: // CONTENTS_SOLID and any others
1180 TraceLine(p->oldorg, p->org, v);
1181 VectorCopy(v, p->org);
1182 p->texnum = smokeparticletexture[rand()&7];
1183 p->type = pt_splash;
1186 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1187 p->die = cl.time + 1000;
1194 // fill in gaps to compact the array
1196 while (maxparticle >= activeparticles)
1198 *freeparticles[i++] = particles[maxparticle--];
1199 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1202 numparticles = activeparticles;