]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - r_part.c
some whitespace changes
[xonotic/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()
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()
274 {
275         numparticles = 0;
276         qfree(particles);
277         qfree(freeparticles);
278 }
279
280 void r_part_newmap()
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 extern  cvar_t  sv_gravity;
1017
1018 void R_MoveParticles (void)
1019 {
1020         particle_t              *p;
1021         int                             i, activeparticles, maxparticle, j, a;
1022         vec3_t                  v;
1023         float                   gravity, dvel, frametime;
1024
1025         // LordHavoc: early out condition
1026         if (!numparticles)
1027                 return;
1028
1029         frametime = cl.time - cl.oldtime;
1030         if (!frametime)
1031                 return; // if absolutely still, don't update particles
1032         gravity = frametime * sv_gravity.value;
1033         dvel = 1+4*frametime;
1034
1035         activeparticles = 0;
1036         maxparticle = -1;
1037         j = 0;
1038         for (i = 0, p = particles;i < numparticles;i++, p++)
1039         {
1040                 if (p->die < cl.time)
1041                 {
1042                         freeparticles[j++] = p;
1043                         continue;
1044                 }
1045
1046                 VectorCopy(p->org, p->oldorg);
1047                 p->org[0] += p->vel[0]*frametime;
1048                 p->org[1] += p->vel[1]*frametime;
1049                 p->org[2] += p->vel[2]*frametime;
1050                 if (p->bounce)
1051                 {
1052                         vec3_t normal;
1053                         float dist;
1054                         if (TraceLine(p->oldorg, p->org, v, normal) < 1)
1055                         {
1056                                 VectorCopy(v, p->org);
1057                                 if (p->bounce < 0)
1058                                 {
1059                                         byte *color24 = (byte *) &d_8to24table[(int)p->color];
1060                                         R_Decal(v, p->tex, p->scale, color24[0], color24[1], color24[2], p->alpha);
1061                                         p->die = -1;
1062                                         freeparticles[j++] = p;
1063                                         continue;
1064                                         /*
1065                                         VectorClear(p->vel);
1066                                         p->type = pt_decal;
1067                                         // have to negate the direction (why?)
1068                                         VectorNegate(normal, p->direction);
1069                                         VectorVectors(p->direction, p->decalright, p->decalup);
1070                                         VectorSubtract(p->org, p->direction, p->org); // push off the surface a bit so it doesn't flicker
1071                                         p->bounce = 0;
1072                                         p->time2 = cl.time + 30;
1073                                         */
1074                                 }
1075                                 else
1076                                 {
1077                                         dist = DotProduct(p->vel, normal) * -p->bounce;
1078                                         VectorMAQuick(p->vel, dist, normal, p->vel);
1079                                         if (DotProduct(p->vel, p->vel) < 0.03)
1080                                                 VectorClear(p->vel);
1081                                 }
1082                         }
1083                 }
1084                 
1085                 switch (p->type)
1086                 {
1087                 case pt_static:
1088                         break;
1089
1090                         // LordHavoc: drop-through because of shared code
1091                 case pt_blob:
1092                         p->vel[2] *= dvel;
1093                 case pt_blob2:
1094                         p->vel[0] *= dvel;
1095                         p->vel[1] *= dvel;
1096                         p->alpha -= frametime * 256;
1097                         if (p->alpha < 1)
1098                                 p->die = -1;
1099                         break;
1100
1101                 case pt_grav:
1102                         p->vel[2] -= gravity;
1103                         break;
1104                 case pt_slowgrav:
1105                         p->vel[2] -= gravity * 0.05;
1106                         break;
1107                 case pt_lavasplash:
1108                         p->vel[2] -= gravity * 0.05;
1109                         p->alpha -= frametime * 192;
1110                         if (p->alpha < 1)
1111                                 p->die = -1;
1112                         break;
1113                 case pt_snow:
1114                         if (cl.time > p->time2)
1115                         {
1116                                 p->time2 = cl.time + (rand() & 3) * 0.1;
1117                                 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1118                                 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1119                                 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1120                         }
1121                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1122                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1123                         {
1124                                 vec3_t normal;
1125                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1126                                         break; // still in solid
1127                                 p->die = cl.time + 1000;
1128                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1129                                 switch (a)
1130                                 {
1131                                 case CONTENTS_LAVA:
1132                                 case CONTENTS_SLIME:
1133                                         p->tex = smokeparticletexture[rand()&7];
1134                                         p->type = pt_steam;
1135                                         p->alpha = 96;
1136                                         p->scale = 5;
1137                                         p->vel[2] = 96;
1138                                         break;
1139                                 case CONTENTS_WATER:
1140                                         p->tex = smokeparticletexture[rand()&7];
1141                                         p->type = pt_splash;
1142                                         p->alpha = 96;
1143                                         p->scale = 5;
1144                                         p->vel[2] = 96;
1145                                         break;
1146                                 default: // CONTENTS_SOLID and any others
1147                                         TraceLine(p->oldorg, p->org, v, normal);
1148                                         VectorCopy(v, p->org);
1149                                         p->tex = smokeparticletexture[rand()&7];
1150                                         p->type = pt_fade;
1151                                         VectorClear(p->vel);
1152                                         break;
1153                                 }
1154                         }
1155                         break;
1156                 case pt_blood:
1157                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1158                         {
1159                                 p->die = -1;
1160                                 break;
1161                         }
1162                         p->vel[2] -= gravity * 0.5;
1163                         break;
1164                 case pt_spark:
1165                         p->alpha -= frametime * 512;
1166                         p->vel[2] -= gravity;
1167                         if (p->alpha < 1)
1168                                 p->die = -1;
1169                         break;
1170                 case pt_fade:
1171                         p->alpha -= frametime * 512;
1172                         if (p->alpha < 1)
1173                                 p->die = -1;
1174                         break;
1175                 case pt_bubble:
1176                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1177                         if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1178                         {
1179                                 p->tex = smokeparticletexture[rand()&7];
1180                                 p->type = pt_splashpuff;
1181                                 p->scale = 4;
1182                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1183                                 break;
1184                         }
1185                         p->vel[2] += gravity * 0.25;
1186                         p->vel[0] *= (1 - (frametime * 0.0625));
1187                         p->vel[1] *= (1 - (frametime * 0.0625));
1188                         p->vel[2] *= (1 - (frametime * 0.0625));
1189                         if (cl.time > p->time2)
1190                         {
1191                                 p->time2 = cl.time + lhrandom(0, 0.5);
1192                                 p->vel[0] += lhrandom(-32,32);
1193                                 p->vel[1] += lhrandom(-32,32);
1194                                 p->vel[2] += lhrandom(-32,32);
1195                         }
1196                         p->alpha -= frametime * 256;
1197                         if (p->alpha < 1)
1198                                 p->die = -1;
1199                         break;
1200                 case pt_bulletsmoke:
1201                         p->scale += frametime * 16;
1202                         p->alpha -= frametime * 1024;
1203                         p->vel[2] += gravity * 0.05;
1204                         if (p->alpha < 1)
1205                                 p->die = -1;
1206                         break;
1207                 case pt_smoke:
1208                         p->scale += frametime * 32;
1209                         p->alpha -= frametime * 512;
1210                         p->vel[2] += gravity * 0.05;
1211                         if (p->alpha < 1)
1212                                 p->die = -1;
1213                         break;
1214                 case pt_steam:
1215                         p->scale += frametime * 48;
1216                         p->alpha -= frametime * 512;
1217                         p->vel[2] += gravity * 0.05;
1218                         if (p->alpha < 1)
1219                                 p->die = -1;
1220                         break;
1221                 case pt_splashpuff:
1222 //                      p->scale += frametime * 24;
1223                         p->alpha -= frametime * 1024;
1224                         if (p->alpha < 1)
1225                                 p->die = -1;
1226                         break;
1227                 case pt_rain:
1228                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1229                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1230                         {
1231                                 vec3_t normal;
1232                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1233                                         break; // still in solid
1234                                 p->die = cl.time + 1000;
1235                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1236                                 switch (a)
1237                                 {
1238                                 case CONTENTS_LAVA:
1239                                 case CONTENTS_SLIME:
1240                                         p->tex = smokeparticletexture[rand()&7];
1241                                         p->type = pt_steam;
1242                                         p->scale = 3;
1243                                         p->vel[2] = 96;
1244                                         break;
1245                                 case CONTENTS_WATER:
1246                                         p->tex = smokeparticletexture[rand()&7];
1247                                         p->type = pt_splashpuff;
1248                                         p->scale = 4;
1249                                         break;
1250                                 default: // CONTENTS_SOLID and any others
1251                                         TraceLine(p->oldorg, p->org, v, normal);
1252                                         VectorCopy(v, p->org);
1253                                         p->tex = smokeparticletexture[rand()&7];
1254                                         p->type = pt_splashpuff;
1255                                         p->scale = 4;
1256                                         break;
1257                                 }
1258                         }
1259                         break;
1260                 case pt_flame:
1261                         p->alpha -= frametime * 512;
1262                         p->vel[2] += gravity;
1263 //                      p->scale -= frametime * 16;
1264                         if (p->alpha < 16)
1265                                 p->die = -1;
1266                         break;
1267                         /*
1268                 case pt_flamingdebris:
1269                         if (cl.time >= p->time2)
1270                         {
1271                                 p->time2 = cl.time + 0.01;
1272                                 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));
1273                         }
1274                         p->alpha -= frametime * 512;
1275                         p->vel[2] -= gravity * 0.5f;
1276                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1277                                 p->die = -1;
1278                         if (p->alpha < 1)
1279                                 p->die = -1;
1280                         break;
1281                 case pt_smokingdebris:
1282                         if (cl.time >= p->time2)
1283                         {
1284                                 p->time2 = cl.time + 0.01;
1285                                 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));
1286                         }
1287                         p->alpha -= frametime * 512;
1288                         p->vel[2] -= gravity * 0.5f;
1289                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1290                                 p->die = -1;
1291                         if (p->alpha < 1)
1292                                 p->die = -1;
1293                         break;
1294                 case pt_flamefall:
1295                         p->alpha -= frametime * 512;
1296                         p->vel[2] -= gravity * 0.5f;
1297                         if (p->alpha < 1)
1298                                 p->die = -1;
1299                         break;
1300                         */
1301                         /*
1302                 case pt_decal:
1303                         if (cl.time > p->time2)
1304                         {
1305                                 p->alpha -= frametime * 256;
1306                                 if (p->alpha < 1)
1307                                         p->die = -1;
1308                         }
1309                         if (p->alpha < 64)
1310                                 p->die = -1;
1311                         break;
1312                         */
1313                 case pt_oneframe:
1314                         if (p->time2)
1315                                 p->die = -1;
1316                         p->time2 = 1;
1317                         break;
1318                 default:
1319                         printf("unknown particle type %i\n", p->type);
1320                         p->die = -1;
1321                         break;
1322                 }
1323
1324                 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1325                 if (p->die < cl.time)
1326                         freeparticles[j++] = p;
1327                 else
1328                 {
1329                         maxparticle = i;
1330                         activeparticles++;
1331                 }
1332         }
1333         // fill in gaps to compact the array
1334         i = 0;
1335         while (maxparticle >= activeparticles)
1336         {
1337                 *freeparticles[i++] = particles[maxparticle--];
1338                 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1339                         maxparticle--;
1340         }
1341         numparticles = activeparticles;
1342 }
1343
1344 void R_DrawParticles (void)
1345 {
1346         particle_t              *p;
1347         int                             i, dynamiclight, staticlight, r, g, b;
1348         byte                    br, bg, bb, ba;
1349         float                   scale, scale2, minparticledist;
1350         byte                    *color24;
1351         vec3_t                  uprightangles, up2, right2, tempcolor, corner;
1352         mleaf_t                 *leaf;
1353
1354         // LordHavoc: early out condition
1355         if ((!numparticles) || (!r_drawparticles.value))
1356                 return;
1357
1358         staticlight = dynamiclight = r_particles_lighting.value;
1359         if (!r_dynamic.value)
1360                 dynamiclight = 0;
1361         c_particles += numparticles;
1362
1363         uprightangles[0] = 0;
1364         uprightangles[1] = r_refdef.viewangles[1];
1365         uprightangles[2] = 0;
1366         AngleVectors (uprightangles, NULL, right2, up2);
1367
1368         minparticledist = DotProduct(r_origin, vpn) + 16.0f;
1369
1370         for (i = 0, p = particles;i < numparticles;i++, p++)
1371         {
1372                 // LordHavoc: unnecessary (array was already compacted)
1373 //              if (p->die < cl.time)
1374 //                      continue;
1375
1376                 // LordHavoc: only render if not too close
1377                 if (DotProduct(p->org, vpn) < minparticledist)
1378                         continue;
1379
1380                 // LordHavoc: check if it's in a visible leaf
1381                 leaf = Mod_PointInLeaf(p->org, cl.worldmodel);
1382                 if (leaf->visframe != r_framecount)
1383                         continue;
1384
1385                 /*
1386                 if (p->type == pt_decal)
1387                 {
1388                         VectorSubtract(p->org, r_origin, v);
1389                         if (DotProduct(p->direction, v) < 0)
1390                                 continue;
1391                 }
1392                 */
1393
1394                 color24 = (byte *) &d_8to24table[(int)p->color];
1395                 r = color24[0];
1396                 g = color24[1];
1397                 b = color24[2];
1398                 if (staticlight && (p->dynlight || staticlight >= 2)) // LordHavoc: only light blood and smoke
1399                 {
1400                         R_CompleteLightPoint(tempcolor, p->org, dynamiclight);
1401                         r = (r * (int) tempcolor[0]) >> 7;
1402                         g = (g * (int) tempcolor[1]) >> 7;
1403                         b = (b * (int) tempcolor[2]) >> 7;
1404                 }
1405                 br = (byte) min(r, 255);
1406                 bg = (byte) min(g, 255);
1407                 bb = (byte) min(b, 255);
1408                 ba = (byte) p->alpha;
1409                 transpolybegin(R_GetTexture(p->tex), 0, R_GetTexture(p->tex), p->rendermode);
1410                 scale = p->scale * -0.5;scale2 = p->scale;
1411                 /*
1412                 if (p->type == pt_decal)
1413                 {
1414                         corner[0] = p->org[0] + p->decalup[0]*scale + p->decalright[0]*scale;
1415                         corner[1] = p->org[1] + p->decalup[1]*scale + p->decalright[1]*scale;
1416                         corner[2] = p->org[2] + p->decalup[2]*scale + p->decalright[2]*scale;
1417                         transpolyvertub(corner[0]                                                 , corner[1]                                                 , corner[2]                                                 , 0,1,br,bg,bb,ba);
1418                         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);
1419                         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);
1420                         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);
1421                 }
1422                 else*/ if (p->tex == rainparticletexture) // rain streak
1423                 {
1424                         corner[0] = p->org[0] + up2[0]*scale + right2[0]*scale;
1425                         corner[1] = p->org[1] + up2[1]*scale + right2[1]*scale;
1426                         corner[2] = p->org[2] + up2[2]*scale + right2[2]*scale;
1427                         transpolyvertub(corner[0]                                   , corner[1]                                   , corner[2]                                   , 0,1,br,bg,bb,ba);
1428                         transpolyvertub(corner[0] + up2[0]*scale2                   , corner[1] + up2[1]*scale2                   , corner[2] + up2[2]*scale2                   , 0,0,br,bg,bb,ba);
1429                         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);
1430                         transpolyvertub(corner[0]                 + right2[0]*scale2, corner[1]                 + right2[1]*scale2, corner[2]                 + right2[2]*scale2, 1,1,br,bg,bb,ba);
1431                 }
1432                 else
1433                 {
1434                         corner[0] = p->org[0] + vup[0]*scale + vright[0]*scale;
1435                         corner[1] = p->org[1] + vup[1]*scale + vright[1]*scale;
1436                         corner[2] = p->org[2] + vup[2]*scale + vright[2]*scale;
1437                         transpolyvertub(corner[0]                                   , corner[1]                                   , corner[2]                                   , 0,1,br,bg,bb,ba);
1438                         transpolyvertub(corner[0] + vup[0]*scale2                   , corner[1] + vup[1]*scale2                   , corner[2] + vup[2]*scale2                   , 0,0,br,bg,bb,ba);
1439                         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);
1440                         transpolyvertub(corner[0]                 + vright[0]*scale2, corner[1]                 + vright[1]*scale2, corner[2]                 + vright[2]*scale2, 1,1,br,bg,bb,ba);
1441                 }
1442                 transpolyend();
1443         }
1444 }