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 16384 // 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
28 pt_static, pt_grav, pt_blob, pt_blob2, pt_bulletsmoke, pt_smoke, pt_snow, pt_rain, pt_spark, pt_bubble, pt_fade, pt_steam, pt_splash, pt_splashpuff, pt_flame, pt_blood, pt_oneframe, pt_lavasplash, pt_raindropsplash, pt_underwaterspark, pt_explosionsplash
38 typedef struct particle_s
43 particletexture_t *tex;
47 float time2; // used for various things (snow fluttering, for example)
48 float bounce; // how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide, 2 = keep bouncing forever, 1.5 is typical)
50 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
51 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
52 float pressure; // if non-zero, apply pressure to other particles
53 int dynlight; // if set the particle will be dynamically lit (if r_dynamicparticles is on), used for smoke and blood
54 int rendermode; // a TPOLYTYPE_ value
59 static int particlepalette[256] =
61 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
62 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
63 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
64 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
65 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
66 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
67 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
68 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
69 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
70 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
71 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
72 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
73 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
74 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
75 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
76 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
77 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
78 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
79 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
80 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
81 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
82 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
83 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
84 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
85 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
86 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
87 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
88 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
89 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
90 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
91 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
92 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
95 static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
96 //static int explounderwatersparkramp[8] = {0x00074b, 0x000f6f, 0x071f93, 0x0f33b7, 0x2b63cf, 0x4f97e3, 0xb5e7ff, 0xffffff};
98 static rtexture_t *particlefonttexture;
100 static particletexture_t particletexture;
101 static particletexture_t smokeparticletexture[8];
102 static particletexture_t rainparticletexture;
103 static particletexture_t bubbleparticletexture;
104 static particletexture_t bulletholetexture[8];
105 static particletexture_t rocketglowparticletexture;
106 static particletexture_t raindropsplashparticletexture[16];
108 static particle_t *particles;
109 static int r_numparticles;
111 static int numparticles;
112 static particle_t **freeparticles; // list used only in compacting particles array
114 static cvar_t r_particles = {CVAR_SAVE, "r_particles", "1"};
115 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
116 static cvar_t r_particles_lighting = {CVAR_SAVE, "r_particles_lighting", "1"};
117 static cvar_t r_particles_bloodshowers = {CVAR_SAVE, "r_particles_bloodshowers", "1"};
118 static cvar_t r_particles_blood = {CVAR_SAVE, "r_particles_blood", "1"};
119 static cvar_t r_particles_smoke = {CVAR_SAVE, "r_particles_smoke", "1"};
120 static cvar_t r_particles_sparks = {CVAR_SAVE, "r_particles_sparks", "1"};
121 static cvar_t r_particles_bubbles = {CVAR_SAVE, "r_particles_bubbles", "1"};
122 static cvar_t r_particles_explosions = {CVAR_SAVE, "r_particles_explosions", "0"};
124 static byte shadebubble(float dx, float dy, vec3_t light)
128 dz = 1 - (dx*dx+dy*dy);
129 if (dz > 0) // it does hit the sphere
133 normal[0] = dx;normal[1] = dy;normal[2] = dz;
134 VectorNormalize(normal);
135 dot = DotProduct(normal, light);
136 if (dot > 0.5) // interior reflection
137 f += ((dot * 2) - 1);
138 else if (dot < -0.5) // exterior reflection
139 f += ((dot * -2) - 1);
141 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
142 VectorNormalize(normal);
143 dot = DotProduct(normal, light);
144 if (dot > 0.5) // interior reflection
145 f += ((dot * 2) - 1);
146 else if (dot < -0.5) // exterior reflection
147 f += ((dot * -2) - 1);
149 f += 16; // just to give it a haze so you can see the outline
150 f = bound(0, f, 255);
157 static void R_InitParticleTexture (void)
159 int x,y,d,i,m, texnum;
160 float dx, dy, radius, f, f2;
161 byte data[32][32][4], noise1[64][64], noise2[64][64];
163 byte particletexturedata[256][256][4];
165 memset(&particletexturedata[0][0][0], 255, sizeof(particletexturedata));
167 #define SETUPTEX(var)\
169 int basex, basey, y;\
172 Sys_Error("R_InitParticleTexture: ran out of textures (64)\n");\
173 return; /* only to hush compiler */ \
175 basex = (texnum & 7) * 32;\
176 basey = ((texnum >> 3) & 7) * 32;\
177 var.s1 = (basex + 1) / 256.0f;\
178 var.t1 = (basey + 1) / 256.0f;\
179 var.s2 = (basex + 31) / 256.0f;\
180 var.t2 = (basey + 31) / 256.0f;\
181 for (y = 0;y < 32;y++)\
182 memcpy(&particletexturedata[basey + y][basex][0], &data[y][0][0], 32*4);\
186 for (y = 0;y < 32;y++)
189 for (x = 0;x < 32;x++)
191 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
193 d = (256 - (dx*dx+dy*dy));
194 d = bound(0, d, 255);
195 data[y][x][3] = (byte) d;
198 SETUPTEX(particletexture)
199 // particletexture = R_LoadTexture ("particletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
201 for (i = 0;i < 8;i++)
205 fractalnoise(&noise1[0][0], 64, 4);
206 fractalnoise(&noise2[0][0], 64, 8);
208 for (y = 0;y < 32;y++)
211 for (x = 0;x < 32;x++)
213 d = (noise1[y][x] - 128) * 2 + 64; // was + 128
214 d = bound(0, d, 255);
215 data[y][x][0] = data[y][x][1] = data[y][x][2] = d;
217 d = (noise2[y][x] - 128) * 3 + 192;
219 d = (d * (256 - (int) (dx*dx+dy*dy))) >> 8;
220 d = bound(0, d, 255);
221 data[y][x][3] = (byte) d;
229 SETUPTEX(smokeparticletexture[i])
230 // smokeparticletexture[i] = R_LoadTexture (va("smokeparticletexture%02d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
233 light[0] = 1;light[1] = 1;light[2] = 1;
234 VectorNormalize(light);
235 for (y = 0;y < 32;y++)
237 for (x = 0;x < 32;x++)
239 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
240 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);
243 SETUPTEX(rainparticletexture)
244 // rainparticletexture = R_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
246 light[0] = 1;light[1] = 1;light[2] = 1;
247 VectorNormalize(light);
248 for (y = 0;y < 32;y++)
250 for (x = 0;x < 32;x++)
252 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
253 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
256 SETUPTEX(bubbleparticletexture)
257 // bubbleparticletexture = R_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
259 for (i = 0;i < 8;i++)
262 fractalnoise(&noise1[0][0], 64, 8);
263 for (y = 0;y < 32;y++)
264 for (x = 0;x < 32;x++)
265 p[y][x] = (noise1[y][x] / 8.0f) - 64.0f;
266 for (m = 0;m < 32;m++)
270 fx = lhrandom(14, 18);
271 fy = lhrandom(14, 18);
274 dx = lhrandom(-1, 1);
275 dy = lhrandom(-1, 1);
276 f = (dx * dx + dy * dy);
278 while(f < 0.125f || f > 1.0f);
279 f = (m + 1) / 40.0f; //lhrandom(0.0f, 1.0);
282 for (j = 0;f > 0 && j < (32 * 14);j++)
288 p[y - 1][x - 1] += f * 0.125f;
289 p[y - 1][x ] += f * 0.25f;
290 p[y - 1][x + 1] += f * 0.125f;
291 p[y ][x - 1] += f * 0.25f;
293 p[y ][x + 1] += f * 0.25f;
294 p[y + 1][x - 1] += f * 0.125f;
295 p[y + 1][x ] += f * 0.25f;
296 p[y + 1][x + 1] += f * 0.125f;
297 // f -= (0.5f / (32 * 16));
300 for (y = 0;y < 32;y++)
302 for (x = 0;x < 32;x++)
305 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
306 data[y][x][3] = (byte) bound(0, m, 255);
310 SETUPTEX(bulletholetexture[i])
311 // bulletholetexture[i] = R_LoadTexture (va("bulletholetexture%02d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
314 for (y = 0;y < 32;y++)
317 for (x = 0;x < 32;x++)
320 d = (2048.0f / (dx*dx+dy*dy+1)) - 8.0f;
321 data[y][x][0] = bound(0, d * 1.0f, 255);
322 data[y][x][1] = bound(0, d * 0.8f, 255);
323 data[y][x][2] = bound(0, d * 0.5f, 255);
324 data[y][x][3] = bound(0, d * 1.0f, 255);
327 SETUPTEX(rocketglowparticletexture)
328 // rocketglowparticletexture = R_LoadTexture ("glowparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
330 for (i = 0;i < 16;i++)
332 radius = i * 3.0f / 16.0f;
333 f2 = 255.0f * ((15.0f - i) / 15.0f);
334 for (y = 0;y < 32;y++)
336 dy = (y - 16) * 0.25f;
337 for (x = 0;x < 32;x++)
339 dx = (x - 16) * 0.25f;
340 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
341 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
342 f = bound(0.0f, f, 255.0f);
343 data[y][x][3] = (int) f;
346 SETUPTEX(raindropsplashparticletexture[i])
347 // raindropsplashparticletexture[i] = R_LoadTexture (va("raindropslashparticletexture%02d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
350 particlefonttexture = R_LoadTexture ("particlefont", 256, 256, &particletexturedata[0][0][0], TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
353 static void r_part_start(void)
355 particles = (particle_t *) qmalloc(r_numparticles * sizeof(particle_t));
356 freeparticles = (void *) qmalloc(r_numparticles * sizeof(particle_t *));
358 R_InitParticleTexture ();
361 static void r_part_shutdown(void)
365 qfree(freeparticles);
368 static void r_part_newmap(void)
378 void R_ReadPointFile_f (void);
379 void R_Particles_Init (void)
383 i = COM_CheckParm ("-particles");
387 r_numparticles = (int)(atoi(com_argv[i+1]));
388 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
389 r_numparticles = ABSOLUTE_MIN_PARTICLES;
393 r_numparticles = MAX_PARTICLES;
396 Cmd_AddCommand ("pointfile", R_ReadPointFile_f);
398 Cvar_RegisterVariable (&r_particles);
399 Cvar_RegisterVariable (&r_drawparticles);
400 Cvar_RegisterVariable (&r_particles_lighting);
401 Cvar_RegisterVariable (&r_particles_bloodshowers);
402 Cvar_RegisterVariable (&r_particles_blood);
403 Cvar_RegisterVariable (&r_particles_smoke);
404 Cvar_RegisterVariable (&r_particles_sparks);
405 Cvar_RegisterVariable (&r_particles_bubbles);
406 Cvar_RegisterVariable (&r_particles_explosions);
408 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
411 //void particle(int ptype, int pcolor, int ptex, int prendermode, int plight, float pscale, float palpha, float ptime, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz)
412 #define particle(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz, ptime2, pvx2, pvy2, pvz2, pfriction, ppressure)\
416 if (numparticles >= r_numparticles)\
418 part = &particles[numparticles++];\
419 part->type = (ptype);\
420 tempcolor = (pcolor);\
421 part->color[0] = ((tempcolor) >> 16) & 0xFF;\
422 part->color[1] = ((tempcolor) >> 8) & 0xFF;\
423 part->color[2] = (tempcolor) & 0xFF;\
424 part->color[3] = 0xFF;\
425 part->tex = (&ptex);\
426 part->dynlight = (plight);\
427 part->rendermode = (prendermode);\
428 part->scale = (pscale);\
429 part->alpha = (palpha);\
430 part->die = cl.time + (ptime);\
431 part->bounce = (pbounce);\
432 part->org[0] = (px);\
433 part->org[1] = (py);\
434 part->org[2] = (pz);\
435 part->vel[0] = (pvx);\
436 part->vel[1] = (pvy);\
437 part->vel[2] = (pvz);\
438 part->time2 = (ptime2);\
439 part->vel2[0] = (pvx2);\
440 part->vel2[1] = (pvy2);\
441 part->vel2[2] = (pvz2);\
442 part->friction = (pfriction);\
443 part->pressure = (ppressure);\
451 void R_EntityParticles (entity_t *ent)
455 float sp, sy, cp, cy;
459 static vec3_t avelocities[NUMVERTEXNORMALS];
460 if (!r_particles.value) return; // LordHavoc: particles are optional
465 if (!avelocities[0][0])
466 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
467 avelocities[0][i] = (rand()&255) * 0.01;
469 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
471 angle = cl.time * avelocities[i][0];
474 angle = cl.time * avelocities[i][1];
482 particle(pt_oneframe, particlepalette[0x6f], particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 9999, 0, ent->render.origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->render.origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->render.origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0, 0, 0, 0, 0, 0);
487 void R_ReadPointFile_f (void)
493 char name[MAX_OSPATH];
495 sprintf (name,"maps/%s.pts", sv.name);
497 COM_FOpenFile (name, &f, false, true);
500 Con_Printf ("couldn't open %s\n", name);
504 Con_Printf ("Reading %s...\n", name);
508 char *str = Qgetline (f);
509 r = sscanf (str,"%f %f %f\n", &org[0], &org[1], &org[2]);
514 if (numparticles >= r_numparticles)
516 Con_Printf ("Not enough free particles\n");
519 particle(pt_static, particlepalette[(-c)&15], particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 99999, 0, org[0], org[1], org[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
523 Con_Printf ("%i points read\n", c);
528 R_ParseParticleEffect
530 Parse an effect out of the server message
533 void R_ParseParticleEffect (void)
536 int i, count, msgcount, color;
538 for (i=0 ; i<3 ; i++)
539 org[i] = MSG_ReadCoord ();
540 for (i=0 ; i<3 ; i++)
541 dir[i] = MSG_ReadChar () * (1.0/16);
542 msgcount = MSG_ReadByte ();
543 color = MSG_ReadByte ();
550 R_RunParticleEffect (org, dir, color, count);
559 void R_ParticleExplosion (vec3_t org, int smoke)
564 byte noise1[32*32], noise2[32*32];
566 if (r_particles.value && r_particles_explosions.value)
568 i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
569 if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
571 for (i = 0;i < 128;i++)
572 particle(pt_bubble, 0xFFFFFF, bubbleparticletexture, TPOLYTYPE_ALPHA, false, lhrandom(1, 2), 255, 9999, 1.5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-96, 96), lhrandom(-96, 96), lhrandom(-96, 96), 0, 0, 0, 0, 0, 0);
574 ang[2] = lhrandom(0, 360);
575 fractalnoise(noise1, 32, 4);
576 fractalnoise(noise2, 32, 8);
577 for (i = 0;i < 32;i++)
579 for (j = 0;j < 32;j++)
582 VectorMA(org, 16, v, v);
583 TraceLine(org, v, end, NULL, 0);
584 ang[0] = (j + 0.5f) * (360.0f / 32.0f);
585 ang[1] = (i + 0.5f) * (360.0f / 32.0f);
586 AngleVectors(ang, v, NULL, NULL);
587 f = noise1[j*32+i] * 1.5f;
588 VectorScale(v, f, v);
589 particle(pt_underwaterspark, noise2[j*32+i] * 0x010101, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2], 512.0f, 0, 0, 0, 2, 0);
590 VectorScale(v, 0.75, v);
591 particle(pt_underwaterspark, explosparkramp[(noise2[j*32+i] >> 5)], particletexture, TPOLYTYPE_ALPHA, false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2], 512.0f, 0, 0, 0, 2, 0);
597 ang[2] = lhrandom(0, 360);
598 fractalnoise(noise1, 32, 4);
599 fractalnoise(noise2, 32, 8);
600 for (i = 0;i < 32;i++)
602 for (j = 0;j < 32;j++)
605 VectorMA(org, 16, v, v);
606 TraceLine(org, v, end, NULL, 0);
607 ang[0] = (j + 0.5f) * (360.0f / 32.0f);
608 ang[1] = (i + 0.5f) * (360.0f / 32.0f);
609 AngleVectors(ang, v, NULL, NULL);
610 f = noise1[j*32+i] * 1.5f;
611 VectorScale(v, f, v);
612 particle(pt_spark, noise2[j*32+i] * 0x010101, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2] + 160.0f, 512.0f, 0, 0, 0, 2, 0);
613 VectorScale(v, 0.75, v);
614 particle(pt_spark, explosparkramp[(noise2[j*32+i] >> 5)], particletexture, TPOLYTYPE_ALPHA, false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2] + 160.0f, 512.0f, 0, 0, 0, 2, 0);
616 // VectorScale(v, 384, v);
617 // particle(pt_spark, explosparkramp[rand()&7], particletexture, TPOLYTYPE_ALPHA, false, 2, lhrandom(16, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2] + 160.0f, 512.0f, 0, 0, 0, 2, 0);
632 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
635 if (!r_particles.value) return; // LordHavoc: particles are optional
637 for (i = 0;i < 512;i++)
638 particle(pt_fade, particlepalette[colorStart + (i % colorLength)], particletexture, TPOLYTYPE_ALPHA, false, 1.5, 255, 0.3, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192), 0, 0, 0, 0, 0.1f, 0);
647 void R_BlobExplosion (vec3_t org)
650 if (!r_particles.value) return; // LordHavoc: particles are optional
652 for (i = 0;i < 256;i++)
653 particle(pt_blob , particlepalette[ 66+(rand()%6)], particletexture, TPOLYTYPE_ALPHA, false, 4, 255, 9999, 0, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(-128, 128), 0, 0, 0, 0, 0, 0);
654 for (i = 0;i < 256;i++)
655 particle(pt_blob2, particlepalette[150+(rand()%6)], particletexture, TPOLYTYPE_ALPHA, false, 4, 255, 9999, 0, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(-128, 128), 0, 0, 0, 0, 0, 0);
664 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
666 if (!r_particles.value) return; // LordHavoc: particles are optional
670 R_ParticleExplosion(org, false);
674 particle(pt_fade, particlepalette[color + (rand()&7)], particletexture, TPOLYTYPE_ALPHA, false, 1, 128, 9999, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-15, 15), lhrandom(-15, 15), lhrandom(-15, 15), 0, 0, 0, 0, 0, 0);
677 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
683 void R_SparkShower (vec3_t org, vec3_t dir, int count)
685 particletexture_t *tex;
686 if (!r_particles.value) return; // LordHavoc: particles are optional
688 tex = &bulletholetexture[rand()&7];
689 R_Decal(org, particlefonttexture, tex->s1, tex->t1, tex->s2, tex->t2, 16, 0, 0, 0, 255);
692 if (r_particles_smoke.value)
693 particle(pt_bulletsmoke, 0xA0A0A0, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 5, 255, 9999, 0, org[0], org[1], org[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 16), 0, 0, 0, 0, 0, 0);
695 if (r_particles_sparks.value)
699 particle(pt_spark, particlepalette[0x68 + (rand() & 7)], particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(0, 255), 9999, 1.5, org[0], org[1], org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(0, 128), 512.0f, 0, 0, 0, 0.2f, 0);
703 void R_BloodPuff (vec3_t org, vec3_t vel, int count)
705 // bloodcount is used to accumulate counts too small to cause a blood particle
706 static int bloodcount = 0;
707 if (!r_particles.value) return; // LordHavoc: particles are optional
708 if (!r_particles_blood.value) return;
713 while(bloodcount >= 10)
715 particle(pt_blood, 0x300000, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 24, 255, 9999, -1, org[0], org[1], org[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64), 0, 0, 0, 0, 1.0f, 0);
720 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
725 if (!r_particles.value) return; // LordHavoc: particles are optional
726 if (!r_particles_bloodshowers.value) return;
727 if (!r_particles_blood.value) return;
729 VectorSubtract(maxs, mins, diff);
730 center[0] = (mins[0] + maxs[0]) * 0.5;
731 center[1] = (mins[1] + maxs[1]) * 0.5;
732 center[2] = (mins[2] + maxs[2]) * 0.5;
733 // FIXME: change velspeed back to 2.0x after fixing mod
734 velscale[0] = velspeed * 2.0 / diff[0];
735 velscale[1] = velspeed * 2.0 / diff[1];
736 velscale[2] = velspeed * 2.0 / diff[2];
741 org[0] = lhrandom(mins[0], maxs[0]);
742 org[1] = lhrandom(mins[1], maxs[1]);
743 org[2] = lhrandom(mins[2], maxs[2]);
744 vel[0] = (org[0] - center[0]) * velscale[0];
745 vel[1] = (org[1] - center[1]) * velscale[1];
746 vel[2] = (org[2] - center[2]) * velscale[2];
747 particle(pt_blood, 0x300000, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 24, 255, 9999, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 0, 0, 0, 0, 1.0f, 0);
751 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
754 if (!r_particles.value) return; // LordHavoc: particles are optional
755 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
756 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
757 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
760 particle(gravity ? pt_grav : pt_static, particlepalette[colorbase + (rand()&3)], particletexture, TPOLYTYPE_ALPHA, false, 2, 255, lhrandom(1, 2), 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0] + lhrandom(-randomvel, randomvel), dir[1] + lhrandom(-randomvel, randomvel), dir[2] + lhrandom(-randomvel, randomvel), 0, 0, 0, 0, 0, 0);
763 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
767 if (!r_particles.value) return; // LordHavoc: particles are optional
768 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
769 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
770 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
771 if (dir[2] < 0) // falling
773 t = (maxs[2] - mins[2]) / -dir[2];
778 t = (maxs[2] - mins[2]) / dir[2];
781 if (t < 0 || t > 2) // sanity check
789 vel[0] = dir[0] + lhrandom(-16, 16);
790 vel[1] = dir[1] + lhrandom(-16, 16);
791 vel[2] = dir[2] + lhrandom(-32, 32);
792 particle(pt_rain, particlepalette[colorbase + (rand()&3)], rainparticletexture, TPOLYTYPE_ALPHA, true, 3, 255, t, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), z, vel[0], vel[1], vel[2], 0, vel[0], vel[1], vel[2], 0, 0);
798 vel[0] = dir[0] + lhrandom(-16, 16);
799 vel[1] = dir[1] + lhrandom(-16, 16);
800 vel[2] = dir[2] + lhrandom(-32, 32);
801 particle(pt_snow, particlepalette[colorbase + (rand()&3)], particletexture, TPOLYTYPE_ALPHA, false, 2, 255, t, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), z, vel[0], vel[1], vel[2], 0, vel[0], vel[1], vel[2], 0, 0);
805 Host_Error("R_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
809 void R_FlameCube (vec3_t mins, vec3_t maxs, int count)
812 if (!r_particles.value) return; // LordHavoc: particles are optional
813 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
814 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
815 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
818 particle(pt_flame, particlepalette[224 + (rand()&15)], smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, false, 8, 255, 9999, 1.1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(-32, 64), 0, 0, 0, 0, 0.1f, 0);
821 void R_Flames (vec3_t org, vec3_t vel, int count)
823 if (!r_particles.value) return; // LordHavoc: particles are optional
826 particle(pt_flame, particlepalette[224 + (rand()&15)], smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, false, 8, 255, 9999, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 0, 0, 0, 0, 0.1f, 0);
837 void R_LavaSplash (vec3_t origin)
842 if (!r_particles.value) return; // LordHavoc: particles are optional
844 for (i=-128 ; i<128 ; i+=16)
846 for (j=-128 ; j<128 ; j+=16)
848 dir[0] = j + lhrandom(0, 8);
849 dir[1] = i + lhrandom(0, 8);
851 org[0] = origin[0] + dir[0];
852 org[1] = origin[1] + dir[1];
853 org[2] = origin[2] + lhrandom(0, 64);
854 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
855 particle(pt_lavasplash, particlepalette[224 + (rand()&7)], particletexture, TPOLYTYPE_ALPHA, false, 7, 255, 9999, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, 0, 0);
866 void R_TeleportSplash (vec3_t org)
869 if (!r_particles.value) return; // LordHavoc: particles are optional
871 for (i=-16 ; i<16 ; i+=8)
872 for (j=-16 ; j<16 ; j+=8)
873 for (k=-24 ; k<32 ; k+=8)
874 particle(pt_fade, 0xFFFFFF, particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(64, 128), 9999, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), i*2 + lhrandom(-12.5, 12.5), j*2 + lhrandom(-12.5, 12.5), k*2 + lhrandom(27.5, 52.5), 0, 0, 0, 0, 0.1f, -512.0f);
877 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
879 vec3_t vec, dir, vel;
880 float len, dec = 0, speed;
881 int contents, bubbles, polytype;
883 if (!r_particles.value) return; // LordHavoc: particles are optional
885 VectorSubtract(end, start, dir);
886 VectorNormalize(dir);
888 if (type == 0 && host_frametime != 0) // rocket glow
889 particle(pt_oneframe, 0xFFFFFF, rocketglowparticletexture, TPOLYTYPE_ALPHA, false, 24, 255, 9999, 0, end[0] - 12 * dir[0], end[1] - 12 * dir[1], end[2] - 12 * dir[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
891 t = ent->persistent.trail_time;
893 return; // no particles to spawn this frame (sparse trail)
898 VectorSubtract (end, start, vec);
899 len = VectorNormalizeLength (vec);
902 // advance the trail time
903 ent->persistent.trail_time = cl.time;
906 speed = len / (cl.time - cl.oldtime);
907 VectorScale(vec, speed, vel);
909 // advance into this frame to reach the first puff location
910 dec = t - cl.oldtime;
912 VectorMA(start, dec, vec, start);
914 contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
915 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
917 // advance the trail time
918 ent->persistent.trail_time = cl.time;
922 bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
924 polytype = TPOLYTYPE_ALPHA;
925 if (ent->render.effects & EF_ADDITIVE)
926 polytype = TPOLYTYPE_ADD;
932 case 0: // rocket trail
933 if (!r_particles_smoke.value)
935 else if (bubbles && r_particles_bubbles.value)
938 particle(pt_bubble, 0xFFFFFF, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
939 particle(pt_bubble, 0xFFFFFF, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
940 particle(pt_smoke, 0xFFFFFF, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
945 particle(pt_smoke, 0xC0C0C0, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
946 //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
947 //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
948 //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
949 //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
953 case 1: // grenade trail
954 // FIXME: make it gradually stop smoking
955 if (!r_particles_smoke.value)
957 else if (bubbles && r_particles_bubbles.value)
960 particle(pt_bubble, 0xFFFFFF, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
961 particle(pt_bubble, 0xFFFFFF, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
962 particle(pt_smoke, 0xFFFFFF, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
967 particle(pt_smoke, 0x808080, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
973 if (!r_particles_blood.value)
978 particle(pt_blood, 0x300000, smokeparticletexture[rand()&7], polytype, true, 24, 255, 9999, -1, start[0], start[1], start[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64), 0, 0, 0, 0, 1.0f, 0);
982 case 4: // slight blood
983 if (!r_particles_blood.value)
988 particle(pt_blood, 0x300000, smokeparticletexture[rand()&7], polytype, true, 24, 255, 9999, -1, start[0], start[1], start[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64), 0, 0, 0, 0, 1.0f, 0);
992 case 3: // green tracer
994 particle(pt_fade, 0x373707, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
997 case 5: // flame tracer
999 particle(pt_fade, 0xCF632B, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1002 case 6: // voor trail
1003 dec = 0.05f; // sparse trail
1004 particle(pt_fade, 0x47232B, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1007 case 7: // Nehahra smoke tracer
1008 if (!r_particles_smoke.value)
1013 particle(pt_smoke, 0xC0C0C0, smokeparticletexture[rand()&7], polytype, true, 10, 64, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1018 // advance to next time and position
1021 VectorMA (start, dec, vec, start);
1023 ent->persistent.trail_time = t;
1026 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1030 if (!r_particles.value) return; // LordHavoc: particles are optional
1031 if (!r_particles_smoke.value) return;
1033 VectorSubtract (end, start, vec);
1034 len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
1035 VectorScale(vec, 3, vec);
1036 color = particlepalette[color];
1039 particle(pt_smoke, color, particletexture, TPOLYTYPE_ALPHA, false, 8, 192, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1040 VectorAdd (start, vec, start);
1050 void R_MoveParticles (void)
1053 int i, activeparticles, maxparticle, j, a, b, pressureused = false;
1054 vec3_t v, org, o, normal;
1055 float gravity, dvel, frametime, f;
1057 // LordHavoc: early out condition
1061 frametime = cl.time - cl.oldtime;
1063 return; // if absolutely still, don't update particles
1064 gravity = frametime * sv_gravity.value;
1065 dvel = 1+4*frametime;
1067 activeparticles = 0;
1070 for (i = 0, p = particles;i < numparticles;i++, p++)
1072 if (p->die < cl.time)
1074 freeparticles[j++] = p;
1078 VectorCopy(p->org, p->oldorg);
1079 VectorMA(p->org, frametime, p->vel, p->org);
1082 f = 1.0f - (p->friction * frametime);
1083 VectorScale(p->vel, f, p->vel);
1085 VectorCopy(p->org, org);
1090 if (TraceLine(p->oldorg, p->org, v, normal, 0) < 1)
1092 VectorCopy(v, p->org);
1095 R_Decal(v, particlefonttexture, p->tex->s1, p->tex->t1, p->tex->s2, p->tex->t2, p->scale, p->color[0], p->color[1], p->color[2], p->alpha);
1097 freeparticles[j++] = p;
1102 dist = DotProduct(p->vel, normal) * -p->bounce;
1103 VectorMA(p->vel, dist, normal, p->vel);
1104 if (DotProduct(p->vel, p->vel) < 0.03)
1105 VectorClear(p->vel);
1115 // LordHavoc: drop-through because of shared code
1121 p->alpha -= frametime * 256;
1127 p->vel[2] -= gravity;
1130 p->vel[2] -= gravity * 0.05;
1131 p->alpha -= frametime * 192;
1136 if (cl.time > p->time2)
1138 p->time2 = cl.time + (rand() & 3) * 0.1;
1139 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1140 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1141 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1143 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1144 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1147 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1148 break; // still in solid
1149 p->die = cl.time + 1000;
1150 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1154 case CONTENTS_SLIME:
1155 p->tex = &smokeparticletexture[rand()&7];
1161 case CONTENTS_WATER:
1162 p->tex = &smokeparticletexture[rand()&7];
1163 p->type = pt_splash;
1168 default: // CONTENTS_SOLID and any others
1169 TraceLine(p->oldorg, p->org, v, normal, 0);
1170 VectorCopy(v, p->org);
1171 p->tex = &smokeparticletexture[rand()&7];
1173 VectorClear(p->vel);
1180 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1181 if (a != CONTENTS_EMPTY)
1183 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1186 p->scale += frametime * 32.0f;
1187 p->alpha -= frametime * 128.0f;
1188 p->vel[2] += gravity * 0.125f;
1199 p->vel[2] -= gravity * 0.5;
1202 p->alpha -= frametime * p->time2;
1203 p->vel[2] -= gravity;
1206 else if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1207 p->type = pt_underwaterspark;
1209 case pt_underwaterspark:
1210 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents == CONTENTS_EMPTY)
1212 p->tex = &smokeparticletexture[rand()&7];
1213 p->color[0] = p->color[1] = p->color[2] = 255;
1215 p->type = pt_explosionsplash;
1218 p->vel[2] += gravity * 0.5f;
1219 p->alpha -= frametime * p->time2;
1223 case pt_explosionsplash:
1224 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents == CONTENTS_EMPTY)
1225 p->vel[2] -= gravity;
1228 p->scale += frametime * 64.0f;
1229 p->alpha -= frametime * 1024.0f;
1234 p->alpha -= frametime * 512;
1239 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1240 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1242 p->tex = &smokeparticletexture[rand()&7];
1243 p->type = pt_splashpuff;
1245 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1248 p->vel[2] += gravity * 0.25;
1249 p->vel[0] *= (1 - (frametime * 0.0625));
1250 p->vel[1] *= (1 - (frametime * 0.0625));
1251 p->vel[2] *= (1 - (frametime * 0.0625));
1252 if (cl.time > p->time2)
1254 p->time2 = cl.time + lhrandom(0, 0.5);
1255 p->vel[0] += lhrandom(-32,32);
1256 p->vel[1] += lhrandom(-32,32);
1257 p->vel[2] += lhrandom(-32,32);
1259 p->alpha -= frametime * 256;
1263 case pt_bulletsmoke:
1264 p->scale += frametime * 16;
1265 p->alpha -= frametime * 1024;
1266 p->vel[2] += gravity * 0.1;
1271 p->scale += frametime * 24;
1272 p->alpha -= frametime * 256;
1273 p->vel[2] += gravity * 0.1;
1278 p->scale += frametime * 48;
1279 p->alpha -= frametime * 512;
1280 p->vel[2] += gravity * 0.05;
1285 p->alpha -= frametime * 1024;
1291 b = Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents;
1292 VectorCopy(p->oldorg, o);
1296 f = TraceLine(o, p->org, v, normal, a);
1297 b = traceline_endcontents;
1298 if (f < 1 && b != CONTENTS_EMPTY && b != CONTENTS_SKY)
1300 p->die = cl.time + 1000;
1301 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1302 VectorCopy(v, p->org);
1306 case CONTENTS_SLIME:
1307 p->tex = &smokeparticletexture[rand()&7];
1312 default: // water, solid, and anything else
1313 p->tex = &raindropsplashparticletexture[0];
1315 VectorCopy(normal, p->vel2);
1316 // VectorAdd(p->org, normal, p->org);
1317 p->type = pt_raindropsplash;
1324 case pt_raindropsplash:
1325 p->time2 += frametime * 64.0f;
1326 if (p->time2 >= 16.0f)
1331 p->tex = &raindropsplashparticletexture[(int) p->time2];
1334 p->alpha -= frametime * 512;
1335 p->vel[2] += gravity;
1345 printf("unknown particle type %i\n", p->type);
1350 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1351 if (p->die < cl.time)
1352 freeparticles[j++] = p;
1358 pressureused = true;
1361 // fill in gaps to compact the array
1363 while (maxparticle >= activeparticles)
1365 *freeparticles[i++] = particles[maxparticle--];
1366 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1369 numparticles = activeparticles;
1373 activeparticles = 0;
1374 for (i = 0, p = particles;i < numparticles;i++, p++)
1376 freeparticles[activeparticles++] = p;
1378 if (activeparticles)
1380 for (i = 0, p = particles;i < numparticles;i++, p++)
1382 for (j = 0;j < activeparticles;j++)
1384 if (freeparticles[j] != p)
1386 float dist, diff[3];
1387 VectorSubtract(p->org, freeparticles[j]->org, diff);
1388 dist = DotProduct(diff, diff);
1389 if (dist < 4096 && dist >= 1)
1391 dist = freeparticles[j]->scale * 4.0f * frametime / sqrt(dist);
1392 VectorMA(p->vel, dist, diff, p->vel);
1393 //dist = freeparticles[j]->scale * 4.0f * frametime / dist;
1394 //VectorMA(p->vel, dist, freeparticles[j]->vel, p->vel);
1403 void R_DrawParticles (void)
1406 int i, dynamiclight, staticlight, r, g, b, texnum;
1407 float minparticledist;
1408 vec3_t uprightangles, up2, right2, v, right, up;
1411 // LordHavoc: early out condition
1412 if ((!numparticles) || (!r_drawparticles.value))
1415 staticlight = dynamiclight = r_particles_lighting.value;
1416 if (!r_dynamic.value)
1418 c_particles += numparticles;
1420 uprightangles[0] = 0;
1421 uprightangles[1] = r_refdef.viewangles[1];
1422 uprightangles[2] = 0;
1423 AngleVectors (uprightangles, NULL, right2, up2);
1425 minparticledist = DotProduct(r_origin, vpn) + 16.0f;
1427 texnum = R_GetTexture(particlefonttexture);
1428 for (i = 0, p = particles;i < numparticles;i++, p++)
1430 if (p->tex == NULL || p->alpha < 1 || p->scale < 0.1f)
1433 // LordHavoc: only render if not too close
1434 if (DotProduct(p->org, vpn) < minparticledist)
1437 // LordHavoc: check if it's in a visible leaf
1438 leaf = Mod_PointInLeaf(p->org, cl.worldmodel);
1439 if (leaf->visframe != r_framecount)
1445 if (staticlight && (p->dynlight || staticlight >= 2)) // LordHavoc: only light blood and smoke
1447 R_CompleteLightPoint(v, p->org, dynamiclight, leaf);
1449 r = (r * (int) v[0]) >> 7;
1450 g = (g * (int) v[1]) >> 7;
1451 b = (b * (int) v[2]) >> 7;
1456 r = (r * (*((long *) &v[0]) & 0x7FFFFF)) >> 7;
1457 g = (g * (*((long *) &v[1]) & 0x7FFFFF)) >> 7;
1458 b = (b * (*((long *) &v[2]) & 0x7FFFFF)) >> 7;
1461 if (p->type == pt_raindropsplash)
1463 // treat as double-sided
1464 if (DotProduct(p->vel2, r_origin) > DotProduct(p->vel2, p->org))
1466 VectorNegate(p->vel2, v);
1467 VectorVectors(v, right, up);
1470 VectorVectors(p->vel2, right, up);
1471 transpolyparticle(p->org, right, up, p->scale, texnum, p->rendermode, r, g, b, p->alpha, p->tex->s1, p->tex->t1, p->tex->s2, p->tex->t2);
1473 else if (p->tex == &rainparticletexture) // rain streak
1474 transpolyparticle(p->org, right2, up2, p->scale, texnum, p->rendermode, r, g, b, p->alpha, p->tex->s1, p->tex->t1, p->tex->s2, p->tex->t2);
1476 transpolyparticle(p->org, vright, vup, p->scale, texnum, p->rendermode, r, g, b, p->alpha, p->tex->s1, p->tex->t1, p->tex->s2, p->tex->t2);