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