]> icculus.org git repositories - divverent/darkplaces.git/blob - r_part.c
cleaned up nearly all of the externs in .c files (moved to appropriate .h files)
[divverent/darkplaces.git] / r_part.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.  
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20
21 #include "quakedef.h"
22
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
25
26 // LordHavoc: added dust, smoke, snow, bloodcloud, and many others
27 typedef enum
28 {
29         pt_static, pt_grav, pt_slowgrav, 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_decal*/, pt_blood, pt_oneframe, pt_lavasplash
30 }
31 ptype_t;
32
33 typedef struct particle_s
34 {
35         ptype_t         type;
36         vec3_t          org;
37         vec3_t          vel;
38         rtexture_t      *tex;
39         byte            dynlight; // if set the particle will be dynamically lit (if r_dynamicparticles is on), used for smoke and blood
40         byte            rendermode; // a TPOLYTYPE_ value
41         byte            color;
42         byte            pad2;
43         float           die;
44         float           scale;
45         float           alpha; // 0-255
46         float           time2; // used for various things (snow fluttering, for example)
47         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)
48         vec3_t          oldorg;
49         vec3_t          vel2; // used for snow fluttering (base velocity, wind for instance)
50 //      vec3_t          direction; // used by decals
51 //      vec3_t          decalright; // used by decals
52 //      vec3_t          decalup; // used by decals
53 }
54 particle_t;
55
56 float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);
57
58 int             ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
59 int             ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
60 int             ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
61
62 rtexture_t *particletexture;
63 rtexture_t *smokeparticletexture[8];
64 rtexture_t *rainparticletexture;
65 rtexture_t *bubbleparticletexture;
66 rtexture_t *bulletholetexture[8];
67 rtexture_t *rocketglowparticletexture;
68
69 particle_t      *particles;
70 int                     r_numparticles;
71
72 vec3_t                  r_pright, r_pup, r_ppn;
73
74 int                     numparticles;
75 particle_t      **freeparticles; // list used only in compacting particles array
76
77 cvar_t r_particles = {"r_particles", "1", true};
78 cvar_t r_drawparticles = {"r_drawparticles", "1"};
79 cvar_t r_particles_lighting = {"r_particles_lighting", "1", true};
80 cvar_t r_particles_bloodshowers = {"r_particles_bloodshowers", "1", true};
81 cvar_t r_particles_blood = {"r_particles_blood", "1", true};
82 cvar_t r_particles_smoke = {"r_particles_smoke", "1", true};
83 cvar_t r_particles_sparks = {"r_particles_sparks", "1", true};
84 cvar_t r_particles_bubbles = {"r_particles_bubbles", "1", true};
85
86 byte shadebubble(float dx, float dy, vec3_t light)
87 {
88         float   dz, f, dot;
89         vec3_t  normal;
90         dz = 1 - (dx*dx+dy*dy);
91         if (dz > 0) // it does hit the sphere
92         {
93                 f = 0;
94                 // back side
95                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
96                 VectorNormalize(normal);
97                 dot = DotProduct(normal, light);
98                 if (dot > 0.5) // interior reflection
99                         f += ((dot *  2) - 1);
100                 else if (dot < -0.5) // exterior reflection
101                         f += ((dot * -2) - 1);
102                 // front side
103                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
104                 VectorNormalize(normal);
105                 dot = DotProduct(normal, light);
106                 if (dot > 0.5) // interior reflection
107                         f += ((dot *  2) - 1);
108                 else if (dot < -0.5) // exterior reflection
109                         f += ((dot * -2) - 1);
110                 f *= 128;
111                 f += 16; // just to give it a haze so you can see the outline
112                 f = bound(0, f, 255);
113                 return (byte) f;
114         }
115         else
116                 return 0;
117 }
118
119 void R_InitParticleTexture (void)
120 {
121         int             x,y,d,i,m;
122         float   dx, dy;
123         byte    data[32][32][4], noise1[64][64], noise2[64][64];
124         vec3_t  light;
125
126         for (y = 0;y < 32;y++)
127         {
128                 dy = y - 16;
129                 for (x = 0;x < 32;x++)
130                 {
131                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
132                         dx = x - 16;
133                         d = (256 - (dx*dx+dy*dy));
134                         d = bound(0, d, 255);
135                         data[y][x][3] = (byte) d;
136                 }
137         }
138         particletexture = R_LoadTexture ("particletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
139
140         for (i = 0;i < 8;i++)
141         {
142                 do
143                 {
144                         fractalnoise(&noise1[0][0], 64, 4);
145                         fractalnoise(&noise2[0][0], 64, 8);
146                         m = 0;
147                         for (y = 0;y < 32;y++)
148                         {
149                                 dy = y - 16;
150                                 for (x = 0;x < 32;x++)
151                                 {
152                                         d = (noise1[y][x] - 128) * 2 + 128;
153                                         d = bound(0, d, 255);
154                                         data[y][x][0] = data[y][x][1] = data[y][x][2] = d;
155                                         dx = x - 16;
156                                         d = (noise2[y][x] - 128) * 4 + 128;
157                                         if (d > 0)
158                                                 d = (d * (256 - (int) (dx*dx+dy*dy))) >> 8;
159                                         d = bound(0, d, 255);
160                                         data[y][x][3] = (byte) d;
161                                         if (m < d)
162                                                 m = d;
163                                 }
164                         }
165                 }
166                 while (m < 224);
167
168                 smokeparticletexture[i] = R_LoadTexture (va("smokeparticletexture%d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
169         }
170
171         light[0] = 1;light[1] = 1;light[2] = 1;
172         VectorNormalize(light);
173         for (y = 0;y < 32;y++)
174         {
175                 for (x = 0;x < 32;x++)
176                 {
177                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
178                         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);
179                 }
180         }
181         rainparticletexture = R_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
182
183         light[0] = 1;light[1] = 1;light[2] = 1;
184         VectorNormalize(light);
185         for (y = 0;y < 32;y++)
186         {
187                 for (x = 0;x < 32;x++)
188                 {
189                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
190                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
191                 }
192         }
193         bubbleparticletexture = R_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
194
195         for (i = 0;i < 8;i++)
196         {
197                 float p[32][32];
198                 fractalnoise(&noise1[0][0], 64, 8);
199                 for (y = 0;y < 32;y++)
200                         for (x = 0;x < 32;x++)
201                                 p[y][x] = (noise1[y][x] / 8.0f) - 64.0f;
202                 for (m = 0;m < 32;m++)
203                 {
204                         int j;
205                         float fx, fy, f;
206                         fx = lhrandom(14, 18);
207                         fy = lhrandom(14, 18);
208                         do
209                         {
210                                 dx = lhrandom(-1, 1);
211                                 dy = lhrandom(-1, 1);
212                                 f = (dx * dx + dy * dy);
213                         }
214                         while(f < 0.125f || f > 1.0f);
215                         f = (m + 1) / 40.0f; //lhrandom(0.0f, 1.0);
216                         dx *= 1.0f / 32.0f;
217                         dy *= 1.0f / 32.0f;
218                         for (j = 0;f > 0 && j < (32 * 14);j++)
219                         {
220                                 y = fy;
221                                 x = fx;
222                                 fx += dx;
223                                 fy += dy;
224                                 p[y - 1][x - 1] += f * 0.125f;
225                                 p[y - 1][x    ] += f * 0.25f;
226                                 p[y - 1][x + 1] += f * 0.125f;
227                                 p[y    ][x - 1] += f * 0.25f;
228                                 p[y    ][x    ] += f;
229                                 p[y    ][x + 1] += f * 0.25f;
230                                 p[y + 1][x - 1] += f * 0.125f;
231                                 p[y + 1][x    ] += f * 0.25f;
232                                 p[y + 1][x + 1] += f * 0.125f;
233 //                              f -= (0.5f / (32 * 16));
234                         }
235                 }
236                 for (y = 0;y < 32;y++)
237                 {
238                         for (x = 0;x < 32;x++)
239                         {
240                                 m = p[y][x];
241                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
242                                 data[y][x][3] = (byte) bound(0, m, 255);
243                         }
244                 }
245
246                 bulletholetexture[i] = R_LoadTexture (va("bulletholetexture%d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
247         }
248
249         for (y = 0;y < 32;y++)
250         {
251                 dy = y - 16;
252                 for (x = 0;x < 32;x++)
253                 {
254                         dx = x - 16;
255                         d = (2048.0f / (dx*dx+dy*dy+1)) - 8.0f;
256                         data[y][x][0] = bound(0, d * 1.0f, 255);
257                         data[y][x][1] = bound(0, d * 0.8f, 255);
258                         data[y][x][2] = bound(0, d * 0.5f, 255);
259                         data[y][x][3] = bound(0, d * 1.0f, 255);
260                 }
261         }
262         rocketglowparticletexture = R_LoadTexture ("glowparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
263 }
264
265 void r_part_start(void)
266 {
267         particles = (particle_t *) qmalloc(r_numparticles * sizeof(particle_t));
268         freeparticles = (void *) qmalloc(r_numparticles * sizeof(particle_t *));
269         numparticles = 0;
270         R_InitParticleTexture ();
271 }
272
273 void r_part_shutdown(void)
274 {
275         numparticles = 0;
276         qfree(particles);
277         qfree(freeparticles);
278 }
279
280 void r_part_newmap(void)
281 {
282         numparticles = 0;
283 }
284
285 /*
286 ===============
287 R_InitParticles
288 ===============
289 */
290 void R_ReadPointFile_f (void);
291 void R_Particles_Init (void)
292 {
293         int             i;
294
295         i = COM_CheckParm ("-particles");
296
297         if (i)
298         {
299                 r_numparticles = (int)(atoi(com_argv[i+1]));
300                 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
301                         r_numparticles = ABSOLUTE_MIN_PARTICLES;
302         }
303         else
304         {
305                 r_numparticles = MAX_PARTICLES;
306         }
307
308         Cmd_AddCommand ("pointfile", R_ReadPointFile_f);        
309
310         Cvar_RegisterVariable (&r_particles);
311         Cvar_RegisterVariable (&r_drawparticles);
312         Cvar_RegisterVariable (&r_particles_lighting);
313         Cvar_RegisterVariable (&r_particles_bloodshowers);
314         Cvar_RegisterVariable (&r_particles_blood);
315         Cvar_RegisterVariable (&r_particles_smoke);
316         Cvar_RegisterVariable (&r_particles_sparks);
317         Cvar_RegisterVariable (&r_particles_bubbles);
318
319         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
320 }
321
322 //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)
323 #define particle(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz)\
324 {\
325         particle_t      *part;\
326         if (numparticles >= r_numparticles)\
327                 return;\
328         part = &particles[numparticles++];\
329         part->type = (ptype);\
330         part->color = (pcolor);\
331         part->tex = (ptex);\
332         part->dynlight = (plight);\
333         part->rendermode = (prendermode);\
334         part->scale = (pscale);\
335         part->alpha = (palpha);\
336         part->die = cl.time + (ptime);\
337         part->bounce = (pbounce);\
338         part->org[0] = (px);\
339         part->org[1] = (py);\
340         part->org[2] = (pz);\
341         part->vel[0] = (pvx);\
342         part->vel[1] = (pvy);\
343         part->vel[2] = (pvz);\
344         part->time2 = 0;\
345         part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
346 }
347 /*
348 #define particle2(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, poscale, pvscale)\
349 {\
350         particle_t      *part;\
351         if (numparticles >= r_numparticles)\
352                 return;\
353         part = &particles[numparticles++];\
354         part->type = (ptype);\
355         part->color = (pcolor);\
356         part->tex = (ptex);\
357         part->dynlight = (plight);\
358         part->rendermode = (prendermode);\
359         part->scale = (pscale);\
360         part->alpha = (palpha);\
361         part->die = cl.time + (ptime);\
362         part->bounce = (pbounce);\
363         part->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
364         part->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
365         part->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
366         part->vel[0] = lhrandom(-(pvscale), (pvscale));\
367         part->vel[1] = lhrandom(-(pvscale), (pvscale));\
368         part->vel[2] = lhrandom(-(pvscale), (pvscale));\
369         part->time2 = 0;\
370         part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
371 }
372 */
373 /*
374 #define particle3(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
375 {\
376         particle_t      *part;\
377         if (numparticles >= r_numparticles)\
378                 return;\
379         part = &particles[numparticles++];\
380         part->type = (ptype);\
381         part->color = (pcolor);\
382         part->tex = (ptex);\
383         part->dynlight = (plight);\
384         part->rendermode = (prendermode);\
385         part->scale = (pscale);\
386         part->alpha = (palpha);\
387         part->die = cl.time + (ptime);\
388         part->bounce = (pbounce);\
389         part->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
390         part->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
391         part->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
392         part->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
393         part->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
394         part->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
395         part->time2 = 0;\
396         part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
397 }
398 */
399 #define particle4(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz, ptime2, pvx2, pvy2, pvz2)\
400 {\
401         particle_t      *part;\
402         if (numparticles >= r_numparticles)\
403                 return;\
404         part = &particles[numparticles++];\
405         part->type = (ptype);\
406         part->color = (pcolor);\
407         part->tex = (ptex);\
408         part->dynlight = (plight);\
409         part->rendermode = (prendermode);\
410         part->scale = (pscale);\
411         part->alpha = (palpha);\
412         part->die = cl.time + (ptime);\
413         part->bounce = (pbounce);\
414         part->org[0] = (px);\
415         part->org[1] = (py);\
416         part->org[2] = (pz);\
417         part->vel[0] = (pvx);\
418         part->vel[1] = (pvy);\
419         part->vel[2] = (pvz);\
420         part->time2 = (ptime2);\
421         part->vel2[0] = (pvx2);\
422         part->vel2[1] = (pvy2);\
423         part->vel2[2] = (pvz2);\
424 }
425
426 /*
427 ===============
428 R_EntityParticles
429 ===============
430 */
431 void R_EntityParticles (entity_t *ent)
432 {
433         int                     i;
434         float           angle;
435         float           sp, sy, cp, cy;
436         vec3_t          forward;
437         float           dist;
438         float           beamlength;
439         static vec3_t avelocities[NUMVERTEXNORMALS];
440         if (!r_particles.value) return; // LordHavoc: particles are optional
441         
442         dist = 64;
443         beamlength = 16;
444
445         if (!avelocities[0][0])
446                 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
447                         avelocities[0][i] = (rand()&255) * 0.01;
448
449         for (i=0 ; i<NUMVERTEXNORMALS ; i++)
450         {
451                 angle = cl.time * avelocities[i][0];
452                 sy = sin(angle);
453                 cy = cos(angle);
454                 angle = cl.time * avelocities[i][1];
455                 sp = sin(angle);
456                 cp = cos(angle);
457         
458                 forward[0] = cp*cy;
459                 forward[1] = cp*sy;
460                 forward[2] = -sp;
461
462                 particle(pt_oneframe, 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);
463         }
464 }
465
466
467 void R_ReadPointFile_f (void)
468 {
469         FILE    *f;
470         vec3_t  org;
471         int             r;
472         int             c;
473         char    name[MAX_OSPATH];
474         
475         sprintf (name,"maps/%s.pts", sv.name);
476
477         COM_FOpenFile (name, &f, false);
478         if (!f)
479         {
480                 Con_Printf ("couldn't open %s\n", name);
481                 return;
482         }
483         
484         Con_Printf ("Reading %s...\n", name);
485         c = 0;
486         for (;;)
487         {
488                 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
489                 if (r != 3)
490                         break;
491                 c++;
492                 
493                 if (numparticles >= r_numparticles)
494                 {
495                         Con_Printf ("Not enough free particles\n");
496                         break;
497                 }
498                 particle(pt_static, (-c)&15, particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 99999, 0, org[0], org[1], org[2], 0, 0, 0);
499         }
500
501         fclose (f);
502         Con_Printf ("%i points read\n", c);
503 }
504
505 /*
506 ===============
507 R_ParseParticleEffect
508
509 Parse an effect out of the server message
510 ===============
511 */
512 void R_ParseParticleEffect (void)
513 {
514         vec3_t          org, dir;
515         int                     i, count, msgcount, color;
516         
517         for (i=0 ; i<3 ; i++)
518                 org[i] = MSG_ReadCoord ();
519         for (i=0 ; i<3 ; i++)
520                 dir[i] = MSG_ReadChar () * (1.0/16);
521         msgcount = MSG_ReadByte ();
522         color = MSG_ReadByte ();
523
524 if (msgcount == 255)
525         count = 1024;
526 else
527         count = msgcount;
528         
529         R_RunParticleEffect (org, dir, color, count);
530 }
531         
532 /*
533 ===============
534 R_ParticleExplosion
535
536 ===============
537 */
538 void R_ParticleExplosion (vec3_t org, int smoke)
539 {
540         int i;
541         if (!r_particles.value) return; // LordHavoc: particles are optional
542
543 //      particle(pt_smoke, (rand()&7) + 8, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 30, 255, 2, org[0], org[1], org[2], 0, 0, 0);
544
545         i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
546         if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
547         {
548                 for (i = 0;i < 128;i++)
549                         particle(pt_bubble, 254, bubbleparticletexture, TPOLYTYPE_ADD, 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));
550         }
551         else
552                 R_NewExplosion(org);
553         /*
554         else
555         {
556                 int j;
557 //              int color;
558                 float f, forg[3], fvel[3], fvel2[3];
559 //              for (i = 0;i < 256;i++)
560 //                      particle(pt_fallfadespark, ramp3[rand()%6], particletexture, TPOLYTYPE_ALPHA, false, 1.5, lhrandom(128, 255), 5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(0, 384));
561 //              for (i = 0;i < 256;i++)
562 //                      particle(pt_fallfadespark, ramp3[rand()%6], particletexture, TPOLYTYPE_ALPHA, false, 1.5, lhrandom(128, 255), 5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-150, 150), lhrandom(-150, 150), lhrandom(-150, 150));
563                 for (i = 0;i < 32;i++)
564                 {
565                         fvel[0] = lhrandom(-150, 150);
566                         fvel[1] = lhrandom(-150, 150);
567                         fvel[2] = lhrandom(-150, 150) + 80;
568 //                      particle(pt_flamefall, 106 + (rand()%6), particletexture, TPOLYTYPE_ALPHA, 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]);
569                         for (j = 0;j < 64;j++)
570                         {
571                                 forg[0] = lhrandom(-20, 20) + org[0];
572                                 forg[1] = lhrandom(-20, 20) + org[1];
573                                 forg[2] = lhrandom(-20, 20) + org[2];
574                                 fvel2[0] = fvel[0] + lhrandom(-30, 30);
575                                 fvel2[1] = fvel[1] + lhrandom(-30, 30);
576                                 fvel2[2] = fvel[2] + lhrandom(-30, 30);
577                                 f = lhrandom(0.2, 1);
578                                 fvel2[0] *= f;
579                                 fvel2[1] *= f;
580                                 fvel2[2] *= f;
581                                 particle(pt_flamefall, 106 + (rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 5, lhrandom(96, 192), 5, forg[0], forg[1], forg[2], fvel2[0], fvel2[1], fvel2[2]);
582                         }
583                 }
584 //              for (i = 0;i < 16;i++)
585 //                      particle(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 20, 192, 99, org[0] + lhrandom(-20, 20), org[1] + lhrandom(-20, 20), org[2] + lhrandom(-20, 20), 0, 0, 0);
586 //              for (i = 0;i < 50;i++)
587 //                      particle(pt_flamingdebris, ramp3[rand()%6], particletexture, TPOLYTYPE_ALPHA, false, 3, 255, 99, org[0] + lhrandom(-10, 10), org[1] + lhrandom(-10, 10), org[2] + lhrandom(-10, 10), lhrandom(-200, 200), lhrandom(-200, 200), lhrandom(-200, 200));
588 //              for (i = 0;i < 30;i++)
589 //                      particle(pt_smokingdebris, 10 + (rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 99, org[0] + lhrandom(-10, 10), org[1] + lhrandom(-10, 10), org[2] + lhrandom(-10, 10), lhrandom(-100, 100), lhrandom(-100, 100), lhrandom(-100, 100));
590         }
591         */
592 }
593
594 /*
595 ===============
596 R_ParticleExplosion2
597
598 ===============
599 */
600 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
601 {
602         int                     i;
603         if (!r_particles.value) return; // LordHavoc: particles are optional
604
605         for (i = 0;i < 512;i++)
606                 particle(pt_fade, 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));
607 }
608
609 /*
610 ===============
611 R_BlobExplosion
612
613 ===============
614 */
615 void R_BlobExplosion (vec3_t org)
616 {
617         int                     i;
618         if (!r_particles.value) return; // LordHavoc: particles are optional
619         
620         for (i = 0;i < 256;i++)
621                 particle(pt_blob,   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));
622         for (i = 0;i < 256;i++)
623                 particle(pt_blob2, 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));
624 }
625
626 /*
627 ===============
628 R_RunParticleEffect
629
630 ===============
631 */
632 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
633 {
634         if (!r_particles.value) return; // LordHavoc: particles are optional
635         
636         if (count == 1024)
637         {
638                 R_ParticleExplosion(org, false);
639                 return;
640         }
641         while (count--)
642                 particle(pt_fade, 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));
643 }
644
645 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
646 /*
647 ===============
648 R_SparkShower
649 ===============
650 */
651 void R_SparkShower (vec3_t org, vec3_t dir, int count)
652 {
653         if (!r_particles.value) return; // LordHavoc: particles are optional
654
655         R_Decal(org, bulletholetexture[rand()&7], 16, 0, 0, 0, 255);
656
657         // smoke puff
658         if (r_particles_smoke.value)
659                 particle(pt_bulletsmoke, 10, 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));
660
661         if (r_particles_sparks.value)
662         {
663                 // sparks
664                 while(count--)
665                         particle(pt_spark, ramp3[rand()%6], particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(0, 255), 9999, 1.5, org[0], org[1], org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(0, 128));
666         }
667 }
668
669 void R_BloodPuff (vec3_t org, vec3_t vel, int count)
670 {
671         // bloodcount is used to accumulate counts too small to cause a blood particle
672         static int bloodcount = 0;
673         if (!r_particles.value) return; // LordHavoc: particles are optional
674         if (!r_particles_blood.value) return;
675
676         if (count > 100)
677                 count = 100;
678         bloodcount += count;
679         while(bloodcount >= 10)
680         {
681                 particle(pt_blood, 68+(rand()&3), 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));
682                 bloodcount -= 10;
683         }
684 }
685
686 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
687 {
688         vec3_t          diff;
689         vec3_t          center;
690         vec3_t          velscale;
691         if (!r_particles.value) return; // LordHavoc: particles are optional
692         if (!r_particles_bloodshowers.value) return;
693         if (!r_particles_blood.value) return;
694
695         VectorSubtract(maxs, mins, diff);
696         center[0] = (mins[0] + maxs[0]) * 0.5;
697         center[1] = (mins[1] + maxs[1]) * 0.5;
698         center[2] = (mins[2] + maxs[2]) * 0.5;
699         // FIXME: change velspeed back to 2.0x after fixing mod
700         velscale[0] = velspeed * 2.0 / diff[0];
701         velscale[1] = velspeed * 2.0 / diff[1];
702         velscale[2] = velspeed * 2.0 / diff[2];
703         
704         while (count--)
705         {
706                 vec3_t org, vel;
707                 org[0] = lhrandom(mins[0], maxs[0]);
708                 org[1] = lhrandom(mins[1], maxs[1]);
709                 org[2] = lhrandom(mins[2], maxs[2]);
710                 vel[0] = (org[0] - center[0]) * velscale[0];
711                 vel[1] = (org[1] - center[1]) * velscale[1];
712                 vel[2] = (org[2] - center[2]) * velscale[2];
713                 particle(pt_blood, 68+(rand()&3), smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 24, 255, 9999, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2]);
714         }
715 }
716
717 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
718 {
719         float           t;
720         if (!r_particles.value) return; // LordHavoc: particles are optional
721         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
722         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
723         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
724
725         while (count--)
726                 particle(gravity ? pt_grav : pt_static, 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));
727 }
728
729 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
730 {
731         vec3_t          vel;
732         float           t, z;
733         if (!r_particles.value) return; // LordHavoc: particles are optional
734         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
735         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
736         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
737         if (dir[2] < 0) // falling
738         {
739                 t = (maxs[2] - mins[2]) / -dir[2];
740                 z = maxs[2];
741         }
742         else // rising??
743         {
744                 t = (maxs[2] - mins[2]) / dir[2];
745                 z = mins[2];
746         }
747         if (t < 0 || t > 2) // sanity check
748                 t = 2;
749
750         switch(type)
751         {
752         case 0:
753                 while(count--)
754                 {
755                         vel[0] = dir[0] + lhrandom(-16, 16);
756                         vel[1] = dir[1] + lhrandom(-16, 16);
757                         vel[2] = dir[2] + lhrandom(-32, 32);
758                         particle4(pt_rain, 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]);
759                 }
760                 break;
761         case 1:
762                 while(count--)
763                 {
764                         vel[0] = dir[0] + lhrandom(-16, 16);
765                         vel[1] = dir[1] + lhrandom(-16, 16);
766                         vel[2] = dir[2] + lhrandom(-32, 32);
767                         particle4(pt_snow, 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]);
768                 }
769                 break;
770         default:
771                 Host_Error("R_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
772         }
773 }
774
775 void R_FlameCube (vec3_t mins, vec3_t maxs, int count)
776 {
777         float           t;
778         if (!r_particles.value) return; // LordHavoc: particles are optional
779         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
780         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
781         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
782
783         while (count--)
784                 particle(pt_flame, 224 + (rand()&15), smokeparticletexture[rand()&7], TPOLYTYPE_ADD, 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));
785 }
786
787 void R_Flames (vec3_t org, vec3_t vel, int count)
788 {
789         if (!r_particles.value) return; // LordHavoc: particles are optional
790
791         while (count--)
792                 particle(pt_flame, 224 + (rand()&15), smokeparticletexture[rand()&7], TPOLYTYPE_ADD, 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));
793 }
794
795
796
797 /*
798 ===============
799 R_LavaSplash
800
801 ===============
802 */
803 void R_LavaSplash (vec3_t origin)
804 {
805         int                     i, j;
806         float           vel;
807         vec3_t          dir, org;
808         if (!r_particles.value) return; // LordHavoc: particles are optional
809
810         for (i=-128 ; i<128 ; i+=16)
811         {
812                 for (j=-128 ; j<128 ; j+=16)
813                 {
814                         dir[0] = j + lhrandom(0, 8);
815                         dir[1] = i + lhrandom(0, 8);
816                         dir[2] = 256;
817                         org[0] = origin[0] + dir[0];
818                         org[1] = origin[1] + dir[1];
819                         org[2] = origin[2] + lhrandom(0, 64);
820                         vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
821                         particle(pt_lavasplash, 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);
822 //                      particle(pt_lavasplash, 224 + (rand()&7), particletexture, TPOLYTYPE_ALPHA, false, 7, 255, 9999, 0, origin[0] + i, origin[1] + j, origin[2] + lhrandom(0, 63), i * lhrandom(0.125, 0.25), j * lhrandom(0.125, 0.25), lhrandom(64, 128));
823                 }
824         }
825 }
826
827 /*
828 ===============
829 R_TeleportSplash
830
831 ===============
832 */
833 void R_TeleportSplash (vec3_t org)
834 {
835         int                     i, j, k;
836         if (!r_particles.value) return; // LordHavoc: particles are optional
837
838         for (i=-16 ; i<16 ; i+=8)
839                 for (j=-16 ; j<16 ; j+=8)
840                         for (k=-24 ; k<32 ; k+=8)
841                                 particle(pt_fade, 254, particletexture, TPOLYTYPE_ADD, 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));
842 }
843
844 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
845 {
846         vec3_t          vec, dir, vel;
847         float           len, dec = 0, speed;
848         int                     contents, bubbles, polytype;
849         double          t;
850         if (!r_particles.value) return; // LordHavoc: particles are optional
851
852         VectorSubtract(end, start, dir);
853         VectorNormalize(dir);
854
855         if (type == 0 && host_frametime != 0) // rocket glow
856                 particle(pt_oneframe, 254, rocketglowparticletexture, TPOLYTYPE_ADD, false, 24, 255, 9999, 0, end[0] - 12 * dir[0], end[1] - 12 * dir[1], end[2] - 12 * dir[2], 0, 0, 0);
857
858         t = ent->render.trail_time;
859         if (t >= cl.time)
860                 return; // no particles to spawn this frame (sparse trail)
861
862         if (t < cl.oldtime)
863                 t = cl.oldtime;
864
865         VectorSubtract (end, start, vec);
866         len = VectorNormalizeLength (vec);
867         if (len <= 0.01f)
868         {
869                 // advance the trail time
870                 ent->render.trail_time = cl.time;
871                 return;
872         }
873         speed = len / (cl.time - cl.oldtime);
874         VectorScale(vec, speed, vel);
875
876         // advance into this frame to reach the first puff location
877         dec = t - cl.oldtime;
878         dec *= speed;
879         VectorMA(start, dec, vec, start);
880
881         contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
882         if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
883         {
884                 // advance the trail time
885                 ent->render.trail_time = cl.time;
886                 return;
887         }
888
889         bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
890
891         polytype = TPOLYTYPE_ALPHA;
892         if (ent->render.effects & EF_ADDITIVE)
893                 polytype = TPOLYTYPE_ADD;
894
895         while (t < cl.time)
896         {
897                 switch (type)
898                 {
899                         case 0: // rocket trail
900                                 if (!r_particles_smoke.value)
901                                         dec = cl.time - t;
902                                 else if (bubbles && r_particles_bubbles.value)
903                                 {
904                                         dec = 0.01f;
905                                         particle(pt_bubble, 254, 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));
906                                         particle(pt_bubble, 254, 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));
907                                         particle(pt_smoke, 254, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
908                                 }
909                                 else
910                                 {
911                                         dec = 0.01f;
912                                         particle(pt_smoke, 12, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
913 //                                      particle(pt_spark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
914 //                                      particle(pt_spark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
915 //                                      particle(pt_spark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
916 //                                      particle(pt_spark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
917                                 }
918                                 break;
919
920                         case 1: // grenade trail
921                                 // FIXME: make it gradually stop smoking
922                                 if (!r_particles_smoke.value)
923                                         dec = cl.time - t;
924                                 else if (bubbles && r_particles_bubbles.value)
925                                 {
926                                         dec = 0.02f;
927                                         particle(pt_bubble, 254, 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));
928                                         particle(pt_bubble, 254, 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));
929                                         particle(pt_smoke, 254, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
930                                 }
931                                 else
932                                 {
933                                         dec = 0.02f;
934                                         particle(pt_smoke, 8, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
935                                 }
936                                 break;
937
938
939                         case 2: // blood
940                                 if (!r_particles_blood.value)
941                                         dec = cl.time - t;
942                                 else
943                                 {
944                                         dec = 0.2f;
945                                         particle(pt_blood, 67+(rand()&3), 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));
946                                 }
947                                 break;
948
949                         case 4: // slight blood
950                                 if (!r_particles_blood.value)
951                                         dec = cl.time - t;
952                                 else
953                                 {
954                                         dec = 0.3f;
955                                         particle(pt_blood, 67+(rand()&3), 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));
956                                 }
957                                 break;
958
959                         case 3: // green tracer
960                                 dec = 0.02f;
961                                 particle(pt_fade,  56, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
962                                 break;
963
964                         case 5: // flame tracer
965                                 dec = 0.02f;
966                                 particle(pt_fade, 234, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
967                                 break;
968
969                         case 6: // voor trail
970                                 dec = 0.05f; // sparse trail
971                                 particle(pt_fade, 152 + (rand()&3), smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
972                                 break;
973
974                         case 7: // Nehahra smoke tracer
975                                 if (!r_particles_smoke.value)
976                                         dec = cl.time - t;
977                                 else
978                                 {
979                                         dec = 0.14f;
980                                         particle(pt_smoke, 12, smokeparticletexture[rand()&7], polytype, true, 10, 64, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
981                                 }
982                                 break;
983                 }
984
985                 // advance to next time and position
986                 t += dec;
987                 dec *= speed;
988                 VectorMA (start, dec, vec, start);
989         }
990         ent->render.trail_time = t;
991 }
992
993 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
994 {
995         vec3_t          vec;
996         int                     len;
997         if (!r_particles.value) return; // LordHavoc: particles are optional
998         if (!r_particles_smoke.value) return;
999
1000         VectorSubtract (end, start, vec);
1001         len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
1002         VectorScale(vec, 3, vec);
1003         while (len--)
1004         {
1005                 particle(pt_smoke, color, particletexture, TPOLYTYPE_ALPHA, false, 8, 192, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
1006                 VectorAdd (start, vec, start);
1007         }
1008 }
1009
1010
1011 /*
1012 ===============
1013 R_DrawParticles
1014 ===============
1015 */
1016 void R_MoveParticles (void)
1017 {
1018         particle_t              *p;
1019         int                             i, activeparticles, maxparticle, j, a;
1020         vec3_t                  v;
1021         float                   gravity, dvel, frametime;
1022
1023         // LordHavoc: early out condition
1024         if (!numparticles)
1025                 return;
1026
1027         frametime = cl.time - cl.oldtime;
1028         if (!frametime)
1029                 return; // if absolutely still, don't update particles
1030         gravity = frametime * sv_gravity.value;
1031         dvel = 1+4*frametime;
1032
1033         activeparticles = 0;
1034         maxparticle = -1;
1035         j = 0;
1036         for (i = 0, p = particles;i < numparticles;i++, p++)
1037         {
1038                 if (p->die < cl.time)
1039                 {
1040                         freeparticles[j++] = p;
1041                         continue;
1042                 }
1043
1044                 VectorCopy(p->org, p->oldorg);
1045                 p->org[0] += p->vel[0]*frametime;
1046                 p->org[1] += p->vel[1]*frametime;
1047                 p->org[2] += p->vel[2]*frametime;
1048                 if (p->bounce)
1049                 {
1050                         vec3_t normal;
1051                         float dist;
1052                         if (TraceLine(p->oldorg, p->org, v, normal) < 1)
1053                         {
1054                                 VectorCopy(v, p->org);
1055                                 if (p->bounce < 0)
1056                                 {
1057                                         byte *color24 = (byte *) &d_8to24table[(int)p->color];
1058                                         R_Decal(v, p->tex, p->scale, color24[0], color24[1], color24[2], p->alpha);
1059                                         p->die = -1;
1060                                         freeparticles[j++] = p;
1061                                         continue;
1062                                         /*
1063                                         VectorClear(p->vel);
1064                                         p->type = pt_decal;
1065                                         // have to negate the direction (why?)
1066                                         VectorNegate(normal, p->direction);
1067                                         VectorVectors(p->direction, p->decalright, p->decalup);
1068                                         VectorSubtract(p->org, p->direction, p->org); // push off the surface a bit so it doesn't flicker
1069                                         p->bounce = 0;
1070                                         p->time2 = cl.time + 30;
1071                                         */
1072                                 }
1073                                 else
1074                                 {
1075                                         dist = DotProduct(p->vel, normal) * -p->bounce;
1076                                         VectorMAQuick(p->vel, dist, normal, p->vel);
1077                                         if (DotProduct(p->vel, p->vel) < 0.03)
1078                                                 VectorClear(p->vel);
1079                                 }
1080                         }
1081                 }
1082                 
1083                 switch (p->type)
1084                 {
1085                 case pt_static:
1086                         break;
1087
1088                         // LordHavoc: drop-through because of shared code
1089                 case pt_blob:
1090                         p->vel[2] *= dvel;
1091                 case pt_blob2:
1092                         p->vel[0] *= dvel;
1093                         p->vel[1] *= dvel;
1094                         p->alpha -= frametime * 256;
1095                         if (p->alpha < 1)
1096                                 p->die = -1;
1097                         break;
1098
1099                 case pt_grav:
1100                         p->vel[2] -= gravity;
1101                         break;
1102                 case pt_slowgrav:
1103                         p->vel[2] -= gravity * 0.05;
1104                         break;
1105                 case pt_lavasplash:
1106                         p->vel[2] -= gravity * 0.05;
1107                         p->alpha -= frametime * 192;
1108                         if (p->alpha < 1)
1109                                 p->die = -1;
1110                         break;
1111                 case pt_snow:
1112                         if (cl.time > p->time2)
1113                         {
1114                                 p->time2 = cl.time + (rand() & 3) * 0.1;
1115                                 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1116                                 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1117                                 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1118                         }
1119                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1120                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1121                         {
1122                                 vec3_t normal;
1123                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1124                                         break; // still in solid
1125                                 p->die = cl.time + 1000;
1126                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1127                                 switch (a)
1128                                 {
1129                                 case CONTENTS_LAVA:
1130                                 case CONTENTS_SLIME:
1131                                         p->tex = smokeparticletexture[rand()&7];
1132                                         p->type = pt_steam;
1133                                         p->alpha = 96;
1134                                         p->scale = 5;
1135                                         p->vel[2] = 96;
1136                                         break;
1137                                 case CONTENTS_WATER:
1138                                         p->tex = smokeparticletexture[rand()&7];
1139                                         p->type = pt_splash;
1140                                         p->alpha = 96;
1141                                         p->scale = 5;
1142                                         p->vel[2] = 96;
1143                                         break;
1144                                 default: // CONTENTS_SOLID and any others
1145                                         TraceLine(p->oldorg, p->org, v, normal);
1146                                         VectorCopy(v, p->org);
1147                                         p->tex = smokeparticletexture[rand()&7];
1148                                         p->type = pt_fade;
1149                                         VectorClear(p->vel);
1150                                         break;
1151                                 }
1152                         }
1153                         break;
1154                 case pt_blood:
1155                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1156                         {
1157                                 p->die = -1;
1158                                 break;
1159                         }
1160                         p->vel[2] -= gravity * 0.5;
1161                         break;
1162                 case pt_spark:
1163                         p->alpha -= frametime * 512;
1164                         p->vel[2] -= gravity;
1165                         if (p->alpha < 1)
1166                                 p->die = -1;
1167                         break;
1168                 case pt_fade:
1169                         p->alpha -= frametime * 512;
1170                         if (p->alpha < 1)
1171                                 p->die = -1;
1172                         break;
1173                 case pt_bubble:
1174                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1175                         if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1176                         {
1177                                 p->tex = smokeparticletexture[rand()&7];
1178                                 p->type = pt_splashpuff;
1179                                 p->scale = 4;
1180                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1181                                 break;
1182                         }
1183                         p->vel[2] += gravity * 0.25;
1184                         p->vel[0] *= (1 - (frametime * 0.0625));
1185                         p->vel[1] *= (1 - (frametime * 0.0625));
1186                         p->vel[2] *= (1 - (frametime * 0.0625));
1187                         if (cl.time > p->time2)
1188                         {
1189                                 p->time2 = cl.time + lhrandom(0, 0.5);
1190                                 p->vel[0] += lhrandom(-32,32);
1191                                 p->vel[1] += lhrandom(-32,32);
1192                                 p->vel[2] += lhrandom(-32,32);
1193                         }
1194                         p->alpha -= frametime * 256;
1195                         if (p->alpha < 1)
1196                                 p->die = -1;
1197                         break;
1198                 case pt_bulletsmoke:
1199                         p->scale += frametime * 16;
1200                         p->alpha -= frametime * 1024;
1201                         p->vel[2] += gravity * 0.05;
1202                         if (p->alpha < 1)
1203                                 p->die = -1;
1204                         break;
1205                 case pt_smoke:
1206                         p->scale += frametime * 32;
1207                         p->alpha -= frametime * 512;
1208                         p->vel[2] += gravity * 0.05;
1209                         if (p->alpha < 1)
1210                                 p->die = -1;
1211                         break;
1212                 case pt_steam:
1213                         p->scale += frametime * 48;
1214                         p->alpha -= frametime * 512;
1215                         p->vel[2] += gravity * 0.05;
1216                         if (p->alpha < 1)
1217                                 p->die = -1;
1218                         break;
1219                 case pt_splashpuff:
1220 //                      p->scale += frametime * 24;
1221                         p->alpha -= frametime * 1024;
1222                         if (p->alpha < 1)
1223                                 p->die = -1;
1224                         break;
1225                 case pt_rain:
1226                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1227                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1228                         {
1229                                 vec3_t normal;
1230                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1231                                         break; // still in solid
1232                                 p->die = cl.time + 1000;
1233                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1234                                 switch (a)
1235                                 {
1236                                 case CONTENTS_LAVA:
1237                                 case CONTENTS_SLIME:
1238                                         p->tex = smokeparticletexture[rand()&7];
1239                                         p->type = pt_steam;
1240                                         p->scale = 3;
1241                                         p->vel[2] = 96;
1242                                         break;
1243                                 case CONTENTS_WATER:
1244                                         p->tex = smokeparticletexture[rand()&7];
1245                                         p->type = pt_splashpuff;
1246                                         p->scale = 4;
1247                                         break;
1248                                 default: // CONTENTS_SOLID and any others
1249                                         TraceLine(p->oldorg, p->org, v, normal);
1250                                         VectorCopy(v, p->org);
1251                                         p->tex = smokeparticletexture[rand()&7];
1252                                         p->type = pt_splashpuff;
1253                                         p->scale = 4;
1254                                         break;
1255                                 }
1256                         }
1257                         break;
1258                 case pt_flame:
1259                         p->alpha -= frametime * 512;
1260                         p->vel[2] += gravity;
1261 //                      p->scale -= frametime * 16;
1262                         if (p->alpha < 16)
1263                                 p->die = -1;
1264                         break;
1265                         /*
1266                 case pt_flamingdebris:
1267                         if (cl.time >= p->time2)
1268                         {
1269                                 p->time2 = cl.time + 0.01;
1270                                 particle(pt_flame, p->color, particletexture, TPOLYTYPE_ADD, false, 4, p->alpha, 9999, 0, org[0], org[1], org[2], lhrandom(-50, 50), lhrandom(-50, 50), lhrandom(-50, 50));
1271                         }
1272                         p->alpha -= frametime * 512;
1273                         p->vel[2] -= gravity * 0.5f;
1274                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1275                                 p->die = -1;
1276                         if (p->alpha < 1)
1277                                 p->die = -1;
1278                         break;
1279                 case pt_smokingdebris:
1280                         if (cl.time >= p->time2)
1281                         {
1282                                 p->time2 = cl.time + 0.01;
1283                                 particle2(pt_flame, 15, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, false, 4, p->alpha, 9999, 0, org[0], org[1], org[2], lhrandom(-50, 50), lhrandom(-50, 50), lhrandom(-50, 50));
1284                         }
1285                         p->alpha -= frametime * 512;
1286                         p->vel[2] -= gravity * 0.5f;
1287                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1288                                 p->die = -1;
1289                         if (p->alpha < 1)
1290                                 p->die = -1;
1291                         break;
1292                 case pt_flamefall:
1293                         p->alpha -= frametime * 512;
1294                         p->vel[2] -= gravity * 0.5f;
1295                         if (p->alpha < 1)
1296                                 p->die = -1;
1297                         break;
1298                         */
1299                         /*
1300                 case pt_decal:
1301                         if (cl.time > p->time2)
1302                         {
1303                                 p->alpha -= frametime * 256;
1304                                 if (p->alpha < 1)
1305                                         p->die = -1;
1306                         }
1307                         if (p->alpha < 64)
1308                                 p->die = -1;
1309                         break;
1310                         */
1311                 case pt_oneframe:
1312                         if (p->time2)
1313                                 p->die = -1;
1314                         p->time2 = 1;
1315                         break;
1316                 default:
1317                         printf("unknown particle type %i\n", p->type);
1318                         p->die = -1;
1319                         break;
1320                 }
1321
1322                 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1323                 if (p->die < cl.time)
1324                         freeparticles[j++] = p;
1325                 else
1326                 {
1327                         maxparticle = i;
1328                         activeparticles++;
1329                 }
1330         }
1331         // fill in gaps to compact the array
1332         i = 0;
1333         while (maxparticle >= activeparticles)
1334         {
1335                 *freeparticles[i++] = particles[maxparticle--];
1336                 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1337                         maxparticle--;
1338         }
1339         numparticles = activeparticles;
1340 }
1341
1342 void R_DrawParticles (void)
1343 {
1344         particle_t              *p;
1345         int                             i, dynamiclight, staticlight, r, g, b;
1346         byte                    br, bg, bb, ba;
1347         float                   scale, scale2, minparticledist;
1348         byte                    *color24;
1349         vec3_t                  uprightangles, up2, right2, tempcolor, corner;
1350         mleaf_t                 *leaf;
1351
1352         // LordHavoc: early out condition
1353         if ((!numparticles) || (!r_drawparticles.value))
1354                 return;
1355
1356         staticlight = dynamiclight = r_particles_lighting.value;
1357         if (!r_dynamic.value)
1358                 dynamiclight = 0;
1359         c_particles += numparticles;
1360
1361         uprightangles[0] = 0;
1362         uprightangles[1] = r_refdef.viewangles[1];
1363         uprightangles[2] = 0;
1364         AngleVectors (uprightangles, NULL, right2, up2);
1365
1366         minparticledist = DotProduct(r_origin, vpn) + 16.0f;
1367
1368         for (i = 0, p = particles;i < numparticles;i++, p++)
1369         {
1370                 // LordHavoc: unnecessary (array was already compacted)
1371 //              if (p->die < cl.time)
1372 //                      continue;
1373
1374                 // LordHavoc: only render if not too close
1375                 if (DotProduct(p->org, vpn) < minparticledist)
1376                         continue;
1377
1378                 // LordHavoc: check if it's in a visible leaf
1379                 leaf = Mod_PointInLeaf(p->org, cl.worldmodel);
1380                 if (leaf->visframe != r_framecount)
1381                         continue;
1382
1383                 /*
1384                 if (p->type == pt_decal)
1385                 {
1386                         VectorSubtract(p->org, r_origin, v);
1387                         if (DotProduct(p->direction, v) < 0)
1388                                 continue;
1389                 }
1390                 */
1391
1392                 color24 = (byte *) &d_8to24table[(int)p->color];
1393                 r = color24[0];
1394                 g = color24[1];
1395                 b = color24[2];
1396                 if (staticlight && (p->dynlight || staticlight >= 2)) // LordHavoc: only light blood and smoke
1397                 {
1398                         R_CompleteLightPoint(tempcolor, p->org, dynamiclight);
1399                         r = (r * (int) tempcolor[0]) >> 7;
1400                         g = (g * (int) tempcolor[1]) >> 7;
1401                         b = (b * (int) tempcolor[2]) >> 7;
1402                 }
1403                 br = (byte) min(r, 255);
1404                 bg = (byte) min(g, 255);
1405                 bb = (byte) min(b, 255);
1406                 ba = (byte) p->alpha;
1407                 transpolybegin(R_GetTexture(p->tex), 0, R_GetTexture(p->tex), p->rendermode);
1408                 scale = p->scale * -0.5;scale2 = p->scale;
1409                 /*
1410                 if (p->type == pt_decal)
1411                 {
1412                         corner[0] = p->org[0] + p->decalup[0]*scale + p->decalright[0]*scale;
1413                         corner[1] = p->org[1] + p->decalup[1]*scale + p->decalright[1]*scale;
1414                         corner[2] = p->org[2] + p->decalup[2]*scale + p->decalright[2]*scale;
1415                         transpolyvertub(corner[0]                                                 , corner[1]                                                 , corner[2]                                                 , 0,1,br,bg,bb,ba);
1416                         transpolyvertub(corner[0] + p->decalup[0]*scale2                          , corner[1] + p->decalup[1]*scale2                          , corner[2] + p->decalup[2]*scale2                          , 0,0,br,bg,bb,ba);
1417                         transpolyvertub(corner[0] + p->decalup[0]*scale2 + p->decalright[0]*scale2, corner[1] + p->decalup[1]*scale2 + p->decalright[1]*scale2, corner[2] + p->decalup[2]*scale2 + p->decalright[2]*scale2, 1,0,br,bg,bb,ba);
1418                         transpolyvertub(corner[0]                        + p->decalright[0]*scale2, corner[1]                        + p->decalright[1]*scale2, corner[2]                        + p->decalright[2]*scale2, 1,1,br,bg,bb,ba);
1419                 }
1420                 else*/ if (p->tex == rainparticletexture) // rain streak
1421                 {
1422                         corner[0] = p->org[0] + up2[0]*scale + right2[0]*scale;
1423                         corner[1] = p->org[1] + up2[1]*scale + right2[1]*scale;
1424                         corner[2] = p->org[2] + up2[2]*scale + right2[2]*scale;
1425                         transpolyvertub(corner[0]                                   , corner[1]                                   , corner[2]                                   , 0,1,br,bg,bb,ba);
1426                         transpolyvertub(corner[0] + up2[0]*scale2                   , corner[1] + up2[1]*scale2                   , corner[2] + up2[2]*scale2                   , 0,0,br,bg,bb,ba);
1427                         transpolyvertub(corner[0] + up2[0]*scale2 + right2[0]*scale2, corner[1] + up2[1]*scale2 + right2[1]*scale2, corner[2] + up2[2]*scale2 + right2[2]*scale2, 1,0,br,bg,bb,ba);
1428                         transpolyvertub(corner[0]                 + right2[0]*scale2, corner[1]                 + right2[1]*scale2, corner[2]                 + right2[2]*scale2, 1,1,br,bg,bb,ba);
1429                 }
1430                 else
1431                 {
1432                         corner[0] = p->org[0] + vup[0]*scale + vright[0]*scale;
1433                         corner[1] = p->org[1] + vup[1]*scale + vright[1]*scale;
1434                         corner[2] = p->org[2] + vup[2]*scale + vright[2]*scale;
1435                         transpolyvertub(corner[0]                                   , corner[1]                                   , corner[2]                                   , 0,1,br,bg,bb,ba);
1436                         transpolyvertub(corner[0] + vup[0]*scale2                   , corner[1] + vup[1]*scale2                   , corner[2] + vup[2]*scale2                   , 0,0,br,bg,bb,ba);
1437                         transpolyvertub(corner[0] + vup[0]*scale2 + vright[0]*scale2, corner[1] + vup[1]*scale2 + vright[1]*scale2, corner[2] + vup[2]*scale2 + vright[2]*scale2, 1,0,br,bg,bb,ba);
1438                         transpolyvertub(corner[0]                 + vright[0]*scale2, corner[1]                 + vright[1]*scale2, corner[2]                 + vright[2]*scale2, 1,1,br,bg,bb,ba);
1439                 }
1440                 transpolyend();
1441         }
1442 }