]> icculus.org git repositories - divverent/darkplaces.git/blob - r_part.c
smoke effects changed a bit (and the smoke texture generator as well), rockets trail...
[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                   4096    // default max # of particles at one time
24 #define ABSOLUTE_MIN_PARTICLES  512             // no fewer than this no matter what's on the command line
25
26 // LordHavoc: added dust, smoke, snow, bloodcloud, and many others
27 typedef enum {
28         pt_static, pt_grav, pt_blob, pt_blob2, pt_smoke, pt_snow, pt_rain, pt_bloodcloud, pt_fallfadespark, pt_bubble, pt_fade, pt_smokecloud, pt_splash
29 } ptype_t;
30
31 typedef struct particle_s
32 {
33         vec3_t          org;
34         float           color;
35         vec3_t          vel;
36         float           die;
37         ptype_t         type;
38         float           scale;
39         short           texnum;
40         float           alpha; // 0-255
41         float           time2; // used for various things (snow fluttering, for example)
42         vec3_t          oldorg;
43         vec3_t          vel2; // used for snow fluttering (base velocity, wind for instance)
44 } particle_t;
45
46 int             ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
47 int             ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
48 int             ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
49
50 int             particletexture;
51 int             smokeparticletexture[8];
52 int             rainparticletexture;
53 int             bubbleparticletexture;
54
55 particle_t      *particles;
56 int                     r_numparticles;
57
58 vec3_t                  r_pright, r_pup, r_ppn;
59
60 int                     numparticles;
61 particle_t      **freeparticles; // list used only in compacting particles array
62
63 // LordHavoc: reduced duplicate code, and allow particle allocation system independence
64 #define ALLOCPARTICLE \
65         if (numparticles >= r_numparticles)\
66                 return;\
67         p = &particles[numparticles++];
68
69 cvar_t r_particles = {"r_particles", "1"};
70 cvar_t r_dynamicparticles = {"r_dynamicparticles", "0", TRUE};
71
72 byte shadebubble(float dx, float dy, vec3_t light)
73 {
74         float   dz, f, dot;
75         vec3_t  normal;
76         if ((dx*dx+dy*dy) < 1) // it does hit the sphere
77         {
78                 dz = 1 - (dx*dx+dy*dy);
79                 f = 0;
80                 // back side
81                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
82                 VectorNormalize(normal);
83                 dot = DotProduct(normal, light);
84                 if (dot > 0.5) // interior reflection
85                         f += ((dot *  2) - 1);
86                 else if (dot < -0.5) // exterior reflection
87                         f += ((dot * -2) - 1);
88                 // front side
89                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
90                 VectorNormalize(normal);
91                 dot = DotProduct(normal, light);
92                 if (dot > 0.5) // interior reflection
93                         f += ((dot *  2) - 1);
94                 else if (dot < -0.5) // exterior reflection
95                         f += ((dot * -2) - 1);
96                 f *= 128;
97                 f += 16; // just to give it a haze so you can see the outline
98                 f = bound(0, f, 255);
99                 return (byte) f;
100         }
101         else
102                 return 0;
103 }
104
105 void R_InitParticleTexture (void)
106 {
107         int             x,y,d,i,m;
108         float   dx, dy;
109         byte    data[32][32][4], noise1[128][128], noise2[128][128];
110         vec3_t  light;
111
112         for (y = 0;y < 32;y++)
113         {
114                 dy = y - 16;
115                 for (x = 0;x < 32;x++)
116                 {
117                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
118                         dx = x - 16;
119                         d = (255 - (dx*dx+dy*dy));
120                         if (d < 0) d = 0;
121                         data[y][x][3] = (byte) d;
122                 }
123         }
124         particletexture = GL_LoadTexture ("particletexture", 32, 32, &data[0][0][0], true, true, 4);
125
126         for (i = 0;i < 8;i++)
127         {
128                 do
129                 {
130                         fractalnoise(&noise1[0][0], 128, 8);
131                         //memset(noise1, 255, 32*32);
132                         fractalnoise(&noise2[0][0], 128, 16);
133                         m = 0;
134                         for (y = 0;y < 32;y++)
135                         {
136                                 dy = y - 16;
137                                 for (x = 0;x < 32;x++)
138                                 {
139                                         int j, k;
140 //                                      k = noise1[y][x];
141                                         k = (noise1[y][x] >> 1) + 128;
142 //                                      data[y][x][0] = data[y][x][1] = data[y][x][2] = (noise1[y][x] >> 1) + 128;
143 //                                      data[y][x][3] = 0;
144                                         data[y][x][0] = data[y][x][1] = data[y][x][2] = data[y][x][3] = 0;
145                                         dx = x - 16;
146                                         d = ((noise2[y][x] * 448) >> 8) - 128;
147 //                                      d = ((noise2[y][x] * 384) >> 8) - 128;
148 //                                      d = noise2[y][x];
149                                         if (d > 0)
150                                         {
151                                                 if (d > 255)
152                                                         d = 255;
153                                                 //d = (d * (255 - (int) (dx*dx+dy*dy))) >> 7;
154                                                 j = (sqrt(dx*dx+dy*dy) * 2.0f - 16.0f);
155                                                 if (j > 0)
156                                                         d = (d * (255 - j*j)) >> 8;
157                                                 if (d < 0) d = 0;
158                                                 if (d > 255) d = 255;
159                                                 data[y][x][3] = (byte) d;
160                                                 if (m < d)
161                                                         m = d;
162                                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = ((k * d) >> 8);
163                                         }
164                                 }
165                         }
166                 }
167                 while (m < 192);
168
169                 smokeparticletexture[i] = GL_LoadTexture (va("smokeparticletexture%d", i), 32, 32, &data[0][0][0], true, true, 4);
170         }
171
172         light[0] = 1;light[1] = 1;light[2] = 1;
173         VectorNormalize(light);
174         for (x=0 ; x<32 ; x++)
175         {
176                 for (y=0 ; y<32 ; y++)
177                 {
178                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
179                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 8.0), y < 24 ? (y - 24) * (1.0 / 24.0) : (y - 24) * (1.0 / 8.0), light);
180                 }
181         }
182         rainparticletexture = GL_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], true, true, 4);
183
184         light[0] = 1;light[1] = 1;light[2] = 1;
185         VectorNormalize(light);
186         for (x=0 ; x<32 ; x++)
187         {
188                 for (y=0 ; y<32 ; y++)
189                 {
190                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
191                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
192                 }
193         }
194         bubbleparticletexture = GL_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], true, true, 4);
195 }
196
197 void r_part_start()
198 {
199         particles = (particle_t *) malloc (r_numparticles * sizeof(particle_t));
200         freeparticles = (void *) malloc (r_numparticles * sizeof(particle_t *));
201         R_InitParticleTexture ();
202 }
203
204 void r_part_shutdown()
205 {
206         free(particles);
207         free(freeparticles);
208 }
209
210 /*
211 ===============
212 R_InitParticles
213 ===============
214 */
215 void R_Particles_Init (void)
216 {
217         int             i;
218
219         i = COM_CheckParm ("-particles");
220
221         if (i)
222         {
223                 r_numparticles = (int)(atoi(com_argv[i+1]));
224                 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
225                         r_numparticles = ABSOLUTE_MIN_PARTICLES;
226         }
227         else
228         {
229                 r_numparticles = MAX_PARTICLES;
230         }
231
232         Cvar_RegisterVariable (&r_particles);
233         Cvar_RegisterVariable (&r_dynamicparticles);
234
235         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown);
236 }
237
238 #define particle(ptype, pcolor, ptex, pscale, palpha, ptime, px, py, pz, pvx, pvy, pvz)\
239 {\
240         particle_t      *p;\
241         ALLOCPARTICLE\
242         p->type = (ptype);\
243         p->color = (pcolor);\
244         p->texnum = (ptex);\
245         p->scale = (pscale);\
246         p->alpha = (palpha);\
247         p->die = cl.time + (ptime);\
248         p->org[0] = (px);\
249         p->org[1] = (py);\
250         p->org[2] = (pz);\
251         p->vel[0] = (pvx);\
252         p->vel[1] = (pvy);\
253         p->vel[2] = (pvz);\
254 }
255 #define particle2(ptype, pcolor, ptex, pscale, palpha, ptime, pbase, poscale, pvscale)\
256 {\
257         particle_t      *p;\
258         ALLOCPARTICLE\
259         p->type = (ptype);\
260         p->color = (pcolor);\
261         p->texnum = (ptex);\
262         p->scale = (pscale);\
263         p->alpha = (palpha);\
264         p->die = cl.time + (ptime);\
265         p->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
266         p->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
267         p->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
268         p->vel[0] = lhrandom(-(pvscale), (pvscale));\
269         p->vel[1] = lhrandom(-(pvscale), (pvscale));\
270         p->vel[2] = lhrandom(-(pvscale), (pvscale));\
271 }
272 #define particle3(ptype, pcolor, ptex, pscale, palpha, ptime, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
273 {\
274         particle_t      *p;\
275         ALLOCPARTICLE\
276         p->type = (ptype);\
277         p->color = (pcolor);\
278         p->texnum = (ptex);\
279         p->scale = (pscale);\
280         p->alpha = (palpha);\
281         p->die = cl.time + (ptime);\
282         p->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
283         p->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
284         p->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
285         p->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
286         p->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
287         p->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
288 }
289 /*
290 void particle(int type, int color, int tex, float scale, int alpha, float time, float x, float y, float z, float vx, float vy, float vz)
291 {
292         particle_t      *p;
293         ALLOCPARTICLE
294
295         p->type = type;
296         p->color = color;
297         p->texnum = tex;
298         p->scale = scale;
299         p->alpha = alpha;
300         p->die = cl.time + time;
301         p->org[0] = x;
302         p->org[1] = y;
303         p->org[2] = z;
304         p->vel[0] = vx;
305         p->vel[1] = vy;
306         p->vel[2] = vz;
307 }
308 void particle2(int type, int color, int tex, float scale, int alpha, float time, vec3_t base, float oscale, float vscale)
309 {
310         particle_t      *p;
311         ALLOCPARTICLE
312
313         p->type = type;
314         p->color = color;
315         p->texnum = tex;
316         p->scale = scale;
317         p->alpha = alpha;
318         p->die = cl.time + time;
319         p->org[0] = lhrandom(-oscale, oscale) + base[0];
320         p->org[1] = lhrandom(-oscale, oscale) + base[1];
321         p->org[2] = lhrandom(-oscale, oscale) + base[2];
322         p->vel[0] = lhrandom(-vscale, vscale);
323         p->vel[1] = lhrandom(-vscale, vscale);
324         p->vel[2] = lhrandom(-vscale, vscale);
325 }
326 void particle3(int type, int color, int tex, float scale, int alpha, float time, vec3_t base, float scalex, float scaley, float scalez, float vscalex, float vscaley, float vscalez)
327 {
328         particle_t      *p;
329         ALLOCPARTICLE
330
331         p->type = type;
332         p->color = color;
333         p->texnum = tex;
334         p->scale = scale;
335         p->alpha = alpha;
336         p->die = cl.time + time;
337         p->org[0] = lhrandom(-scalex, scalex) + base[0];
338         p->org[1] = lhrandom(-scaley, scaley) + base[1];
339         p->org[2] = lhrandom(-scalez, scalez) + base[2];
340         p->vel[0] = lhrandom(-vscalex, vscalex);
341         p->vel[1] = lhrandom(-vscaley, vscaley);
342         p->vel[2] = lhrandom(-vscalez, vscalez);
343 }
344 */
345
346 /*
347 ===============
348 R_EntityParticles
349 ===============
350 */
351
352 #define NUMVERTEXNORMALS        162
353 extern  float   r_avertexnormals[NUMVERTEXNORMALS][3];
354 vec3_t  avelocities[NUMVERTEXNORMALS];
355 float   beamlength = 16;
356 vec3_t  avelocity = {23, 7, 3};
357 float   partstep = 0.01;
358 float   timescale = 0.01;
359
360 void R_EntityParticles (entity_t *ent)
361 {
362         int                     count;
363         int                     i;
364         float           angle;
365         float           sp, sy, cp, cy;
366         vec3_t          forward;
367         float           dist;
368         if (!r_particles.value) return; // LordHavoc: particles are optional
369         
370         dist = 64;
371         count = 50;
372
373         if (!avelocities[0][0])
374                 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
375                         avelocities[0][i] = (rand()&255) * 0.01;
376
377         for (i=0 ; i<NUMVERTEXNORMALS ; i++)
378         {
379                 angle = cl.time * avelocities[i][0];
380                 sy = sin(angle);
381                 cy = cos(angle);
382                 angle = cl.time * avelocities[i][1];
383                 sp = sin(angle);
384                 cp = cos(angle);
385         
386                 forward[0] = cp*cy;
387                 forward[1] = cp*sy;
388                 forward[2] = -sp;
389
390                 particle(pt_static, 0x6f, particletexture, 2, 255, 0, ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0);
391         }
392 }
393
394
395 /*
396 ===============
397 R_ClearParticles
398 ===============
399 */
400 void R_ClearParticles (void)
401 {
402 //      int             i;
403 //      free_particles = &particles[0];
404 //      active_particles = NULL;
405
406 //      for (i=0 ;i<r_numparticles ; i++)
407 //              particles[i].next = &particles[i+1];
408 //      particles[r_numparticles-1].next = NULL;
409
410         numparticles = 0;
411 }
412
413
414 void R_ReadPointFile_f (void)
415 {
416         FILE    *f;
417         vec3_t  org;
418         int             r;
419         int             c;
420         char    name[MAX_OSPATH];
421         
422         sprintf (name,"maps/%s.pts", sv.name);
423
424         COM_FOpenFile (name, &f, false);
425         if (!f)
426         {
427                 Con_Printf ("couldn't open %s\n", name);
428                 return;
429         }
430         
431         Con_Printf ("Reading %s...\n", name);
432         c = 0;
433         for (;;)
434         {
435                 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
436                 if (r != 3)
437                         break;
438                 c++;
439                 
440                 if (numparticles >= r_numparticles)
441                 {
442                         Con_Printf ("Not enough free particles\n");
443                         break;
444                 }
445                 particle(pt_static, (-c)&15, particletexture, 2, 255, 99999, org[0], org[1], org[2], 0, 0, 0);
446         }
447
448         fclose (f);
449         Con_Printf ("%i points read\n", c);
450 }
451
452 /*
453 ===============
454 R_ParseParticleEffect
455
456 Parse an effect out of the server message
457 ===============
458 */
459 void R_ParseParticleEffect (void)
460 {
461         vec3_t          org, dir;
462         int                     i, count, msgcount, color;
463         
464         for (i=0 ; i<3 ; i++)
465                 org[i] = MSG_ReadCoord ();
466         for (i=0 ; i<3 ; i++)
467                 dir[i] = MSG_ReadChar () * (1.0/16);
468         msgcount = MSG_ReadByte ();
469         color = MSG_ReadByte ();
470
471 if (msgcount == 255)
472         count = 1024;
473 else
474         count = msgcount;
475         
476         R_RunParticleEffect (org, dir, color, count);
477 }
478         
479 /*
480 ===============
481 R_ParticleExplosion
482
483 ===============
484 */
485 void R_ParticleExplosion (vec3_t org, int smoke)
486 {
487         int                     i;
488         if (!r_particles.value) return; // LordHavoc: particles are optional
489
490         particle(pt_smokecloud, (rand()&7) + 8, smokeparticletexture[rand()&7], 30, 255, 2, org[0], org[1], org[2], 0, 0, 0);
491
492         i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
493         if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
494         {
495                 for (i=0 ; i<128 ; i++)
496                         particle2(pt_bubble, (rand()&3) + 12, bubbleparticletexture, lhrandom(1, 2), 255, 2, org, 16, 96);
497         }
498         else
499         {
500                 for (i = 0;i < 256;i++)
501                         particle(pt_fallfadespark, ramp3[rand()%6], particletexture, 1.5, lhrandom(128, 255), 5, lhrandom(-16, 16) + org[0], lhrandom(-16, 16) + org[1], lhrandom(-16, 16) + org[2], lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192) + 192);
502         }
503
504 }
505
506 /*
507 ===============
508 R_ParticleExplosion2
509
510 ===============
511 */
512 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
513 {
514         int                     i;
515         if (!r_particles.value) return; // LordHavoc: particles are optional
516
517         for (i = 0;i < 512;i++)
518                 particle2(pt_fade, colorStart + (i % colorLength), particletexture, 1.5, 255, 0.3, org, 8, 192);
519 }
520
521 /*
522 ===============
523 R_BlobExplosion
524
525 ===============
526 */
527 void R_BlobExplosion (vec3_t org)
528 {
529         int                     i;
530         if (!r_particles.value) return; // LordHavoc: particles are optional
531         
532         for (i=0 ; i<512 ; i++)
533                 particle3(pt_blob, 66+(rand()%6), particletexture, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
534         for (i=0 ; i<512 ; i++)
535                 particle3(pt_blob2, 150+(rand()%6), particletexture, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
536 }
537
538 /*
539 ===============
540 R_RunParticleEffect
541
542 ===============
543 */
544 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
545 {
546         if (!r_particles.value) return; // LordHavoc: particles are optional
547         
548         if (count == 1024)
549         {
550                 R_ParticleExplosion(org, false);
551                 return;
552         }
553         color &= ~7;
554         if (count & 7)
555         {
556                 particle2(pt_fade, color + (rand()&7), particletexture, 6, (count & 7) * 16 + (rand()&15), 1, org, 8, 15);
557                 count &= ~7;
558         }
559         count >>= 3;
560         while (count--)
561                 particle2(pt_fade, color + (rand()&7), particletexture, 6, 128, 1, org, 8, 15);
562 }
563
564 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
565 /*
566 ===============
567 R_SparkShower
568 ===============
569 */
570 void R_SparkShower (vec3_t org, vec3_t dir, int count)
571 {
572         if (!r_particles.value) return; // LordHavoc: particles are optional
573
574         // smoke puff
575         particle(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], 8, 160, 99, org[0], org[1], org[2], 0, 0, 0);
576         // sparks
577         while(count--)
578 //              particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, 1, lhrandom(0, 255), 5, org, 4, 96);
579                 particle(pt_fallfadespark, ramp3[rand()%6], particletexture, 1, lhrandom(0, 255), 5, lhrandom(-4, 4) + org[0], lhrandom(-4, 4) + org[1], lhrandom(-4, 4) + org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64) + 64);
580 }
581
582 void R_BloodPuff (vec3_t org)
583 {
584         if (!r_particles.value) return; // LordHavoc: particles are optional
585
586         particle(pt_bloodcloud, 68+(rand()&3), smokeparticletexture[rand()&7], 12, 128, 99, org[0], org[1], org[2], 0, 0, 0);
587         particle(pt_bloodcloud, 68+(rand()&3), smokeparticletexture[rand()&7], 10, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
588         particle(pt_bloodcloud, 68+(rand()&3), smokeparticletexture[rand()&7], 8, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
589 }
590
591 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
592 {
593         int                     j;
594         particle_t      *p;
595         vec3_t          diff;
596         vec3_t          center;
597         vec3_t          velscale;
598         if (!r_particles.value) return; // LordHavoc: particles are optional
599
600         VectorSubtract(maxs, mins, diff);
601         center[0] = (mins[0] + maxs[0]) * 0.5;
602         center[1] = (mins[1] + maxs[1]) * 0.5;
603         center[2] = (mins[2] + maxs[2]) * 0.5;
604         // FIXME: change velspeed back to 2.0x after fixing mod
605         velscale[0] = velspeed * 0.5 / diff[0];
606         velscale[1] = velspeed * 0.5 / diff[1];
607         velscale[2] = velspeed * 0.5 / diff[2];
608         
609         while (count--)
610         {
611                 ALLOCPARTICLE
612
613                 p->texnum = smokeparticletexture[rand()&7];
614                 p->scale = lhrandom(4, 6);
615                 p->alpha = 96 + (rand()&63);
616                 p->die = cl.time + 2;
617                 p->type = pt_bloodcloud;
618                 p->color = (rand()&3)+68;
619                 for (j=0 ; j<3 ; j++)
620                 {
621                         p->org[j] = diff[j] * (float) (rand()%1024) * (1.0 / 1024.0) + mins[j];
622                         p->vel[j] = (p->org[j] - center[j]) * velscale[j];
623                 }
624         }
625 }
626
627 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
628 {
629         int                     j;
630         particle_t      *p;
631         vec3_t          diff;
632         float           t;
633         if (!r_particles.value) return; // LordHavoc: particles are optional
634         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
635         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
636         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
637
638         VectorSubtract(maxs, mins, diff);
639         
640         while (count--)
641         {
642                 ALLOCPARTICLE
643
644                 p->texnum = particletexture;
645                 p->scale = 6;
646                 p->alpha = 255;
647                 p->die = cl.time + 1 + (rand()&15)*0.0625;
648                 if (gravity)
649                         p->type = pt_grav;
650                 else
651                         p->type = pt_static;
652                 p->color = colorbase + (rand()&3);
653                 for (j=0 ; j<3 ; j++)
654                 {
655                         p->org[j] = diff[j] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[j];
656                         if (randomvel)
657                                 p->vel[j] = dir[j] + (rand()%randomvel)-(randomvel*0.5);
658                         else
659                                 p->vel[j] = 0;
660                 }
661         }
662 }
663
664 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
665 {
666         int                     i;
667         particle_t      *p;
668         vec3_t          diff;
669         vec3_t          org;
670         vec3_t          vel;
671         float           t, z;
672         if (!r_particles.value) return; // LordHavoc: particles are optional
673         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
674         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
675         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
676         if (dir[2] < 0) // falling
677         {
678                 t = (maxs[2] - mins[2]) / -dir[2];
679                 z = maxs[2];
680         }
681         else // rising??
682         {
683                 t = (maxs[2] - mins[2]) / dir[2];
684                 z = mins[2];
685         }
686         if (t < 0 || t > 2) // sanity check
687                 t = 2;
688         t += cl.time;
689
690         VectorSubtract(maxs, mins, diff);
691         
692         for (i=0 ; i<count ; i++)
693         {
694                 ALLOCPARTICLE
695
696                 vel[0] = dir[0] + (rand()&31) - 16;
697                 vel[1] = dir[1] + (rand()&31) - 16;
698                 vel[2] = dir[2] + (rand()&63) - 32;
699                 org[0] = diff[0] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[0];
700                 org[1] = diff[1] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[1];
701                 org[2] = z;
702
703                 p->alpha = 255;
704                 p->die = t;
705                 if (type == 1)
706                 {
707                         p->scale = 2;
708                         p->texnum = particletexture;
709                         p->type = pt_snow;
710                 }
711                 else // 0
712                 {
713                         p->scale = 3;
714                         p->texnum = rainparticletexture;
715                         p->type = pt_rain;
716                 }
717                 p->color = colorbase + (rand()&3);
718                 VectorCopy(org, p->org);
719                 VectorCopy(vel, p->vel);
720                 VectorCopy(vel, p->vel2);
721         }
722 }
723
724
725 /*
726 ===============
727 R_LavaSplash
728
729 ===============
730 */
731 void R_LavaSplash (vec3_t org)
732 {
733         int                     i, j;
734         particle_t      *p;
735         float           vel;
736         vec3_t          dir;
737         if (!r_particles.value) return; // LordHavoc: particles are optional
738
739         for (i=-128 ; i<128 ; i+=16)
740                 for (j=-128 ; j<128 ; j+=16)
741                 {
742                         ALLOCPARTICLE
743                 
744                         p->texnum = particletexture;
745                         p->scale = 10;
746                         p->alpha = 128;
747                         p->die = cl.time + 2 + (rand()&31) * 0.02;
748                         p->color = 224 + (rand()&7);
749                         p->type = pt_grav;
750                         
751                         dir[0] = j + (rand()&7);
752                         dir[1] = i + (rand()&7);
753                         dir[2] = 256;
754
755                         p->org[0] = org[0] + dir[0];
756                         p->org[1] = org[1] + dir[1];
757                         p->org[2] = org[2] + (rand()&63);
758
759                         VectorNormalize (dir);                                          
760                         vel = 50 + (rand()&63);
761                         VectorScale (dir, vel, p->vel);
762                 }
763 }
764
765 /*
766 ===============
767 R_TeleportSplash
768
769 ===============
770 */
771 void R_TeleportSplash (vec3_t org)
772 {
773         int                     i, j, k;
774         particle_t      *p;
775         if (!r_particles.value) return; // LordHavoc: particles are optional
776
777         for (i=-16 ; i<16 ; i+=8)
778                 for (j=-16 ; j<16 ; j+=8)
779                         for (k=-24 ; k<32 ; k+=8)
780                         {
781                                 ALLOCPARTICLE
782                 
783                                 p->texnum = particletexture;
784                                 p->scale = 1;
785                                 p->alpha = lhrandom(32,128);
786                                 p->die = cl.time + 5;
787                                 p->color = 254;
788                                 p->type = pt_fade;
789                                 
790                                 p->org[0] = org[0] + i + (rand()&7);
791                                 p->org[1] = org[1] + j + (rand()&7);
792                                 p->org[2] = org[2] + k + (rand()&7);
793         
794                                 p->vel[0] = i*2 + (rand()%25) - 12;
795                                 p->vel[1] = j*2 + (rand()%25) - 12;
796                                 p->vel[2] = k*2 + (rand()%25) - 12 + 40;
797                         }
798 }
799
800 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
801 {
802         vec3_t          vec;
803         float           len, dec = 0, t, nt, speed;
804         int                     j, contents, bubbles;
805         particle_t      *p;
806         if (!r_particles.value) return; // LordHavoc: particles are optional
807
808         t = cl.oldtime;
809         nt = cl.time;
810         if (ent->trail_leftover < 0)
811                 ent->trail_leftover = 0;
812         t += ent->trail_leftover;
813         ent->trail_leftover -= (cl.time - cl.oldtime);
814         if (t >= cl.time)
815                 return;
816
817         contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
818         if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
819                 return;
820
821         VectorSubtract (end, start, vec);
822         len = VectorNormalizeLength (vec);
823         if (len <= 0.01f)
824                 return;
825         speed = len / (nt - t);
826
827         bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
828
829         while (t < nt)
830         {
831                 ALLOCPARTICLE
832                 
833                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
834                 p->die = cl.time + 2;
835
836                 switch (type)
837                 {
838                         case 0: // rocket trail
839                         case 1: // grenade trail
840                                 if (bubbles)
841                                 {
842                                         dec = 0.005f;
843                                         p->texnum = bubbleparticletexture;
844                                         p->scale = lhrandom(1,2);
845                                         p->alpha = 255;
846                                         p->color = 254;
847                                         p->type = pt_bubble;
848                                         p->die = cl.time + 2;
849                                         for (j=0 ; j<3 ; j++)
850                                         {
851                                                 p->vel[j] = (rand()&31)-16;
852                                                 p->org[j] = start[j] + ((rand()&3)-2);
853                                         }
854                                 }
855                                 else
856                                 {
857                                         dec = 0.02f;
858                                         p->texnum = smokeparticletexture[rand()&7];
859                                         p->scale = lhrandom(4, 8);
860                                         p->alpha = 255; //128 + (rand()&63);
861                                         p->color = 254;
862                                         p->type = pt_smoke;
863                                         p->die = cl.time + 10000;
864                                         VectorCopy(start, p->org);
865                                         particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
866                                         particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
867                                         particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
868                                         particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
869                                 }
870                                 break;
871
872                                 /*
873                         case 1: // smoke smoke
874                                 dec = 0.016f;
875                                 p->texnum = smokeparticletexture;
876                                 p->scale = lhrandom(6,9);
877                                 p->alpha = 64;
878                                 if (r_smokecolor.value)
879                                         p->color = r_smokecolor.value;
880                                 else
881                                         p->color = (rand()&3)+12;
882                                 p->type = pt_smoke;
883                                 p->die = cl.time + 1;
884                                 VectorCopy(start, p->org);
885                                 break;
886                                 */
887
888                         case 2: // blood
889                         case 4: // slight blood
890                                 dec = 0.025f;
891                                 p->texnum = smokeparticletexture[rand()&7];
892                                 p->scale = lhrandom(4, 6);
893                                 p->alpha = type == 4 ? 192 : 255;
894                                 p->color = (rand()&3)+68;
895                                 p->type = pt_bloodcloud;
896                                 p->die = cl.time + 9999;
897                                 for (j=0 ; j<3 ; j++)
898                                 {
899                                         p->vel[j] = (rand()&15)-8;
900                                         p->org[j] = start[j] + ((rand()&3)-2);
901                                 }
902                                 break;
903
904                         case 3:
905                         case 5: // tracer
906                                 dec = 0.02f;
907                                 p->texnum = smokeparticletexture[rand()&7];
908                                 p->scale = 4;
909                                 p->alpha = 64 + (rand()&31);
910                                 p->color = type == 3 ? 56 : 234;
911                                 p->type = pt_fade;
912                                 p->die = cl.time + 10000;
913                                 VectorCopy(start, p->org);
914                                 break;
915
916                         case 6: // voor trail
917                                 dec = 0.05f; // sparse trail
918                                 p->texnum = smokeparticletexture[rand()&7];
919                                 p->scale = lhrandom(3, 5);
920                                 p->alpha = 255;
921                                 p->color = 9*16 + 8 + (rand()&3);
922                                 p->type = pt_fade;
923                                 p->die = cl.time + 2;
924                                 for (j=0 ; j<3 ; j++)
925                                 {
926                                         p->vel[j] = (rand()&15)-8;
927                                         p->org[j] = start[j] + ((rand()&3)-2);
928                                 }
929                                 break;
930
931                         case 7: // Nehahra smoke tracer
932                                 dec = 0.14f;
933                                 p->texnum = smokeparticletexture[rand()&7];
934                                 p->scale = lhrandom(8, 12);
935                                 p->alpha = 64;
936                                 p->color = (rand()&3)+12;
937                                 p->type = pt_smoke;
938                                 p->die = cl.time + 10000;
939                                 for (j=0 ; j<3 ; j++)
940                                         p->org[j] = start[j] + ((rand()&3)-2);
941                                 break;
942                 }
943                 
944                 t += dec;
945                 dec *= speed;
946                 VectorMA (start, dec, vec, start);
947         }
948         ent->trail_leftover = t - cl.time;
949 }
950
951 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
952 {
953         vec3_t          vec;
954         int                     len;
955         if (!r_particles.value) return; // LordHavoc: particles are optional
956
957         VectorSubtract (end, start, vec);
958         len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
959         VectorScale(vec, 3, vec);
960         while (len--)
961         {
962                 particle(pt_smoke, color, particletexture, 8, 192, 99, start[0], start[1], start[2], 0, 0, 0);
963                 VectorAdd (start, vec, start);
964         }
965 }
966
967
968 /*
969 ===============
970 R_DrawParticles
971 ===============
972 */
973 extern  cvar_t  sv_gravity;
974 void R_CompleteLightPoint (vec3_t color, vec3_t p);
975
976 void TraceLine (vec3_t start, vec3_t end, vec3_t impact);
977
978 void R_DrawParticles (void)
979 {
980         particle_t              *p;
981         int                             i, r,g,b,a;
982         float                   gravity, dvel, frametime, scale, scale2, minparticledist;
983         byte                    *color24;
984         vec3_t                  up, right, uprightangles, forward2, up2, right2, tempcolor, v;
985         int                             activeparticles, maxparticle, j, k;
986
987         // LordHavoc: early out condition
988         if (!numparticles)
989                 return;
990
991         VectorScale (vup, 1.5, up);
992         VectorScale (vright, 1.5, right);
993
994         uprightangles[0] = 0;
995         uprightangles[1] = r_refdef.viewangles[1];
996         uprightangles[2] = 0;
997         AngleVectors (uprightangles, forward2, right2, up2);
998
999         frametime = cl.time - cl.oldtime;
1000         gravity = frametime * sv_gravity.value;
1001         dvel = 1+4*frametime;
1002
1003         minparticledist = DotProduct(r_refdef.vieworg, vpn) + 16.0f;
1004
1005         activeparticles = 0;
1006         maxparticle = -1;
1007         j = 0;
1008         for (k = 0, p = particles;k < numparticles;k++, p++)
1009         {
1010                 if (p->die < cl.time)
1011                 {
1012                         freeparticles[j++] = p;
1013                         continue;
1014                 }
1015                 maxparticle = k;
1016                 activeparticles++;
1017
1018                 // LordHavoc: only render if not too close
1019                 if (DotProduct(p->org, vpn) >= minparticledist)
1020                 {
1021                         color24 = (byte *) &d_8to24table[(int)p->color];
1022                         r = color24[0];
1023                         g = color24[1];
1024                         b = color24[2];
1025                         a = p->alpha;
1026                         if (r_dynamicparticles.value)
1027                         {
1028                                 R_CompleteLightPoint(tempcolor, p->org);
1029                                 r = (r * (int) tempcolor[0]) >> 7;
1030                                 g = (g * (int) tempcolor[1]) >> 7;
1031                                 b = (b * (int) tempcolor[2]) >> 7;
1032                         }
1033                         transpolybegin(p->texnum, 0, p->texnum, TPOLYTYPE_ALPHA);
1034                         scale = p->scale * -0.5;scale2 = p->scale * 0.5;
1035                         if (p->texnum == rainparticletexture) // rain streak
1036                         {
1037                                 transpolyvert(p->org[0] + up2[0]*scale  + right2[0]*scale , p->org[1] + up2[1]*scale  + right2[1]*scale , p->org[2] + up2[2]*scale  + right2[2]*scale , 0,1,r,g,b,a);
1038                                 transpolyvert(p->org[0] + up2[0]*scale2 + right2[0]*scale , p->org[1] + up2[1]*scale2 + right2[1]*scale , p->org[2] + up2[2]*scale2 + right2[2]*scale , 0,0,r,g,b,a);
1039                                 transpolyvert(p->org[0] + up2[0]*scale2 + right2[0]*scale2, p->org[1] + up2[1]*scale2 + right2[1]*scale2, p->org[2] + up2[2]*scale2 + right2[2]*scale2, 1,0,r,g,b,a);
1040                                 transpolyvert(p->org[0] + up2[0]*scale  + right2[0]*scale2, p->org[1] + up2[1]*scale  + right2[1]*scale2, p->org[2] + up2[2]*scale  + right2[2]*scale2, 1,1,r,g,b,a);
1041                         }
1042                         else
1043                         {
1044                                 transpolyvert(p->org[0] + up[0]*scale  + right[0]*scale , p->org[1] + up[1]*scale  + right[1]*scale , p->org[2] + up[2]*scale  + right[2]*scale , 0,1,r,g,b,a);
1045                                 transpolyvert(p->org[0] + up[0]*scale2 + right[0]*scale , p->org[1] + up[1]*scale2 + right[1]*scale , p->org[2] + up[2]*scale2 + right[2]*scale , 0,0,r,g,b,a);
1046                                 transpolyvert(p->org[0] + up[0]*scale2 + right[0]*scale2, p->org[1] + up[1]*scale2 + right[1]*scale2, p->org[2] + up[2]*scale2 + right[2]*scale2, 1,0,r,g,b,a);
1047                                 transpolyvert(p->org[0] + up[0]*scale  + right[0]*scale2, p->org[1] + up[1]*scale  + right[1]*scale2, p->org[2] + up[2]*scale  + right[2]*scale2, 1,1,r,g,b,a);
1048                         }
1049                         transpolyend();
1050                 }
1051
1052                 VectorCopy(p->org, p->oldorg);
1053                 p->org[0] += p->vel[0]*frametime;
1054                 p->org[1] += p->vel[1]*frametime;
1055                 p->org[2] += p->vel[2]*frametime;
1056                 
1057                 switch (p->type)
1058                 {
1059                 case pt_static:
1060                         break;
1061
1062                 case pt_blob:
1063                         for (i=0 ; i<3 ; i++)
1064                                 p->vel[i] *= dvel;
1065                         break;
1066
1067                 case pt_blob2:
1068                         for (i=0 ; i<2 ; i++)
1069                                 p->vel[i] *= dvel;
1070                         break;
1071
1072                 case pt_grav:
1073                         p->vel[2] -= gravity;
1074                         break;
1075                 case pt_snow:
1076                         if (cl.time > p->time2)
1077                         {
1078                                 p->time2 = cl.time + (rand() & 3) * 0.1;
1079                                 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1080                                 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1081                                 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1082                         }
1083                         break;
1084                 case pt_bloodcloud:
1085 //                      if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1086 //                      {
1087 //                              p->die = -1;
1088 //                              break;
1089 //                      }
1090                         p->scale += frametime * 16;
1091                         p->alpha -= frametime * 512;
1092                         if (p->alpha < 1 || p->scale < 1)
1093                                 p->die = -1;
1094                         break;
1095                 case pt_fallfadespark:
1096                         p->alpha -= frametime * 256;
1097                         p->vel[2] -= gravity;
1098                         if (p->alpha < 1)
1099                                 p->die = -1;
1100                         break;
1101                 case pt_fade:
1102                         p->alpha -= frametime * 512;
1103                         if (p->alpha < 1)
1104                                 p->die = -1;
1105                         break;
1106                 case pt_bubble:
1107                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1108                         if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1109                         {
1110                                 p->texnum = smokeparticletexture[rand()&7];
1111                                 p->type = pt_splash;
1112                                 p->alpha = 96;
1113                                 p->scale = 5;
1114                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1115                                 p->die = cl.time + 1000;
1116 //                              p->die = -1;
1117                         }
1118                         p->vel[2] += gravity * 0.25;
1119                         p->vel[0] *= (1 - (frametime * 0.0625));
1120                         p->vel[1] *= (1 - (frametime * 0.0625));
1121                         p->vel[2] *= (1 - (frametime * 0.0625));
1122                         if (cl.time > p->time2)
1123                         {
1124                                 p->time2 = cl.time + lhrandom(0, 0.5);
1125                                 p->vel[0] += lhrandom(-32,32);
1126                                 p->vel[1] += lhrandom(-32,32);
1127                                 p->vel[2] += lhrandom(-32,32);
1128                         }
1129                         p->alpha -= frametime * 64;
1130                         if (p->alpha < 1)
1131                                 p->die = -1;
1132                         break;
1133 // LordHavoc: for smoke trails
1134                 case pt_smoke:
1135                         p->vel[2] += gravity * 0.08f;
1136                         p->scale += frametime * 16;
1137                         p->alpha -= frametime * 384;
1138                         if (p->alpha < 16)
1139                                 p->die = -1;
1140                         break;
1141                 case pt_smokecloud:
1142                         p->scale += frametime * 64;
1143                         p->alpha -= frametime * 384;
1144                         if (p->alpha < 16)
1145                                 p->die = -1;
1146                         break;
1147                 case pt_splash:
1148                         p->scale += frametime * 24;
1149                         p->alpha -= frametime * 512;
1150                         if (p->alpha < 1)
1151                                 p->die = -1;
1152                         break;
1153                 case pt_rain:
1154                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1155                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1156                         {
1157                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1158                                         break; // still in solid
1159                                 p->die = cl.time + 1000;
1160                                 switch (a)
1161                                 {
1162                                 case CONTENTS_LAVA:
1163                                 case CONTENTS_SLIME:
1164                                         p->texnum = smokeparticletexture[rand()&7];
1165                                         p->type = pt_smokecloud;
1166                                         p->alpha = 64;
1167                                         p->vel[2] = 96;
1168                                         break;
1169                                 case CONTENTS_WATER:
1170                                         p->texnum = smokeparticletexture[rand()&7];
1171                                         p->type = pt_splash;
1172                                         p->alpha = 96;
1173                                         p->scale = 5;
1174                                         p->vel[0] = p->vel[1] = p->vel[2] = 0;
1175 //                                      p->texnum = bubbleparticletexture;
1176 //                                      p->type = pt_bubble;
1177 //                                      p->vel[2] *= 0.1;
1178                                         break;
1179                                 default: // CONTENTS_SOLID and any others
1180                                         TraceLine(p->oldorg, p->org, v);
1181                                         VectorCopy(v, p->org);
1182                                         p->texnum = smokeparticletexture[rand()&7];
1183                                         p->type = pt_splash;
1184                                         p->alpha = 96;
1185                                         p->scale = 5;
1186                                         p->vel[0] = p->vel[1] = p->vel[2] = 0;
1187                                         p->die = cl.time + 1000;
1188                                         break;
1189                                 }
1190                         }
1191                         break;
1192                 }
1193         }
1194         // fill in gaps to compact the array
1195         i = 0;
1196         while (maxparticle >= activeparticles)
1197         {
1198                 *freeparticles[i++] = particles[maxparticle--];
1199                 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1200                         maxparticle--;
1201         }
1202         numparticles = activeparticles;
1203 }
1204