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