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