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