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