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_bloodcloud,
29 pt_fadespark, pt_fadespark2, pt_fallfadespark, pt_fallfadespark2, pt_bubble, pt_fade, pt_smokecloud
32 typedef struct particle_s
39 // LordHavoc: added for improved particle effects
43 float time2; // used for various things (snow fluttering, for example)
44 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
47 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
48 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
49 int ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
52 int smokeparticletexture[8];
53 int rainparticletexture;
54 int bubbleparticletexture;
56 particle_t *particles;
59 vec3_t r_pright, r_pup, r_ppn;
62 particle_t **freeparticles; // list used only in compacting particles array
64 // LordHavoc: reduced duplicate code, and allow particle allocation system independence
65 #define ALLOCPARTICLE \
66 if (numparticles >= r_numparticles)\
68 p = &particles[numparticles++];
70 cvar_t r_particles = {"r_particles", "1"};
71 cvar_t r_dynamicparticles = {"r_dynamicparticles", "0", TRUE};
73 void fractalnoise(char *noise, int size);
74 void fractalnoise_zeroedge(char *noise, int size);
76 void R_InitParticleTexture (void)
79 float dx, dy, dz, f, dot;
80 byte data[32][32][4], noise1[32][32], noise2[32][32];
83 particletexture = texture_extension_number++;
84 glBindTexture(GL_TEXTURE_2D, particletexture);
86 for (x=0 ; x<32 ; x++)
88 for (y=0 ; y<32 ; y++)
90 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
93 d = (255 - (dx*dx+dy*dy));
95 data[y][x][3] = (byte) d;
98 glTexImage2D (GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
100 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
102 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
103 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
106 for (i = 0;i < 8;i++)
108 fractalnoise(&noise1[0][0], 32);
109 fractalnoise(&noise2[0][0], 32);
110 for (y = 0;y < 32;y++)
111 for (x = 0;x < 32;x++)
113 data[y][x][0] = data[y][x][1] = data[y][x][2] = (noise1[y][x] >> 1) + 128;
116 d = noise2[y][x] * 4 - 512;
121 d = (d * (255 - (int) (dx*dx+dy*dy))) >> 8;
123 if (d > 255) d = 255;
124 data[y][x][3] = (byte) d;
130 smokeparticletexture[i] = texture_extension_number++;
131 glBindTexture(GL_TEXTURE_2D, smokeparticletexture[i]);
132 glTexImage2D (GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
133 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
134 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
135 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
138 rainparticletexture = texture_extension_number++;
139 glBindTexture(GL_TEXTURE_2D, rainparticletexture);
141 for (x=0 ; x<32 ; x++)
143 for (y=0 ; y<32 ; y++)
145 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
146 if (y < 24) // stretch the upper half to make a raindrop
150 d = (255 - (dx*dx+dy*dy))/2;
156 d = (255 - (dx*dx+dy*dy))/2;
159 data[y][x][3] = (byte) d;
162 glTexImage2D (GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
164 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
166 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
167 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
169 bubbleparticletexture = texture_extension_number++;
170 glBindTexture(GL_TEXTURE_2D, bubbleparticletexture);
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 dx = x * (1.0 / 16.0) - 1.0;
180 dy = y * (1.0 / 16.0) - 1.0;
181 if (dx*dx+dy*dy < 1) // it does hit the sphere
183 dz = 1 - (dx*dx+dy*dy);
186 normal[0] = dx;normal[1] = dy;normal[2] = dz;
187 VectorNormalize(normal);
188 dot = DotProduct(normal, light);
189 if (dot > 0.5) // interior reflection
190 f += ((dot * 2) - 1);
191 else if (dot < -0.5) // exterior reflection
192 f += ((dot * -2) - 1);
194 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
195 VectorNormalize(normal);
196 dot = DotProduct(normal, light);
197 if (dot > 0.5) // interior reflection
198 f += ((dot * 2) - 1);
199 else if (dot < -0.5) // exterior reflection
200 f += ((dot * -2) - 1);
202 f = bound(0, f, 255);
203 data[y][x][3] = (byte) f;
209 glTexImage2D (GL_TEXTURE_2D, 0, 4, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
211 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
213 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
214 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
222 void R_InitParticles (void)
226 i = COM_CheckParm ("-particles");
230 r_numparticles = (int)(atoi(com_argv[i+1]));
231 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
232 r_numparticles = ABSOLUTE_MIN_PARTICLES;
236 r_numparticles = MAX_PARTICLES;
239 particles = (particle_t *) Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles");
240 freeparticles = (void *) Hunk_AllocName (r_numparticles * sizeof(particle_t *), "particles");
242 Cvar_RegisterVariable (&r_particles);
243 Cvar_RegisterVariable (&r_dynamicparticles);
244 R_InitParticleTexture ();
247 #define particle(ptype, pcolor, ptex, pscale, palpha, ptime, px, py, pz, pvx, pvy, pvz)\
252 p->color = (pcolor);\
254 p->scale = (pscale);\
255 p->alpha = (palpha);\
256 p->die = cl.time + (ptime);\
264 #define particle2(ptype, pcolor, ptex, pscale, palpha, ptime, pbase, poscale, pvscale)\
269 p->color = (pcolor);\
271 p->scale = (pscale);\
272 p->alpha = (palpha);\
273 p->die = cl.time + (ptime);\
274 p->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
275 p->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
276 p->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
277 p->vel[0] = lhrandom(-(pvscale), (pvscale));\
278 p->vel[1] = lhrandom(-(pvscale), (pvscale));\
279 p->vel[2] = lhrandom(-(pvscale), (pvscale));\
281 #define particle3(ptype, pcolor, ptex, pscale, palpha, ptime, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
286 p->color = (pcolor);\
288 p->scale = (pscale);\
289 p->alpha = (palpha);\
290 p->die = cl.time + (ptime);\
291 p->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
292 p->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
293 p->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
294 p->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
295 p->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
296 p->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
299 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)
309 p->die = cl.time + time;
317 void particle2(int type, int color, int tex, float scale, int alpha, float time, vec3_t base, float oscale, float vscale)
327 p->die = cl.time + time;
328 p->org[0] = lhrandom(-oscale, oscale) + base[0];
329 p->org[1] = lhrandom(-oscale, oscale) + base[1];
330 p->org[2] = lhrandom(-oscale, oscale) + base[2];
331 p->vel[0] = lhrandom(-vscale, vscale);
332 p->vel[1] = lhrandom(-vscale, vscale);
333 p->vel[2] = lhrandom(-vscale, vscale);
335 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)
345 p->die = cl.time + time;
346 p->org[0] = lhrandom(-scalex, scalex) + base[0];
347 p->org[1] = lhrandom(-scaley, scaley) + base[1];
348 p->org[2] = lhrandom(-scalez, scalez) + base[2];
349 p->vel[0] = lhrandom(-vscalex, vscalex);
350 p->vel[1] = lhrandom(-vscaley, vscaley);
351 p->vel[2] = lhrandom(-vscalez, vscalez);
361 #define NUMVERTEXNORMALS 162
362 extern float r_avertexnormals[NUMVERTEXNORMALS][3];
363 vec3_t avelocities[NUMVERTEXNORMALS];
364 float beamlength = 16;
365 vec3_t avelocity = {23, 7, 3};
366 float partstep = 0.01;
367 float timescale = 0.01;
369 void R_EntityParticles (entity_t *ent)
374 float sp, sy, cp, cy;
377 if (!r_particles.value) return; // LordHavoc: particles are optional
382 if (!avelocities[0][0])
383 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
384 avelocities[0][i] = (rand()&255) * 0.01;
386 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
388 angle = cl.time * avelocities[i][0];
391 angle = cl.time * avelocities[i][1];
399 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);
409 void R_ClearParticles (void)
412 // free_particles = &particles[0];
413 // active_particles = NULL;
415 // for (i=0 ;i<r_numparticles ; i++)
416 // particles[i].next = &particles[i+1];
417 // particles[r_numparticles-1].next = NULL;
423 void R_ReadPointFile_f (void)
429 char name[MAX_OSPATH];
431 sprintf (name,"maps/%s.pts", sv.name);
433 COM_FOpenFile (name, &f, false);
436 Con_Printf ("couldn't open %s\n", name);
440 Con_Printf ("Reading %s...\n", name);
444 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
449 if (numparticles >= r_numparticles)
451 Con_Printf ("Not enough free particles\n");
454 particle(pt_static, (-c)&15, particletexture, 2, 255, 99999, org[0], org[1], org[2], 0, 0, 0);
458 Con_Printf ("%i points read\n", c);
463 R_ParseParticleEffect
465 Parse an effect out of the server message
468 void R_ParseParticleEffect (void)
471 int i, count, msgcount, color;
473 for (i=0 ; i<3 ; i++)
474 org[i] = MSG_ReadCoord ();
475 for (i=0 ; i<3 ; i++)
476 dir[i] = MSG_ReadChar () * (1.0/16);
477 msgcount = MSG_ReadByte ();
478 color = MSG_ReadByte ();
485 R_RunParticleEffect (org, dir, color, count);
494 void R_ParticleExplosion (vec3_t org, int smoke)
497 if (!r_particles.value) return; // LordHavoc: particles are optional
499 particle(pt_smokecloud, (rand()&7) + 8, smokeparticletexture[rand()&7], 30, 96, 2, org[0], org[1], org[2], 0, 0, 0);
501 i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
502 if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
503 for (i=0 ; i<32 ; i++)
504 particle2(pt_bubble, (rand()&3) + 12, bubbleparticletexture, lhrandom(1, 2), 255, 2, org, 16, 16);
513 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
516 if (!r_particles.value) return; // LordHavoc: particles are optional
518 for (i = 0;i < 512;i++)
519 particle2(pt_fadespark2, colorStart + (i % colorLength), particletexture, 1.5, 255, 0.3, org, 8, 192);
528 void R_BlobExplosion (vec3_t org)
531 if (!r_particles.value) return; // LordHavoc: particles are optional
533 for (i=0 ; i<512 ; i++)
534 particle3(pt_blob, 66+(rand()%6), particletexture, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
535 for (i=0 ; i<512 ; i++)
536 particle3(pt_blob2, 150+(rand()%6), particletexture, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
545 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
547 if (!r_particles.value) return; // LordHavoc: particles are optional
551 R_ParticleExplosion(org, false);
557 particle2(pt_fade, color + (rand()&7), particletexture, 6, (count & 7) * 16 + (rand()&15), 1, org, 8, 15);
562 particle2(pt_fade, color + (rand()&7), particletexture, 6, 128, 1, org, 8, 15);
565 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
571 void R_SparkShower (vec3_t org, vec3_t dir, int count)
573 if (!r_particles.value) return; // LordHavoc: particles are optional
576 particle(pt_smokecloud, 12+(rand()&3), smokeparticletexture[rand()&7], 8, 64, 99, org[0], org[1], org[2], 0, 0, 0);
579 particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, 1, lhrandom(0, 255), 5, org, 4, 96);
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);
589 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
596 if (!r_particles.value) return; // LordHavoc: particles are optional
598 VectorSubtract(maxs, mins, diff);
599 center[0] = (mins[0] + maxs[0]) * 0.5;
600 center[1] = (mins[1] + maxs[1]) * 0.5;
601 center[2] = (mins[2] + maxs[2]) * 0.5;
602 velscale[0] = velspeed * 2.0 / diff[0];
603 velscale[1] = velspeed * 2.0 / diff[1];
604 velscale[2] = velspeed * 2.0 / diff[2];
610 p->texnum = smokeparticletexture[rand()&7];
612 p->alpha = 96 + (rand()&63);
613 p->die = cl.time + 2;
614 p->type = pt_fadespark;
615 p->color = (rand()&3)+68;
616 for (j=0 ; j<3 ; j++)
618 p->org[j] = diff[j] * (float) (rand()%1024) * (1.0 / 1024.0) + mins[j];
619 p->vel[j] = (p->org[j] - center[j]) * velscale[j];
624 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
630 if (!r_particles.value) return; // LordHavoc: particles are optional
631 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
632 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
633 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
635 VectorSubtract(maxs, mins, diff);
641 p->texnum = particletexture;
644 p->die = cl.time + 1 + (rand()&15)*0.0625;
649 p->color = colorbase + (rand()&3);
650 for (j=0 ; j<3 ; j++)
652 p->org[j] = diff[j] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[j];
654 p->vel[j] = dir[j] + (rand()%randomvel)-(randomvel*0.5);
661 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
669 if (!r_particles.value) return; // LordHavoc: particles are optional
670 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
671 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
672 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
673 if (dir[2] < 0) // falling
675 t = (maxs[2] - mins[2]) / -dir[2];
680 t = (maxs[2] - mins[2]) / dir[2];
683 if (t < 0 || t > 2) // sanity check
687 VectorSubtract(maxs, mins, diff);
689 for (i=0 ; i<count ; i++)
693 vel[0] = dir[0] + (rand()&31) - 16;
694 vel[1] = dir[1] + (rand()&31) - 16;
695 vel[2] = dir[2] + (rand()&63) - 32;
696 org[0] = diff[0] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[0];
697 org[1] = diff[1] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[1];
705 p->texnum = particletexture;
710 p->texnum = rainparticletexture;
713 p->color = colorbase + (rand()&3);
714 VectorCopy(org, p->org);
715 VectorCopy(vel, p->vel);
716 VectorCopy(vel, p->vel2);
727 void R_LavaSplash (vec3_t org)
733 if (!r_particles.value) return; // LordHavoc: particles are optional
735 for (i=-128 ; i<128 ; i+=16)
736 for (j=-128 ; j<128 ; j+=16)
740 p->texnum = particletexture;
743 p->die = cl.time + 2 + (rand()&31) * 0.02;
744 p->color = 224 + (rand()&7);
747 dir[0] = j + (rand()&7);
748 dir[1] = i + (rand()&7);
751 p->org[0] = org[0] + dir[0];
752 p->org[1] = org[1] + dir[1];
753 p->org[2] = org[2] + (rand()&63);
755 VectorNormalize (dir);
756 vel = 50 + (rand()&63);
757 VectorScale (dir, vel, p->vel);
767 void R_TeleportSplash (vec3_t org)
771 if (!r_particles.value) return; // LordHavoc: particles are optional
773 for (i=-16 ; i<16 ; i+=8)
774 for (j=-16 ; j<16 ; j+=8)
775 for (k=-24 ; k<32 ; k+=8)
779 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 static int tracercount;
803 if (!r_particles.value) return; // LordHavoc: particles are optional
807 if (ent->trail_leftover < 0)
808 ent->trail_leftover = 0;
809 t += ent->trail_leftover;
810 ent->trail_leftover -= (cl.time - cl.oldtime);
814 contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
815 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
818 VectorSubtract (end, start, vec);
819 len = VectorNormalizeLength (vec);
822 speed = len / (nt - t);
824 bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
830 p->vel[0] = p->vel[1] = p->vel[2] = 0;
831 p->die = cl.time + 2;
835 case 0: // rocket trail
836 case 1: // grenade trail
840 p->texnum = bubbleparticletexture;
841 p->scale = lhrandom(1,2);
843 p->color = (rand()&3)+12;
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);
855 p->texnum = smokeparticletexture[rand()&7];
856 p->scale = lhrandom(8, 12);
857 p->alpha = 64 + (rand()&31);
858 p->color = (rand()&3)+12;
860 p->die = cl.time + 10000;
861 VectorCopy(start, p->org);
866 case 1: // smoke smoke
868 p->texnum = smokeparticletexture;
869 p->scale = lhrandom(6,9);
871 if (r_smokecolor.value)
872 p->color = r_smokecolor.value;
874 p->color = (rand()&3)+12;
876 p->die = cl.time + 1;
877 VectorCopy(start, p->org);
883 p->texnum = smokeparticletexture[rand()&7];
884 p->scale = lhrandom(6, 8);
886 p->color = (rand()&3)+68;
887 p->type = pt_bloodcloud;
888 p->die = cl.time + 9999;
889 for (j=0 ; j<3 ; j++)
891 p->vel[j] = (rand()&15)-8;
892 p->org[j] = start[j] + ((rand()&3)-2);
899 p->texnum = particletexture;
902 p->die = cl.time + 0.2; //5;
905 p->color = 52 + ((tracercount&4)<<1);
907 p->color = 230 + ((tracercount&4)<<1);
911 VectorCopy (start, p->org);
914 p->vel[0] = 30*vec[1];
915 p->vel[1] = 30*-vec[0];
919 p->vel[0] = 30*-vec[1];
920 p->vel[1] = 30*vec[0];
924 case 4: // slight blood
925 dec = 0.025f; // sparse trail
926 p->texnum = smokeparticletexture[rand()&7];
927 p->scale = lhrandom(6, 8);
929 p->color = (rand()&3)+68;
930 p->type = pt_fadespark2;
931 p->die = cl.time + 9999;
932 for (j=0 ; j<3 ; j++)
934 p->vel[j] = (rand()&15)-8;
935 p->org[j] = start[j] + ((rand()&3)-2);
939 case 6: // voor trail
940 dec = 0.05f; // sparse trail
941 p->texnum = smokeparticletexture[rand()&7];
942 p->scale = lhrandom(3, 5);
944 p->color = 9*16 + 8 + (rand()&3);
945 p->type = pt_fadespark2;
946 p->die = cl.time + 2;
947 for (j=0 ; j<3 ; j++)
949 p->vel[j] = (rand()&15)-8;
950 p->org[j] = start[j] + ((rand()&3)-2);
954 case 7: // Nehahra smoke tracer
956 p->texnum = smokeparticletexture[rand()&7];
957 p->scale = lhrandom(8, 12);
959 p->color = (rand()&3)+12;
961 p->die = cl.time + 10000;
962 for (j=0 ; j<3 ; j++)
963 p->org[j] = start[j] + ((rand()&3)-2);
969 VectorMA (start, dec, vec, start);
971 ent->trail_leftover = t - cl.time;
974 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
978 if (!r_particles.value) return; // LordHavoc: particles are optional
980 VectorSubtract (end, start, vec);
981 len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
982 VectorScale(vec, 3, vec);
985 particle(pt_smoke, color, particletexture, 8, 192, 99, start[0], start[1], start[2], 0, 0, 0);
986 VectorAdd (start, vec, start);
991 extern qboolean lighthalf;
998 extern cvar_t sv_gravity;
999 void R_CompleteLightPoint (vec3_t color, vec3_t p);
1001 void R_DrawParticles (void)
1005 float gravity, dvel, frametime, scale, scale2, minparticledist;
1007 vec3_t up, right, uprightangles, forward2, up2, right2, tempcolor;
1008 int activeparticles, maxparticle, j, k;
1010 // LordHavoc: early out condition
1014 VectorScale (vup, 1.5, up);
1015 VectorScale (vright, 1.5, right);
1017 uprightangles[0] = 0;
1018 uprightangles[1] = r_refdef.viewangles[1];
1019 uprightangles[2] = 0;
1020 AngleVectors (uprightangles, forward2, right2, up2);
1022 frametime = cl.time - cl.oldtime;
1023 gravity = frametime * sv_gravity.value;
1024 dvel = 1+4*frametime;
1026 minparticledist = DotProduct(r_refdef.vieworg, vpn) + 16.0f;
1028 activeparticles = 0;
1031 for (k = 0, p = particles;k < numparticles;k++, p++)
1033 if (p->die < cl.time)
1035 freeparticles[j++] = p;
1041 // LordHavoc: only render if not too close
1042 if (DotProduct(p->org, vpn) >= minparticledist)
1044 color24 = (byte *) &d_8to24table[(int)p->color];
1049 if (r_dynamicparticles.value)
1051 R_CompleteLightPoint(tempcolor, p->org);
1052 r = (r * (int) tempcolor[0]) >> 7;
1053 g = (g * (int) tempcolor[1]) >> 7;
1054 b = (b * (int) tempcolor[2]) >> 7;
1056 transpolybegin(p->texnum, 0, p->texnum, TPOLYTYPE_ALPHA);
1057 scale = p->scale * -0.5;scale2 = p->scale * 0.5;
1058 if (p->texnum == rainparticletexture) // rain streak
1060 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);
1061 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);
1062 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);
1063 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);
1067 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);
1068 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);
1069 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);
1070 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);
1075 p->org[0] += p->vel[0]*frametime;
1076 p->org[1] += p->vel[1]*frametime;
1077 p->org[2] += p->vel[2]*frametime;
1085 for (i=0 ; i<3 ; i++)
1090 for (i=0 ; i<2 ; i++)
1095 p->vel[2] -= gravity;
1097 // LordHavoc: for smoke trails
1099 p->scale += frametime * 6;
1100 p->alpha -= frametime * 128;
1105 if (cl.time > p->time2)
1107 p->time2 = cl.time + (rand() & 3) * 0.1;
1108 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1109 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1110 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1114 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1119 p->scale += frametime * 4;
1120 p->alpha -= frametime * 64;
1121 if (p->alpha < 1 || p->scale < 1)
1125 p->alpha -= frametime * 256;
1126 p->vel[2] -= gravity;
1131 p->alpha -= frametime * 512;
1132 p->vel[2] -= gravity;
1136 case pt_fallfadespark:
1137 p->alpha -= frametime * 256;
1138 p->vel[2] -= gravity;
1142 case pt_fallfadespark2:
1143 p->alpha -= frametime * 512;
1144 p->vel[2] -= gravity;
1149 p->alpha -= frametime * 512;
1154 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents == CONTENTS_EMPTY)
1156 p->vel[2] += gravity;
1157 if (p->vel[2] >= 200)
1158 p->vel[2] = lhrandom(130, 200);
1159 if (cl.time > p->time2)
1161 p->time2 = cl.time + lhrandom(0, 0.5);
1162 p->vel[0] = lhrandom(-32,32);
1163 p->vel[1] = lhrandom(-32,32);
1165 p->alpha -= frametime * 64;
1170 p->scale += frametime * 60;
1171 p->alpha -= frametime * 96;
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;