]> icculus.org git repositories - divverent/darkplaces.git/blob - r_part.c
-safe now does something approximately close to correct. Not a full fix
[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                   16384   // 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 typedef enum
27 {
28         pt_static, pt_grav, pt_blob, pt_blob2, pt_bulletsmoke, pt_smoke, pt_snow, pt_rain, pt_spark, pt_bubble, pt_fade, pt_steam, pt_splash, pt_splashpuff, pt_flame, pt_blood, pt_oneframe, pt_lavasplash, pt_raindropsplash, pt_underwaterspark, pt_explosionsplash
29 }
30 ptype_t;
31
32 typedef struct
33 {
34         float s1, t1, s2, t2;
35 }
36 particletexture_t;
37
38 typedef struct particle_s
39 {
40         ptype_t         type;
41         vec3_t          org;
42         vec3_t          vel;
43         particletexture_t       *tex;
44         float           die;
45         float           scale;
46         float           alpha; // 0-255
47         float           time2; // used for various things (snow fluttering, for example)
48         float           bounce; // how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide, 2 = keep bouncing forever, 1.5 is typical)
49         vec3_t          oldorg;
50         vec3_t          vel2; // used for snow fluttering (base velocity, wind for instance)
51         float           friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
52         float           pressure; // if non-zero, apply pressure to other particles
53         int                     dynlight; // if set the particle will be dynamically lit (if r_dynamicparticles is on), used for smoke and blood
54         int                     rendermode; // a TPOLYTYPE_ value
55         byte            color[4];
56 }
57 particle_t;
58
59 static int particlepalette[256] =
60 {
61         0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
62         0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
63         0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
64         0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
65         0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
66         0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
67         0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
68         0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
69         0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
70         0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
71         0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
72         0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
73         0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
74         0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
75         0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
76         0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
77         0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
78         0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
79         0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
80         0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
81         0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
82         0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
83         0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
84         0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
85         0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
86         0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
87         0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
88         0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
89         0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
90         0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
91         0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
92         0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
93 };
94
95 static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
96 //static int explounderwatersparkramp[8] = {0x00074b, 0x000f6f, 0x071f93, 0x0f33b7, 0x2b63cf, 0x4f97e3, 0xb5e7ff, 0xffffff};
97
98 static rtexture_t *particlefonttexture;
99
100 static particletexture_t particletexture;
101 static particletexture_t smokeparticletexture[8];
102 static particletexture_t rainparticletexture;
103 static particletexture_t bubbleparticletexture;
104 static particletexture_t bulletholetexture[8];
105 static particletexture_t rocketglowparticletexture;
106 static particletexture_t raindropsplashparticletexture[16];
107
108 static particle_t       *particles;
109 static int                      r_numparticles;
110
111 static int                      numparticles;
112 static particle_t       **freeparticles; // list used only in compacting particles array
113
114 static cvar_t r_particles = {CVAR_SAVE, "r_particles", "1"};
115 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
116 static cvar_t r_particles_lighting = {CVAR_SAVE, "r_particles_lighting", "1"};
117 static cvar_t r_particles_bloodshowers = {CVAR_SAVE, "r_particles_bloodshowers", "1"};
118 static cvar_t r_particles_blood = {CVAR_SAVE, "r_particles_blood", "1"};
119 static cvar_t r_particles_smoke = {CVAR_SAVE, "r_particles_smoke", "1"};
120 static cvar_t r_particles_sparks = {CVAR_SAVE, "r_particles_sparks", "1"};
121 static cvar_t r_particles_bubbles = {CVAR_SAVE, "r_particles_bubbles", "1"};
122 static cvar_t r_particles_explosions = {CVAR_SAVE, "r_particles_explosions", "0"};
123
124 static byte shadebubble(float dx, float dy, vec3_t light)
125 {
126         float   dz, f, dot;
127         vec3_t  normal;
128         dz = 1 - (dx*dx+dy*dy);
129         if (dz > 0) // it does hit the sphere
130         {
131                 f = 0;
132                 // back side
133                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
134                 VectorNormalize(normal);
135                 dot = DotProduct(normal, light);
136                 if (dot > 0.5) // interior reflection
137                         f += ((dot *  2) - 1);
138                 else if (dot < -0.5) // exterior reflection
139                         f += ((dot * -2) - 1);
140                 // front side
141                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
142                 VectorNormalize(normal);
143                 dot = DotProduct(normal, light);
144                 if (dot > 0.5) // interior reflection
145                         f += ((dot *  2) - 1);
146                 else if (dot < -0.5) // exterior reflection
147                         f += ((dot * -2) - 1);
148                 f *= 128;
149                 f += 16; // just to give it a haze so you can see the outline
150                 f = bound(0, f, 255);
151                 return (byte) f;
152         }
153         else
154                 return 0;
155 }
156
157 static void R_InitParticleTexture (void)
158 {
159         int             x,y,d,i,m, texnum;
160         float   dx, dy, radius, f, f2;
161         byte    data[32][32][4], noise1[64][64], noise2[64][64];
162         vec3_t  light;
163         byte    particletexturedata[256][256][4];
164
165         memset(&particletexturedata[0][0][0], 255, sizeof(particletexturedata));
166         texnum = 0;
167         #define SETUPTEX(var)\
168         {\
169                 int basex, basey, y;\
170                 if (texnum >= 64)\
171                 {\
172                         Sys_Error("R_InitParticleTexture: ran out of textures (64)\n");\
173                         return; /* only to hush compiler */ \
174                 }\
175                 basex = (texnum & 7) * 32;\
176                 basey = ((texnum >> 3) & 7) * 32;\
177                 var.s1 = (basex + 1) / 256.0f;\
178                 var.t1 = (basey + 1) / 256.0f;\
179                 var.s2 = (basex + 31) / 256.0f;\
180                 var.t2 = (basey + 31) / 256.0f;\
181                 for (y = 0;y < 32;y++)\
182                         memcpy(&particletexturedata[basey + y][basex][0], &data[y][0][0], 32*4);\
183                 texnum++;\
184         }
185
186         for (y = 0;y < 32;y++)
187         {
188                 dy = y - 16;
189                 for (x = 0;x < 32;x++)
190                 {
191                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
192                         dx = x - 16;
193                         d = (256 - (dx*dx+dy*dy));
194                         d = bound(0, d, 255);
195                         data[y][x][3] = (byte) d;
196                 }
197         }
198         SETUPTEX(particletexture)
199 //      particletexture = R_LoadTexture ("particletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
200
201         for (i = 0;i < 8;i++)
202         {
203                 do
204                 {
205                         fractalnoise(&noise1[0][0], 64, 4);
206                         fractalnoise(&noise2[0][0], 64, 8);
207                         m = 0;
208                         for (y = 0;y < 32;y++)
209                         {
210                                 dy = y - 16;
211                                 for (x = 0;x < 32;x++)
212                                 {
213                                         d = (noise1[y][x] - 128) * 2 + 64; // was + 128
214                                         d = bound(0, d, 255);
215                                         data[y][x][0] = data[y][x][1] = data[y][x][2] = d;
216                                         dx = x - 16;
217                                         d = (noise2[y][x] - 128) * 3 + 192;
218                                         if (d > 0)
219                                                 d = (d * (256 - (int) (dx*dx+dy*dy))) >> 8;
220                                         d = bound(0, d, 255);
221                                         data[y][x][3] = (byte) d;
222                                         if (m < d)
223                                                 m = d;
224                                 }
225                         }
226                 }
227                 while (m < 224);
228
229                 SETUPTEX(smokeparticletexture[i])
230 //              smokeparticletexture[i] = R_LoadTexture (va("smokeparticletexture%02d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
231         }
232
233         light[0] = 1;light[1] = 1;light[2] = 1;
234         VectorNormalize(light);
235         for (y = 0;y < 32;y++)
236         {
237                 for (x = 0;x < 32;x++)
238                 {
239                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
240                         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);
241                 }
242         }
243         SETUPTEX(rainparticletexture)
244 //      rainparticletexture = R_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
245
246         light[0] = 1;light[1] = 1;light[2] = 1;
247         VectorNormalize(light);
248         for (y = 0;y < 32;y++)
249         {
250                 for (x = 0;x < 32;x++)
251                 {
252                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
253                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
254                 }
255         }
256         SETUPTEX(bubbleparticletexture)
257 //      bubbleparticletexture = R_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
258
259         for (i = 0;i < 8;i++)
260         {
261                 float p[32][32];
262                 fractalnoise(&noise1[0][0], 64, 8);
263                 for (y = 0;y < 32;y++)
264                         for (x = 0;x < 32;x++)
265                                 p[y][x] = (noise1[y][x] / 8.0f) - 64.0f;
266                 for (m = 0;m < 32;m++)
267                 {
268                         int j;
269                         float fx, fy, f;
270                         fx = lhrandom(14, 18);
271                         fy = lhrandom(14, 18);
272                         do
273                         {
274                                 dx = lhrandom(-1, 1);
275                                 dy = lhrandom(-1, 1);
276                                 f = (dx * dx + dy * dy);
277                         }
278                         while(f < 0.125f || f > 1.0f);
279                         f = (m + 1) / 40.0f; //lhrandom(0.0f, 1.0);
280                         dx *= 1.0f / 32.0f;
281                         dy *= 1.0f / 32.0f;
282                         for (j = 0;f > 0 && j < (32 * 14);j++)
283                         {
284                                 y = fy;
285                                 x = fx;
286                                 fx += dx;
287                                 fy += dy;
288                                 p[y - 1][x - 1] += f * 0.125f;
289                                 p[y - 1][x    ] += f * 0.25f;
290                                 p[y - 1][x + 1] += f * 0.125f;
291                                 p[y    ][x - 1] += f * 0.25f;
292                                 p[y    ][x    ] += f;
293                                 p[y    ][x + 1] += f * 0.25f;
294                                 p[y + 1][x - 1] += f * 0.125f;
295                                 p[y + 1][x    ] += f * 0.25f;
296                                 p[y + 1][x + 1] += f * 0.125f;
297 //                              f -= (0.5f / (32 * 16));
298                         }
299                 }
300                 for (y = 0;y < 32;y++)
301                 {
302                         for (x = 0;x < 32;x++)
303                         {
304                                 m = p[y][x];
305                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
306                                 data[y][x][3] = (byte) bound(0, m, 255);
307                         }
308                 }
309
310                 SETUPTEX(bulletholetexture[i])
311 //              bulletholetexture[i] = R_LoadTexture (va("bulletholetexture%02d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
312         }
313
314         for (y = 0;y < 32;y++)
315         {
316                 dy = y - 16;
317                 for (x = 0;x < 32;x++)
318                 {
319                         dx = x - 16;
320                         d = (2048.0f / (dx*dx+dy*dy+1)) - 8.0f;
321                         data[y][x][0] = bound(0, d * 1.0f, 255);
322                         data[y][x][1] = bound(0, d * 0.8f, 255);
323                         data[y][x][2] = bound(0, d * 0.5f, 255);
324                         data[y][x][3] = bound(0, d * 1.0f, 255);
325                 }
326         }
327         SETUPTEX(rocketglowparticletexture)
328 //      rocketglowparticletexture = R_LoadTexture ("glowparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
329
330         for (i = 0;i < 16;i++)
331         {
332                 radius = i * 3.0f / 16.0f;
333                 f2 = 255.0f * ((15.0f - i) / 15.0f);
334                 for (y = 0;y < 32;y++)
335                 {
336                         dy = (y - 16) * 0.25f;
337                         for (x = 0;x < 32;x++)
338                         {
339                                 dx = (x - 16) * 0.25f;
340                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
341                                 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
342                                 f = bound(0.0f, f, 255.0f);
343                                 data[y][x][3] = (int) f;
344                         }
345                 }
346                 SETUPTEX(raindropsplashparticletexture[i])
347 //              raindropsplashparticletexture[i] = R_LoadTexture (va("raindropslashparticletexture%02d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
348         }
349
350         particlefonttexture = R_LoadTexture ("particlefont", 256, 256, &particletexturedata[0][0][0], TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
351 }
352
353 static void r_part_start(void)
354 {
355         particles = (particle_t *) qmalloc(r_numparticles * sizeof(particle_t));
356         freeparticles = (void *) qmalloc(r_numparticles * sizeof(particle_t *));
357         numparticles = 0;
358         R_InitParticleTexture ();
359 }
360
361 static void r_part_shutdown(void)
362 {
363         numparticles = 0;
364         qfree(particles);
365         qfree(freeparticles);
366 }
367
368 static void r_part_newmap(void)
369 {
370         numparticles = 0;
371 }
372
373 /*
374 ===============
375 R_InitParticles
376 ===============
377 */
378 void R_ReadPointFile_f (void);
379 void R_Particles_Init (void)
380 {
381         int             i;
382
383         i = COM_CheckParm ("-particles");
384
385         if (i)
386         {
387                 r_numparticles = (int)(atoi(com_argv[i+1]));
388                 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
389                         r_numparticles = ABSOLUTE_MIN_PARTICLES;
390         }
391         else
392         {
393                 r_numparticles = MAX_PARTICLES;
394         }
395
396         Cmd_AddCommand ("pointfile", R_ReadPointFile_f);
397
398         Cvar_RegisterVariable (&r_particles);
399         Cvar_RegisterVariable (&r_drawparticles);
400         Cvar_RegisterVariable (&r_particles_lighting);
401         Cvar_RegisterVariable (&r_particles_bloodshowers);
402         Cvar_RegisterVariable (&r_particles_blood);
403         Cvar_RegisterVariable (&r_particles_smoke);
404         Cvar_RegisterVariable (&r_particles_sparks);
405         Cvar_RegisterVariable (&r_particles_bubbles);
406         Cvar_RegisterVariable (&r_particles_explosions);
407
408         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
409 }
410
411 //void particle(int ptype, int pcolor, int ptex, int prendermode, int plight, float pscale, float palpha, float ptime, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz)
412 #define particle(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz, ptime2, pvx2, pvy2, pvz2, pfriction, ppressure)\
413 {\
414         particle_t      *part;\
415         int tempcolor;\
416         if (numparticles >= r_numparticles)\
417                 return;\
418         part = &particles[numparticles++];\
419         part->type = (ptype);\
420         tempcolor = (pcolor);\
421         part->color[0] = ((tempcolor) >> 16) & 0xFF;\
422         part->color[1] = ((tempcolor) >> 8) & 0xFF;\
423         part->color[2] = (tempcolor) & 0xFF;\
424         part->color[3] = 0xFF;\
425         part->tex = (&ptex);\
426         part->dynlight = (plight);\
427         part->rendermode = (prendermode);\
428         part->scale = (pscale);\
429         part->alpha = (palpha);\
430         part->die = cl.time + (ptime);\
431         part->bounce = (pbounce);\
432         part->org[0] = (px);\
433         part->org[1] = (py);\
434         part->org[2] = (pz);\
435         part->vel[0] = (pvx);\
436         part->vel[1] = (pvy);\
437         part->vel[2] = (pvz);\
438         part->time2 = (ptime2);\
439         part->vel2[0] = (pvx2);\
440         part->vel2[1] = (pvy2);\
441         part->vel2[2] = (pvz2);\
442         part->friction = (pfriction);\
443         part->pressure = (ppressure);\
444 }
445
446 /*
447 ===============
448 R_EntityParticles
449 ===============
450 */
451 void R_EntityParticles (entity_t *ent)
452 {
453         int                     i;
454         float           angle;
455         float           sp, sy, cp, cy;
456         vec3_t          forward;
457         float           dist;
458         float           beamlength;
459         static vec3_t avelocities[NUMVERTEXNORMALS];
460         if (!r_particles.value) return; // LordHavoc: particles are optional
461
462         dist = 64;
463         beamlength = 16;
464
465         if (!avelocities[0][0])
466                 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
467                         avelocities[0][i] = (rand()&255) * 0.01;
468
469         for (i=0 ; i<NUMVERTEXNORMALS ; i++)
470         {
471                 angle = cl.time * avelocities[i][0];
472                 sy = sin(angle);
473                 cy = cos(angle);
474                 angle = cl.time * avelocities[i][1];
475                 sp = sin(angle);
476                 cp = cos(angle);
477
478                 forward[0] = cp*cy;
479                 forward[1] = cp*sy;
480                 forward[2] = -sp;
481
482                 particle(pt_oneframe, particlepalette[0x6f], particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 9999, 0, ent->render.origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->render.origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->render.origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0, 0, 0, 0, 0, 0);
483         }
484 }
485
486
487 void R_ReadPointFile_f (void)
488 {
489         QFile   *f;
490         vec3_t  org;
491         int             r;
492         int             c;
493         char    name[MAX_OSPATH];
494
495         sprintf (name,"maps/%s.pts", sv.name);
496
497         COM_FOpenFile (name, &f, false, true);
498         if (!f)
499         {
500                 Con_Printf ("couldn't open %s\n", name);
501                 return;
502         }
503
504         Con_Printf ("Reading %s...\n", name);
505         c = 0;
506         for (;;)
507         {
508                 char *str = Qgetline (f);
509                 r = sscanf (str,"%f %f %f\n", &org[0], &org[1], &org[2]);
510                 if (r != 3)
511                         break;
512                 c++;
513
514                 if (numparticles >= r_numparticles)
515                 {
516                         Con_Printf ("Not enough free particles\n");
517                         break;
518                 }
519                 particle(pt_static, particlepalette[(-c)&15], particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 99999, 0, org[0], org[1], org[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
520         }
521
522         Qclose (f);
523         Con_Printf ("%i points read\n", c);
524 }
525
526 /*
527 ===============
528 R_ParseParticleEffect
529
530 Parse an effect out of the server message
531 ===============
532 */
533 void R_ParseParticleEffect (void)
534 {
535         vec3_t          org, dir;
536         int                     i, count, msgcount, color;
537
538         for (i=0 ; i<3 ; i++)
539                 org[i] = MSG_ReadCoord ();
540         for (i=0 ; i<3 ; i++)
541                 dir[i] = MSG_ReadChar () * (1.0/16);
542         msgcount = MSG_ReadByte ();
543         color = MSG_ReadByte ();
544
545         if (msgcount == 255)
546                 count = 1024;
547         else
548                 count = msgcount;
549
550         R_RunParticleEffect (org, dir, color, count);
551 }
552
553 /*
554 ===============
555 R_ParticleExplosion
556
557 ===============
558 */
559 void R_ParticleExplosion (vec3_t org, int smoke)
560 {
561         int i, j;
562         float f;
563         vec3_t v, end, ang;
564         byte noise1[32*32], noise2[32*32];
565
566         if (r_particles.value && r_particles_explosions.value)
567         {
568                 i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
569                 if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
570                 {
571                         for (i = 0;i < 128;i++)
572                                 particle(pt_bubble, 0xFFFFFF, bubbleparticletexture, TPOLYTYPE_ALPHA, false, lhrandom(1, 2), 255, 9999, 1.5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-96, 96), lhrandom(-96, 96), lhrandom(-96, 96), 0, 0, 0, 0, 0, 0);
573
574                         ang[2] = lhrandom(0, 360);
575                         fractalnoise(noise1, 32, 4);
576                         fractalnoise(noise2, 32, 8);
577                         for (i = 0;i < 32;i++)
578                         {
579                                 for (j = 0;j < 32;j++)
580                                 {
581                                         VectorRandom(v);
582                                         VectorMA(org, 16, v, v);
583                                         TraceLine(org, v, end, NULL, 0);
584                                         ang[0] = (j + 0.5f) * (360.0f / 32.0f);
585                                         ang[1] = (i + 0.5f) * (360.0f / 32.0f);
586                                         AngleVectors(ang, v, NULL, NULL);
587                                         f = noise1[j*32+i] * 1.5f;
588                                         VectorScale(v, f, v);
589                                         particle(pt_underwaterspark, noise2[j*32+i] * 0x010101, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2], 512.0f, 0, 0, 0, 2, 0);
590                                         VectorScale(v, 0.75, v);
591                                         particle(pt_underwaterspark, explosparkramp[(noise2[j*32+i] >> 5)], particletexture, TPOLYTYPE_ALPHA, false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2], 512.0f, 0, 0, 0, 2, 0);
592                                 }
593                         }
594                 }
595                 else
596                 {
597                         ang[2] = lhrandom(0, 360);
598                         fractalnoise(noise1, 32, 4);
599                         fractalnoise(noise2, 32, 8);
600                         for (i = 0;i < 32;i++)
601                         {
602                                 for (j = 0;j < 32;j++)
603                                 {
604                                         VectorRandom(v);
605                                         VectorMA(org, 16, v, v);
606                                         TraceLine(org, v, end, NULL, 0);
607                                         ang[0] = (j + 0.5f) * (360.0f / 32.0f);
608                                         ang[1] = (i + 0.5f) * (360.0f / 32.0f);
609                                         AngleVectors(ang, v, NULL, NULL);
610                                         f = noise1[j*32+i] * 1.5f;
611                                         VectorScale(v, f, v);
612                                         particle(pt_spark, noise2[j*32+i] * 0x010101, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2] + 160.0f, 512.0f, 0, 0, 0, 2, 0);
613                                         VectorScale(v, 0.75, v);
614                                         particle(pt_spark, explosparkramp[(noise2[j*32+i] >> 5)], particletexture, TPOLYTYPE_ALPHA, false, 10, lhrandom(128, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2] + 160.0f, 512.0f, 0, 0, 0, 2, 0);
615                                 //      VectorRandom(v);
616                                 //      VectorScale(v, 384, v);
617                                 //      particle(pt_spark, explosparkramp[rand()&7], particletexture, TPOLYTYPE_ALPHA, false, 2, lhrandom(16, 255), 9999, 1.5, end[0], end[1], end[2], v[0], v[1], v[2] + 160.0f, 512.0f, 0, 0, 0, 2, 0);
618                                 }
619                         }
620                 }
621         }
622         else
623                 R_NewExplosion(org);
624 }
625
626 /*
627 ===============
628 R_ParticleExplosion2
629
630 ===============
631 */
632 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
633 {
634         int                     i;
635         if (!r_particles.value) return; // LordHavoc: particles are optional
636
637         for (i = 0;i < 512;i++)
638                 particle(pt_fade, particlepalette[colorStart + (i % colorLength)], particletexture, TPOLYTYPE_ALPHA, false, 1.5, 255, 0.3, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192), 0, 0, 0, 0, 0.1f, 0);
639 }
640
641 /*
642 ===============
643 R_BlobExplosion
644
645 ===============
646 */
647 void R_BlobExplosion (vec3_t org)
648 {
649         int                     i;
650         if (!r_particles.value) return; // LordHavoc: particles are optional
651
652         for (i = 0;i < 256;i++)
653                 particle(pt_blob , particlepalette[ 66+(rand()%6)], particletexture, TPOLYTYPE_ALPHA, false, 4, 255, 9999, 0, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(-128, 128), 0, 0, 0, 0, 0, 0);
654         for (i = 0;i < 256;i++)
655                 particle(pt_blob2, particlepalette[150+(rand()%6)], particletexture, TPOLYTYPE_ALPHA, false, 4, 255, 9999, 0, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(-128, 128), 0, 0, 0, 0, 0, 0);
656 }
657
658 /*
659 ===============
660 R_RunParticleEffect
661
662 ===============
663 */
664 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
665 {
666         if (!r_particles.value) return; // LordHavoc: particles are optional
667
668         if (count == 1024)
669         {
670                 R_ParticleExplosion(org, false);
671                 return;
672         }
673         while (count--)
674                 particle(pt_fade, particlepalette[color + (rand()&7)], particletexture, TPOLYTYPE_ALPHA, false, 1, 128, 9999, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-15, 15), lhrandom(-15, 15), lhrandom(-15, 15), 0, 0, 0, 0, 0, 0);
675 }
676
677 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
678 /*
679 ===============
680 R_SparkShower
681 ===============
682 */
683 void R_SparkShower (vec3_t org, vec3_t dir, int count)
684 {
685         particletexture_t *tex;
686         if (!r_particles.value) return; // LordHavoc: particles are optional
687
688         tex = &bulletholetexture[rand()&7];
689         R_Decal(org, particlefonttexture, tex->s1, tex->t1, tex->s2, tex->t2, 16, 0, 0, 0, 255);
690
691         // smoke puff
692         if (r_particles_smoke.value)
693                 particle(pt_bulletsmoke, 0xA0A0A0, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 5, 255, 9999, 0, org[0], org[1], org[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 16), 0, 0, 0, 0, 0, 0);
694
695         if (r_particles_sparks.value)
696         {
697                 // sparks
698                 while(count--)
699                         particle(pt_spark, particlepalette[0x68 + (rand() & 7)], particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(0, 255), 9999, 1.5, org[0], org[1], org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(0, 128), 512.0f, 0, 0, 0, 0.2f, 0);
700         }
701 }
702
703 void R_BloodPuff (vec3_t org, vec3_t vel, int count)
704 {
705         // bloodcount is used to accumulate counts too small to cause a blood particle
706         static int bloodcount = 0;
707         if (!r_particles.value) return; // LordHavoc: particles are optional
708         if (!r_particles_blood.value) return;
709
710         if (count > 100)
711                 count = 100;
712         bloodcount += count;
713         while(bloodcount >= 10)
714         {
715                 particle(pt_blood, 0x300000, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 24, 255, 9999, -1, org[0], org[1], org[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64), 0, 0, 0, 0, 1.0f, 0);
716                 bloodcount -= 10;
717         }
718 }
719
720 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
721 {
722         vec3_t          diff;
723         vec3_t          center;
724         vec3_t          velscale;
725         if (!r_particles.value) return; // LordHavoc: particles are optional
726         if (!r_particles_bloodshowers.value) return;
727         if (!r_particles_blood.value) return;
728
729         VectorSubtract(maxs, mins, diff);
730         center[0] = (mins[0] + maxs[0]) * 0.5;
731         center[1] = (mins[1] + maxs[1]) * 0.5;
732         center[2] = (mins[2] + maxs[2]) * 0.5;
733         // FIXME: change velspeed back to 2.0x after fixing mod
734         velscale[0] = velspeed * 2.0 / diff[0];
735         velscale[1] = velspeed * 2.0 / diff[1];
736         velscale[2] = velspeed * 2.0 / diff[2];
737
738         while (count--)
739         {
740                 vec3_t org, vel;
741                 org[0] = lhrandom(mins[0], maxs[0]);
742                 org[1] = lhrandom(mins[1], maxs[1]);
743                 org[2] = lhrandom(mins[2], maxs[2]);
744                 vel[0] = (org[0] - center[0]) * velscale[0];
745                 vel[1] = (org[1] - center[1]) * velscale[1];
746                 vel[2] = (org[2] - center[2]) * velscale[2];
747                 particle(pt_blood, 0x300000, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 24, 255, 9999, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 0, 0, 0, 0, 1.0f, 0);
748         }
749 }
750
751 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
752 {
753         float           t;
754         if (!r_particles.value) return; // LordHavoc: particles are optional
755         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
756         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
757         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
758
759         while (count--)
760                 particle(gravity ? pt_grav : pt_static, particlepalette[colorbase + (rand()&3)], particletexture, TPOLYTYPE_ALPHA, false, 2, 255, lhrandom(1, 2), 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0] + lhrandom(-randomvel, randomvel), dir[1] + lhrandom(-randomvel, randomvel), dir[2] + lhrandom(-randomvel, randomvel), 0, 0, 0, 0, 0, 0);
761 }
762
763 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
764 {
765         vec3_t          vel;
766         float           t, z;
767         if (!r_particles.value) return; // LordHavoc: particles are optional
768         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
769         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
770         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
771         if (dir[2] < 0) // falling
772         {
773                 t = (maxs[2] - mins[2]) / -dir[2];
774                 z = maxs[2];
775         }
776         else // rising??
777         {
778                 t = (maxs[2] - mins[2]) / dir[2];
779                 z = mins[2];
780         }
781         if (t < 0 || t > 2) // sanity check
782                 t = 2;
783
784         switch(type)
785         {
786         case 0:
787                 while(count--)
788                 {
789                         vel[0] = dir[0] + lhrandom(-16, 16);
790                         vel[1] = dir[1] + lhrandom(-16, 16);
791                         vel[2] = dir[2] + lhrandom(-32, 32);
792                         particle(pt_rain, particlepalette[colorbase + (rand()&3)], rainparticletexture, TPOLYTYPE_ALPHA, true, 3, 255, t, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), z, vel[0], vel[1], vel[2], 0, vel[0], vel[1], vel[2], 0, 0);
793                 }
794                 break;
795         case 1:
796                 while(count--)
797                 {
798                         vel[0] = dir[0] + lhrandom(-16, 16);
799                         vel[1] = dir[1] + lhrandom(-16, 16);
800                         vel[2] = dir[2] + lhrandom(-32, 32);
801                         particle(pt_snow, particlepalette[colorbase + (rand()&3)], particletexture, TPOLYTYPE_ALPHA, false, 2, 255, t, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), z, vel[0], vel[1], vel[2], 0, vel[0], vel[1], vel[2], 0, 0);
802                 }
803                 break;
804         default:
805                 Host_Error("R_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
806         }
807 }
808
809 void R_FlameCube (vec3_t mins, vec3_t maxs, int count)
810 {
811         float           t;
812         if (!r_particles.value) return; // LordHavoc: particles are optional
813         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
814         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
815         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
816
817         while (count--)
818                 particle(pt_flame, particlepalette[224 + (rand()&15)], smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, false, 8, 255, 9999, 1.1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(-32, 64), 0, 0, 0, 0, 0.1f, 0);
819 }
820
821 void R_Flames (vec3_t org, vec3_t vel, int count)
822 {
823         if (!r_particles.value) return; // LordHavoc: particles are optional
824
825         while (count--)
826                 particle(pt_flame, particlepalette[224 + (rand()&15)], smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, false, 8, 255, 9999, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 0, 0, 0, 0, 0.1f, 0);
827 }
828
829
830
831 /*
832 ===============
833 R_LavaSplash
834
835 ===============
836 */
837 void R_LavaSplash (vec3_t origin)
838 {
839         int                     i, j;
840         float           vel;
841         vec3_t          dir, org;
842         if (!r_particles.value) return; // LordHavoc: particles are optional
843
844         for (i=-128 ; i<128 ; i+=16)
845         {
846                 for (j=-128 ; j<128 ; j+=16)
847                 {
848                         dir[0] = j + lhrandom(0, 8);
849                         dir[1] = i + lhrandom(0, 8);
850                         dir[2] = 256;
851                         org[0] = origin[0] + dir[0];
852                         org[1] = origin[1] + dir[1];
853                         org[2] = origin[2] + lhrandom(0, 64);
854                         vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
855                         particle(pt_lavasplash, particlepalette[224 + (rand()&7)], particletexture, TPOLYTYPE_ALPHA, false, 7, 255, 9999, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, 0, 0);
856                 }
857         }
858 }
859
860 /*
861 ===============
862 R_TeleportSplash
863
864 ===============
865 */
866 void R_TeleportSplash (vec3_t org)
867 {
868         int                     i, j, k;
869         if (!r_particles.value) return; // LordHavoc: particles are optional
870
871         for (i=-16 ; i<16 ; i+=8)
872                 for (j=-16 ; j<16 ; j+=8)
873                         for (k=-24 ; k<32 ; k+=8)
874                                 particle(pt_fade, 0xFFFFFF, particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(64, 128), 9999, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), i*2 + lhrandom(-12.5, 12.5), j*2 + lhrandom(-12.5, 12.5), k*2 + lhrandom(27.5, 52.5), 0, 0, 0, 0, 0.1f, -512.0f);
875 }
876
877 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
878 {
879         vec3_t          vec, dir, vel;
880         float           len, dec = 0, speed;
881         int                     contents, bubbles, polytype;
882         double          t;
883         if (!r_particles.value) return; // LordHavoc: particles are optional
884
885         VectorSubtract(end, start, dir);
886         VectorNormalize(dir);
887
888         if (type == 0 && host_frametime != 0) // rocket glow
889                 particle(pt_oneframe, 0xFFFFFF, rocketglowparticletexture, TPOLYTYPE_ALPHA, false, 24, 255, 9999, 0, end[0] - 12 * dir[0], end[1] - 12 * dir[1], end[2] - 12 * dir[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
890
891         t = ent->persistent.trail_time;
892         if (t >= cl.time)
893                 return; // no particles to spawn this frame (sparse trail)
894
895         if (t < cl.oldtime)
896                 t = cl.oldtime;
897
898         VectorSubtract (end, start, vec);
899         len = VectorNormalizeLength (vec);
900         if (len <= 0.01f)
901         {
902                 // advance the trail time
903                 ent->persistent.trail_time = cl.time;
904                 return;
905         }
906         speed = len / (cl.time - cl.oldtime);
907         VectorScale(vec, speed, vel);
908
909         // advance into this frame to reach the first puff location
910         dec = t - cl.oldtime;
911         dec *= speed;
912         VectorMA(start, dec, vec, start);
913
914         contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
915         if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
916         {
917                 // advance the trail time
918                 ent->persistent.trail_time = cl.time;
919                 return;
920         }
921
922         bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
923
924         polytype = TPOLYTYPE_ALPHA;
925         if (ent->render.effects & EF_ADDITIVE)
926                 polytype = TPOLYTYPE_ADD;
927
928         while (t < cl.time)
929         {
930                 switch (type)
931                 {
932                         case 0: // rocket trail
933                                 if (!r_particles_smoke.value)
934                                         dec = cl.time - t;
935                                 else if (bubbles && r_particles_bubbles.value)
936                                 {
937                                         dec = 0.005f;
938                                         particle(pt_bubble, 0xFFFFFF, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
939                                         particle(pt_bubble, 0xFFFFFF, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
940                                         particle(pt_smoke, 0xFFFFFF, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
941                                 }
942                                 else
943                                 {
944                                         dec = 0.005f;
945                                         particle(pt_smoke, 0xC0C0C0, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
946                                         //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
947                                         //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
948                                         //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
949                                         //particle(pt_spark, particlepalette[0x68 + (rand() & 7)], particletexture, TPOLYTYPE_ALPHA, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.0625, lhrandom(-64, 64) - vel[1] * 0.0625, lhrandom(-64, 64) - vel[2] * 0.0625, 512.0f, 0, 0, 0, 0.1f, 0);
950                                 }
951                                 break;
952
953                         case 1: // grenade trail
954                                 // FIXME: make it gradually stop smoking
955                                 if (!r_particles_smoke.value)
956                                         dec = cl.time - t;
957                                 else if (bubbles && r_particles_bubbles.value)
958                                 {
959                                         dec = 0.02f;
960                                         particle(pt_bubble, 0xFFFFFF, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
961                                         particle(pt_bubble, 0xFFFFFF, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, 0, 0);
962                                         particle(pt_smoke, 0xFFFFFF, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
963                                 }
964                                 else
965                                 {
966                                         dec = 0.02f;
967                                         particle(pt_smoke, 0x808080, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
968                                 }
969                                 break;
970
971
972                         case 2: // blood
973                                 if (!r_particles_blood.value)
974                                         dec = cl.time - t;
975                                 else
976                                 {
977                                         dec = 0.1f;
978                                         particle(pt_blood, 0x300000, smokeparticletexture[rand()&7], polytype, true, 24, 255, 9999, -1, start[0], start[1], start[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64), 0, 0, 0, 0, 1.0f, 0);
979                                 }
980                                 break;
981
982                         case 4: // slight blood
983                                 if (!r_particles_blood.value)
984                                         dec = cl.time - t;
985                                 else
986                                 {
987                                         dec = 0.15f;
988                                         particle(pt_blood, 0x300000, smokeparticletexture[rand()&7], polytype, true, 24, 255, 9999, -1, start[0], start[1], start[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64), 0, 0, 0, 0, 1.0f, 0);
989                                 }
990                                 break;
991
992                         case 3: // green tracer
993                                 dec = 0.02f;
994                                 particle(pt_fade, 0x373707, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
995                                 break;
996
997                         case 5: // flame tracer
998                                 dec = 0.02f;
999                                 particle(pt_fade, 0xCF632B, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1000                                 break;
1001
1002                         case 6: // voor trail
1003                                 dec = 0.05f; // sparse trail
1004                                 particle(pt_fade, 0x47232B, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1005                                 break;
1006
1007                         case 7: // Nehahra smoke tracer
1008                                 if (!r_particles_smoke.value)
1009                                         dec = cl.time - t;
1010                                 else
1011                                 {
1012                                         dec = 0.14f;
1013                                         particle(pt_smoke, 0xC0C0C0, smokeparticletexture[rand()&7], polytype, true, 10, 64, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1014                                 }
1015                                 break;
1016                 }
1017
1018                 // advance to next time and position
1019                 t += dec;
1020                 dec *= speed;
1021                 VectorMA (start, dec, vec, start);
1022         }
1023         ent->persistent.trail_time = t;
1024 }
1025
1026 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1027 {
1028         vec3_t          vec;
1029         int                     len;
1030         if (!r_particles.value) return; // LordHavoc: particles are optional
1031         if (!r_particles_smoke.value) return;
1032
1033         VectorSubtract (end, start, vec);
1034         len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
1035         VectorScale(vec, 3, vec);
1036         color = particlepalette[color];
1037         while (len--)
1038         {
1039                 particle(pt_smoke, color, particletexture, TPOLYTYPE_ALPHA, false, 8, 192, 9999, 0, start[0], start[1], start[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1040                 VectorAdd (start, vec, start);
1041         }
1042 }
1043
1044
1045 /*
1046 ===============
1047 R_DrawParticles
1048 ===============
1049 */
1050 void R_MoveParticles (void)
1051 {
1052         particle_t              *p;
1053         int                             i, activeparticles, maxparticle, j, a, b, pressureused = false;
1054         vec3_t                  v, org, o, normal;
1055         float                   gravity, dvel, frametime, f;
1056
1057         // LordHavoc: early out condition
1058         if (!numparticles)
1059                 return;
1060
1061         frametime = cl.time - cl.oldtime;
1062         if (!frametime)
1063                 return; // if absolutely still, don't update particles
1064         gravity = frametime * sv_gravity.value;
1065         dvel = 1+4*frametime;
1066
1067         activeparticles = 0;
1068         maxparticle = -1;
1069         j = 0;
1070         for (i = 0, p = particles;i < numparticles;i++, p++)
1071         {
1072                 if (p->die < cl.time)
1073                 {
1074                         freeparticles[j++] = p;
1075                         continue;
1076                 }
1077
1078                 VectorCopy(p->org, p->oldorg);
1079                 VectorMA(p->org, frametime, p->vel, p->org);
1080                 if (p->friction)
1081                 {
1082                         f = 1.0f - (p->friction * frametime);
1083                         VectorScale(p->vel, f, p->vel);
1084                 }
1085                 VectorCopy(p->org, org);
1086                 if (p->bounce)
1087                 {
1088                         vec3_t normal;
1089                         float dist;
1090                         if (TraceLine(p->oldorg, p->org, v, normal, 0) < 1)
1091                         {
1092                                 VectorCopy(v, p->org);
1093                                 if (p->bounce < 0)
1094                                 {
1095                                         R_Decal(v, particlefonttexture, p->tex->s1, p->tex->t1, p->tex->s2, p->tex->t2, p->scale, p->color[0], p->color[1], p->color[2], p->alpha);
1096                                         p->die = -1;
1097                                         freeparticles[j++] = p;
1098                                         continue;
1099                                 }
1100                                 else
1101                                 {
1102                                         dist = DotProduct(p->vel, normal) * -p->bounce;
1103                                         VectorMA(p->vel, dist, normal, p->vel);
1104                                         if (DotProduct(p->vel, p->vel) < 0.03)
1105                                                 VectorClear(p->vel);
1106                                 }
1107                         }
1108                 }
1109
1110                 switch (p->type)
1111                 {
1112                 case pt_static:
1113                         break;
1114
1115                         // LordHavoc: drop-through because of shared code
1116                 case pt_blob:
1117                         p->vel[2] *= dvel;
1118                 case pt_blob2:
1119                         p->vel[0] *= dvel;
1120                         p->vel[1] *= dvel;
1121                         p->alpha -= frametime * 256;
1122                         if (p->alpha < 1)
1123                                 p->die = -1;
1124                         break;
1125
1126                 case pt_grav:
1127                         p->vel[2] -= gravity;
1128                         break;
1129                 case pt_lavasplash:
1130                         p->vel[2] -= gravity * 0.05;
1131                         p->alpha -= frametime * 192;
1132                         if (p->alpha < 1)
1133                                 p->die = -1;
1134                         break;
1135                 case pt_snow:
1136                         if (cl.time > p->time2)
1137                         {
1138                                 p->time2 = cl.time + (rand() & 3) * 0.1;
1139                                 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1140                                 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1141                                 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1142                         }
1143                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1144                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1145                         {
1146                                 vec3_t normal;
1147                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1148                                         break; // still in solid
1149                                 p->die = cl.time + 1000;
1150                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1151                                 switch (a)
1152                                 {
1153                                 case CONTENTS_LAVA:
1154                                 case CONTENTS_SLIME:
1155                                         p->tex = &smokeparticletexture[rand()&7];
1156                                         p->type = pt_steam;
1157                                         p->alpha = 96;
1158                                         p->scale = 5;
1159                                         p->vel[2] = 96;
1160                                         break;
1161                                 case CONTENTS_WATER:
1162                                         p->tex = &smokeparticletexture[rand()&7];
1163                                         p->type = pt_splash;
1164                                         p->alpha = 96;
1165                                         p->scale = 5;
1166                                         p->vel[2] = 96;
1167                                         break;
1168                                 default: // CONTENTS_SOLID and any others
1169                                         TraceLine(p->oldorg, p->org, v, normal, 0);
1170                                         VectorCopy(v, p->org);
1171                                         p->tex = &smokeparticletexture[rand()&7];
1172                                         p->type = pt_fade;
1173                                         VectorClear(p->vel);
1174                                         break;
1175                                 }
1176                         }
1177                         break;
1178                 case pt_blood:
1179                         p->friction = 1;
1180                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1181                         if (a != CONTENTS_EMPTY)
1182                         {
1183                                 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1184                                 {
1185                                         p->friction = 5;
1186                                         p->scale += frametime * 32.0f;
1187                                         p->alpha -= frametime * 128.0f;
1188                                         p->vel[2] += gravity * 0.125f;
1189                                         if (p->alpha < 1)
1190                                                 p->die = -1;
1191                                         break;
1192                                 }
1193                                 else
1194                                 {
1195                                         p->die = -1;
1196                                         break;
1197                                 }
1198                         }
1199                         p->vel[2] -= gravity * 0.5;
1200                         break;
1201                 case pt_spark:
1202                         p->alpha -= frametime * p->time2;
1203                         p->vel[2] -= gravity;
1204                         if (p->alpha < 1)
1205                                 p->die = -1;
1206                         else if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1207                                 p->type = pt_underwaterspark;
1208                         break;
1209                 case pt_underwaterspark:
1210                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents == CONTENTS_EMPTY)
1211                         {
1212                                 p->tex = &smokeparticletexture[rand()&7];
1213                                 p->color[0] = p->color[1] = p->color[2] = 255;
1214                                 p->scale = 16;
1215                                 p->type = pt_explosionsplash;
1216                         }
1217                         else
1218                                 p->vel[2] += gravity * 0.5f;
1219                         p->alpha -= frametime * p->time2;
1220                         if (p->alpha < 1)
1221                                 p->die = -1;
1222                         break;
1223                 case pt_explosionsplash:
1224                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents == CONTENTS_EMPTY)
1225                                 p->vel[2] -= gravity;
1226                         else
1227                                 p->alpha = 0;
1228                         p->scale += frametime * 64.0f;
1229                         p->alpha -= frametime * 1024.0f;
1230                         if (p->alpha < 1)
1231                                 p->die = -1;
1232                         break;
1233                 case pt_fade:
1234                         p->alpha -= frametime * 512;
1235                         if (p->alpha < 1)
1236                                 p->die = -1;
1237                         break;
1238                 case pt_bubble:
1239                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1240                         if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1241                         {
1242                                 p->tex = &smokeparticletexture[rand()&7];
1243                                 p->type = pt_splashpuff;
1244                                 p->scale = 4;
1245                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1246                                 break;
1247                         }
1248                         p->vel[2] += gravity * 0.25;
1249                         p->vel[0] *= (1 - (frametime * 0.0625));
1250                         p->vel[1] *= (1 - (frametime * 0.0625));
1251                         p->vel[2] *= (1 - (frametime * 0.0625));
1252                         if (cl.time > p->time2)
1253                         {
1254                                 p->time2 = cl.time + lhrandom(0, 0.5);
1255                                 p->vel[0] += lhrandom(-32,32);
1256                                 p->vel[1] += lhrandom(-32,32);
1257                                 p->vel[2] += lhrandom(-32,32);
1258                         }
1259                         p->alpha -= frametime * 256;
1260                         if (p->alpha < 1)
1261                                 p->die = -1;
1262                         break;
1263                 case pt_bulletsmoke:
1264                         p->scale += frametime * 16;
1265                         p->alpha -= frametime * 1024;
1266                         p->vel[2] += gravity * 0.1;
1267                         if (p->alpha < 1)
1268                                 p->die = -1;
1269                         break;
1270                 case pt_smoke:
1271                         p->scale += frametime * 24;
1272                         p->alpha -= frametime * 256;
1273                         p->vel[2] += gravity * 0.1;
1274                         if (p->alpha < 1)
1275                                 p->die = -1;
1276                         break;
1277                 case pt_steam:
1278                         p->scale += frametime * 48;
1279                         p->alpha -= frametime * 512;
1280                         p->vel[2] += gravity * 0.05;
1281                         if (p->alpha < 1)
1282                                 p->die = -1;
1283                         break;
1284                 case pt_splashpuff:
1285                         p->alpha -= frametime * 1024;
1286                         if (p->alpha < 1)
1287                                 p->die = -1;
1288                         break;
1289                 case pt_rain:
1290                         f = 0;
1291                         b = Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents;
1292                         VectorCopy(p->oldorg, o);
1293                         while (f < 1)
1294                         {
1295                                 a = b;
1296                                 f = TraceLine(o, p->org, v, normal, a);
1297                                 b = traceline_endcontents;
1298                                 if (f < 1 && b != CONTENTS_EMPTY && b != CONTENTS_SKY)
1299                                 {
1300                                         p->die = cl.time + 1000;
1301                                         p->vel[0] = p->vel[1] = p->vel[2] = 0;
1302                                         VectorCopy(v, p->org);
1303                                         switch (b)
1304                                         {
1305                                         case CONTENTS_LAVA:
1306                                         case CONTENTS_SLIME:
1307                                                 p->tex = &smokeparticletexture[rand()&7];
1308                                                 p->type = pt_steam;
1309                                                 p->scale = 3;
1310                                                 p->vel[2] = 96;
1311                                                 break;
1312                                         default: // water, solid, and anything else
1313                                                 p->tex = &raindropsplashparticletexture[0];
1314                                                 p->time2 = 0;
1315                                                 VectorCopy(normal, p->vel2);
1316                                         //      VectorAdd(p->org, normal, p->org);
1317                                                 p->type = pt_raindropsplash;
1318                                                 p->scale = 8;
1319                                                 break;
1320                                         }
1321                                 }
1322                         }
1323                         break;
1324                 case pt_raindropsplash:
1325                         p->time2 += frametime * 64.0f;
1326                         if (p->time2 >= 16.0f)
1327                         {
1328                                 p->die = -1;
1329                                 break;
1330                         }
1331                         p->tex = &raindropsplashparticletexture[(int) p->time2];
1332                         break;
1333                 case pt_flame:
1334                         p->alpha -= frametime * 512;
1335                         p->vel[2] += gravity;
1336                         if (p->alpha < 16)
1337                                 p->die = -1;
1338                         break;
1339                 case pt_oneframe:
1340                         if (p->time2)
1341                                 p->die = -1;
1342                         p->time2 = 1;
1343                         break;
1344                 default:
1345                         printf("unknown particle type %i\n", p->type);
1346                         p->die = -1;
1347                         break;
1348                 }
1349
1350                 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1351                 if (p->die < cl.time)
1352                         freeparticles[j++] = p;
1353                 else
1354                 {
1355                         maxparticle = i;
1356                         activeparticles++;
1357                         if (p->pressure)
1358                                 pressureused = true;
1359                 }
1360         }
1361         // fill in gaps to compact the array
1362         i = 0;
1363         while (maxparticle >= activeparticles)
1364         {
1365                 *freeparticles[i++] = particles[maxparticle--];
1366                 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1367                         maxparticle--;
1368         }
1369         numparticles = activeparticles;
1370
1371         if (pressureused)
1372         {
1373                 activeparticles = 0;
1374                 for (i = 0, p = particles;i < numparticles;i++, p++)
1375                         if (p->pressure)
1376                                 freeparticles[activeparticles++] = p;
1377
1378                 if (activeparticles)
1379                 {
1380                         for (i = 0, p = particles;i < numparticles;i++, p++)
1381                         {
1382                                 for (j = 0;j < activeparticles;j++)
1383                                 {
1384                                         if (freeparticles[j] != p)
1385                                         {
1386                                                 float dist, diff[3];
1387                                                 VectorSubtract(p->org, freeparticles[j]->org, diff);
1388                                                 dist = DotProduct(diff, diff);
1389                                                 if (dist < 4096 && dist >= 1)
1390                                                 {
1391                                                         dist = freeparticles[j]->scale * 4.0f * frametime / sqrt(dist);
1392                                                         VectorMA(p->vel, dist, diff, p->vel);
1393                                                         //dist = freeparticles[j]->scale * 4.0f * frametime / dist;
1394                                                         //VectorMA(p->vel, dist, freeparticles[j]->vel, p->vel);
1395                                                 }
1396                                         }
1397                                 }
1398                         }
1399                 }
1400         }
1401 }
1402
1403 void R_DrawParticles (void)
1404 {
1405         particle_t              *p;
1406         int                             i, dynamiclight, staticlight, r, g, b, texnum;
1407         float                   minparticledist;
1408         vec3_t                  uprightangles, up2, right2, v, right, up;
1409         mleaf_t                 *leaf;
1410
1411         // LordHavoc: early out condition
1412         if ((!numparticles) || (!r_drawparticles.value))
1413                 return;
1414
1415         staticlight = dynamiclight = r_particles_lighting.value;
1416         if (!r_dynamic.value)
1417                 dynamiclight = 0;
1418         c_particles += numparticles;
1419
1420         uprightangles[0] = 0;
1421         uprightangles[1] = r_refdef.viewangles[1];
1422         uprightangles[2] = 0;
1423         AngleVectors (uprightangles, NULL, right2, up2);
1424
1425         minparticledist = DotProduct(r_origin, vpn) + 16.0f;
1426
1427         texnum = R_GetTexture(particlefonttexture);
1428         for (i = 0, p = particles;i < numparticles;i++, p++)
1429         {
1430                 if (p->tex == NULL || p->alpha < 1 || p->scale < 0.1f)
1431                         continue;
1432
1433                 // LordHavoc: only render if not too close
1434                 if (DotProduct(p->org, vpn) < minparticledist)
1435                         continue;
1436
1437                 // LordHavoc: check if it's in a visible leaf
1438                 leaf = Mod_PointInLeaf(p->org, cl.worldmodel);
1439                 if (leaf->visframe != r_framecount)
1440                         continue;
1441
1442                 r = p->color[0];
1443                 g = p->color[1];
1444                 b = p->color[2];
1445                 if (staticlight && (p->dynlight || staticlight >= 2)) // LordHavoc: only light blood and smoke
1446                 {
1447                         R_CompleteLightPoint(v, p->org, dynamiclight, leaf);
1448 #if SLOWMATH
1449                         r = (r * (int) v[0]) >> 7;
1450                         g = (g * (int) v[1]) >> 7;
1451                         b = (b * (int) v[2]) >> 7;
1452 #else
1453                         v[0] += 8388608.0f;
1454                         v[1] += 8388608.0f;
1455                         v[2] += 8388608.0f;
1456                         r = (r * (*((long *) &v[0]) & 0x7FFFFF)) >> 7;
1457                         g = (g * (*((long *) &v[1]) & 0x7FFFFF)) >> 7;
1458                         b = (b * (*((long *) &v[2]) & 0x7FFFFF)) >> 7;
1459 #endif
1460                 }
1461                 if (p->type == pt_raindropsplash)
1462                 {
1463                         // treat as double-sided
1464                         if (DotProduct(p->vel2, r_origin) > DotProduct(p->vel2, p->org))
1465                         {
1466                                 VectorNegate(p->vel2, v);
1467                                 VectorVectors(v, right, up);
1468                         }
1469                         else
1470                                 VectorVectors(p->vel2, right, up);
1471                         transpolyparticle(p->org, right, up, p->scale, texnum, p->rendermode, r, g, b, p->alpha, p->tex->s1, p->tex->t1, p->tex->s2, p->tex->t2);
1472                 }
1473                 else if (p->tex == &rainparticletexture) // rain streak
1474                         transpolyparticle(p->org, right2, up2, p->scale, texnum, p->rendermode, r, g, b, p->alpha, p->tex->s1, p->tex->t1, p->tex->s2, p->tex->t2);
1475                 else
1476                         transpolyparticle(p->org, vright, vup, p->scale, texnum, p->rendermode, r, g, b, p->alpha, p->tex->s1, p->tex->t1, p->tex->s2, p->tex->t2);
1477         }
1478 }