]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_particles.c
default to insert mode instead of replace mode in console
[divverent/darkplaces.git] / cl_particles.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 #ifdef WORKINGLQUAKE
24 #define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
25 #define NUMVERTEXNORMALS        162
26 siextern float r_avertexnormals[NUMVERTEXNORMALS][3];
27 #define m_bytenormals r_avertexnormals
28 #define VectorNormalizeFast VectorNormalize
29 #define CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
30 typedef unsigned char qbyte;
31 #define cl_stainmaps.integer 0
32 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
33 {
34 }
35 #define CL_EntityParticles R_EntityParticles
36 #define CL_ReadPointFile_f R_ReadPointFile_f
37 #define CL_ParseParticleEffect R_ParseParticleEffect
38 #define CL_ParticleExplosion R_ParticleExplosion
39 #define CL_ParticleExplosion2 R_ParticleExplosion2
40 #define CL_TeleportSplash R_TeleportSplash
41 #define CL_BlobExplosion R_BlobExplosion
42 #define CL_RunParticleEffect R_RunParticleEffect
43 #define CL_LavaSplash R_LavaSplash
44 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
45 {
46         vec3_t right1, right2, diff, normal;
47
48         VectorSubtract (org2, org1, normal);
49         VectorNormalizeFast (normal);
50
51         // calculate 'right' vector for start
52         VectorSubtract (r_vieworigin, org1, diff);
53         VectorNormalizeFast (diff);
54         CrossProduct (normal, diff, right1);
55
56         // calculate 'right' vector for end
57         VectorSubtract (r_vieworigin, org2, diff);
58         VectorNormalizeFast (diff);
59         CrossProduct (normal, diff, right2);
60
61         vert[ 0] = org1[0] + width * right1[0];
62         vert[ 1] = org1[1] + width * right1[1];
63         vert[ 2] = org1[2] + width * right1[2];
64         vert[ 3] = org1[0] - width * right1[0];
65         vert[ 4] = org1[1] - width * right1[1];
66         vert[ 5] = org1[2] - width * right1[2];
67         vert[ 6] = org2[0] - width * right2[0];
68         vert[ 7] = org2[1] - width * right2[1];
69         vert[ 8] = org2[2] - width * right2[2];
70         vert[ 9] = org2[0] + width * right2[0];
71         vert[10] = org2[1] + width * right2[1];
72         vert[11] = org2[2] + width * right2[2];
73 }
74 void fractalnoise(qbyte *noise, int size, int startgrid)
75 {
76         int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
77         int *noisebuf;
78 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
79
80         for (sizepower = 0;(1 << sizepower) < size;sizepower++);
81         if (size != (1 << sizepower))
82                 Sys_Error("fractalnoise: size must be power of 2\n");
83
84         for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
85         if (startgrid != (1 << gridpower))
86                 Sys_Error("fractalnoise: grid must be power of 2\n");
87
88         startgrid = bound(0, startgrid, size);
89
90         amplitude = 0xFFFF; // this gets halved before use
91         noisebuf = malloc(size*size*sizeof(int));
92         memset(noisebuf, 0, size*size*sizeof(int));
93
94         for (g2 = startgrid;g2;g2 >>= 1)
95         {
96                 // brownian motion (at every smaller level there is random behavior)
97                 amplitude >>= 1;
98                 for (y = 0;y < size;y += g2)
99                         for (x = 0;x < size;x += g2)
100                                 n(x,y) += (rand()&amplitude);
101
102                 g = g2 >> 1;
103                 if (g)
104                 {
105                         // subdivide, diamond-square algorithm (really this has little to do with squares)
106                         // diamond
107                         for (y = 0;y < size;y += g2)
108                                 for (x = 0;x < size;x += g2)
109                                         n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
110                         // square
111                         for (y = 0;y < size;y += g2)
112                                 for (x = 0;x < size;x += g2)
113                                 {
114                                         n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
115                                         n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
116                                 }
117                 }
118         }
119         // find range of noise values
120         min = max = 0;
121         for (y = 0;y < size;y++)
122                 for (x = 0;x < size;x++)
123                 {
124                         if (n(x,y) < min) min = n(x,y);
125                         if (n(x,y) > max) max = n(x,y);
126                 }
127         max -= min;
128         max++;
129         // normalize noise and copy to output
130         for (y = 0;y < size;y++)
131                 for (x = 0;x < size;x++)
132                         *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
133         free(noisebuf);
134 #undef n
135 }
136 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
137 {
138         float d;
139
140         right[0] = forward[2];
141         right[1] = -forward[0];
142         right[2] = forward[1];
143
144         d = DotProduct(forward, right);
145         right[0] -= d * forward[0];
146         right[1] -= d * forward[1];
147         right[2] -= d * forward[2];
148         VectorNormalizeFast(right);
149         CrossProduct(right, forward, up);
150 }
151 #if QW
152 #include "pmove.h"
153 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
154 #endif
155 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int hitbmodels, int *hitent, int hitsupercontentsmask)
156 {
157 #if QW
158         pmtrace_t trace;
159 #else
160         trace_t trace;
161 #endif
162         memset (&trace, 0, sizeof(trace));
163         trace.fraction = 1;
164         VectorCopy (end, trace.endpos);
165 #if QW
166         PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
167 #else
168         RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
169 #endif
170         VectorCopy(trace.endpos, impact);
171         VectorCopy(trace.plane.normal, normal);
172         return trace.fraction;
173 }
174 #else
175 #include "cl_collision.h"
176 #include "image.h"
177 #endif
178
179 #define MAX_PARTICLES                   32768   // default max # of particles at one time
180 #define ABSOLUTE_MIN_PARTICLES  512             // no fewer than this no matter what's on the command line
181
182 typedef enum
183 {
184         PARTICLE_BILLBOARD = 0,
185         PARTICLE_SPARK = 1,
186         PARTICLE_ORIENTED_DOUBLESIDED = 2,
187         PARTICLE_BEAM = 3
188 }
189 porientation_t;
190
191 typedef enum
192 {
193         PBLEND_ALPHA = 0,
194         PBLEND_ADD = 1,
195         PBLEND_MOD = 2
196 }
197 pblend_t;
198
199 typedef struct particletype_s
200 {
201         pblend_t blendmode;
202         porientation_t orientation;
203         qboolean lighting;
204 }
205 particletype_t;
206
207 typedef enum
208 {
209         pt_alphastatic, pt_static, pt_spark, pt_beam, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_smoke, pt_decal, pt_entityparticle, pt_total
210 }
211 ptype_t;
212
213 // must match ptype_t values
214 particletype_t particletype[pt_total] =
215 {
216         {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
217         {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
218         {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
219         {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
220         {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
221         {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
222         {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
223         {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
224         {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
225         {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
226         {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
227         {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
228 };
229
230 typedef struct particle_s
231 {
232         particletype_t *type;
233         int                     texnum;
234         vec3_t          org;
235         vec3_t          vel; // velocity of particle, or orientation of decal, or end point of beam
236         float           size;
237         float           alpha; // 0-255
238         float           alphafade; // how much alpha reduces per second
239         float           time2; // used for snow fluttering and decal fade
240         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)
241         float           gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
242         float           friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
243         qbyte           color[4];
244 #ifndef WORKINGLQUAKE
245         unsigned short owner; // decal stuck to this entity
246         model_t         *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
247         vec3_t          relativeorigin; // decal at this location in entity's coordinate space
248         vec3_t          relativedirection; // decal oriented this way relative to entity's coordinate space
249 #endif
250 }
251 particle_t;
252
253 static int particlepalette[256] =
254 {
255         0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
256         0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
257         0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
258         0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
259         0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
260         0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
261         0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
262         0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
263         0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
264         0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
265         0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
266         0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
267         0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
268         0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
269         0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
270         0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
271         0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
272         0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
273         0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
274         0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
275         0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
276         0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
277         0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
278         0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
279         0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
280         0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
281         0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
282         0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
283         0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
284         0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
285         0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
286         0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
287 };
288
289 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
290
291 // texture numbers in particle font
292 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
293 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
294 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
295 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
296 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
297 static const int tex_particle = 63;
298 static const int tex_bubble = 62;
299 static const int tex_raindrop = 61;
300 static const int tex_beam = 60;
301
302 static int                      cl_maxparticles;
303 static int                      cl_numparticles;
304 static int                      cl_freeparticle;
305 static particle_t       *particles;
306
307 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
308 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
309 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
310 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
311 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
312 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
313 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
314 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
315 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
316 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
317 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
318 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
319 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
320 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
321 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
322 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
323 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
324 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
325 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
326 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
327
328 void CL_Particles_Clear(void)
329 {
330         cl_numparticles = 0;
331         cl_freeparticle = 0;
332         memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
333 }
334
335 /*
336 ===============
337 CL_InitParticles
338 ===============
339 */
340 void CL_ReadPointFile_f (void);
341 void CL_Particles_Init (void)
342 {
343         int             i;
344
345 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
346         i = COM_CheckParm ("-particles");
347
348         if (i && i < com_argc - 1)
349         {
350                 cl_maxparticles = (int)(atoi(com_argv[i+1]));
351                 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
352                         cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
353         }
354         else
355                 cl_maxparticles = MAX_PARTICLES;
356
357         Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
358
359         Cvar_RegisterVariable (&cl_particles);
360         Cvar_RegisterVariable (&cl_particles_quality);
361         Cvar_RegisterVariable (&cl_particles_size);
362         Cvar_RegisterVariable (&cl_particles_bloodshowers);
363         Cvar_RegisterVariable (&cl_particles_blood);
364         Cvar_RegisterVariable (&cl_particles_blood_alpha);
365         Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
366         Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
367         Cvar_RegisterVariable (&cl_particles_explosions_smoke);
368         Cvar_RegisterVariable (&cl_particles_explosions_sparks);
369         Cvar_RegisterVariable (&cl_particles_explosions_shell);
370         Cvar_RegisterVariable (&cl_particles_bulletimpacts);
371         Cvar_RegisterVariable (&cl_particles_smoke);
372         Cvar_RegisterVariable (&cl_particles_smoke_alpha);
373         Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
374         Cvar_RegisterVariable (&cl_particles_sparks);
375         Cvar_RegisterVariable (&cl_particles_bubbles);
376         Cvar_RegisterVariable (&cl_decals);
377         Cvar_RegisterVariable (&cl_decals_time);
378         Cvar_RegisterVariable (&cl_decals_fadetime);
379
380 #ifdef WORKINGLQUAKE
381         particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
382 #else
383         particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
384 #endif
385         CL_Particles_Clear();
386 }
387
388 void CL_Particles_Shutdown (void)
389 {
390 #ifdef WORKINGLQUAKE
391         // No clue what to do here...
392 #endif
393 }
394
395 // list of all 26 parameters:
396 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
397 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
398 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
399 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
400 // palpha - opacity of particle as 0-255 (can be more than 255)
401 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
402 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
403 // pgravity - how much effect gravity has on the particle (0-1)
404 // pbounce - how much bounce the particle has when it hits a surface (0-1), -1 makes a blood splat when it hits a surface, 0 does not even check for collisions
405 // px,py,pz - starting origin of particle
406 // pvx,pvy,pvz - starting velocity of particle
407 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
408 particle_t *particle(particletype_t *ptype, int pcolor1, int pcolor2, int ptex, float psize, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pfriction)
409 {
410         particle_t *part;
411         int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
412         ptempcolor = (pcolor1);
413         ptempcolor2 = (pcolor2);
414         pcr2 = ((ptempcolor2) >> 16) & 0xFF;
415         pcg2 = ((ptempcolor2) >> 8) & 0xFF;
416         pcb2 = (ptempcolor2) & 0xFF;
417         if (ptempcolor != ptempcolor2)
418         {
419                 pcr1 = ((ptempcolor) >> 16) & 0xFF;
420                 pcg1 = ((ptempcolor) >> 8) & 0xFF;
421                 pcb1 = (ptempcolor) & 0xFF;
422                 ptempcolor = rand() & 0xFF;
423                 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
424                 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
425                 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
426         }
427         for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
428         if (cl_freeparticle >= cl_maxparticles)
429                 return NULL;
430         part = &particles[cl_freeparticle++];
431         if (cl_numparticles < cl_freeparticle)
432                 cl_numparticles = cl_freeparticle;
433         memset(part, 0, sizeof(*part));
434         part->type = (ptype);
435         part->color[0] = pcr2;
436         part->color[1] = pcg2;
437         part->color[2] = pcb2;
438         part->color[3] = 0xFF;
439         part->texnum = ptex;
440         part->size = (psize);
441         part->alpha = (palpha);
442         part->alphafade = (palphafade);
443         part->gravity = (pgravity);
444         part->bounce = (pbounce);
445         part->org[0] = (px);
446         part->org[1] = (py);
447         part->org[2] = (pz);
448         part->vel[0] = (pvx);
449         part->vel[1] = (pvy);
450         part->vel[2] = (pvz);
451         part->time2 = 0;
452         part->friction = (pfriction);
453         return part;
454 }
455
456 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
457 {
458         particle_t *p;
459         if (!cl_decals.integer)
460                 return;
461         p = particle(particletype + pt_decal, color1, color2, texnum, size, alpha, 0, 0, 0, org[0] + normal[0], org[1] + normal[1], org[2] + normal[2], normal[0], normal[1], normal[2], 0);
462         if (p)
463         {
464                 p->time2 = cl.time;
465 #ifndef WORKINGLQUAKE
466                 p->owner = hitent;
467                 p->ownermodel = cl_entities[p->owner].render.model;
468                 Matrix4x4_Transform(&cl_entities[p->owner].render.inversematrix, org, p->relativeorigin);
469                 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.inversematrix, normal, p->relativedirection);
470                 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
471 #endif
472         }
473 }
474
475 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
476 {
477         int i;
478         float bestfrac, bestorg[3], bestnormal[3];
479         float frac, v[3], normal[3], org2[3];
480         int besthitent = 0, hitent;
481         bestfrac = 10;
482         for (i = 0;i < 32;i++)
483         {
484                 VectorRandom(org2);
485                 VectorMA(org, maxdist, org2, org2);
486                 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
487                 if (bestfrac > frac)
488                 {
489                         bestfrac = frac;
490                         besthitent = hitent;
491                         VectorCopy(v, bestorg);
492                         VectorCopy(normal, bestnormal);
493                 }
494         }
495         if (bestfrac < 1)
496                 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
497 }
498
499 /*
500 ===============
501 CL_EntityParticles
502 ===============
503 */
504 void CL_EntityParticles (entity_t *ent)
505 {
506         int                     i;
507         float           angle;
508         float           sp, sy, cp, cy;
509         vec3_t          forward;
510         float           dist;
511         float           beamlength;
512         static vec3_t avelocities[NUMVERTEXNORMALS];
513         if (!cl_particles.integer) return;
514
515         dist = 64;
516         beamlength = 16;
517
518         if (!avelocities[0][0])
519                 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
520                         avelocities[0][i] = (rand()&255) * 0.01;
521
522         for (i=0 ; i<NUMVERTEXNORMALS ; i++)
523         {
524                 angle = cl.time * avelocities[i][0];
525                 sy = sin(angle);
526                 cy = cos(angle);
527                 angle = cl.time * avelocities[i][1];
528                 sp = sin(angle);
529                 cp = cos(angle);
530
531                 forward[0] = cp*cy;
532                 forward[1] = cp*sy;
533                 forward[2] = -sp;
534
535 #ifdef WORKINGLQUAKE
536                 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 0, 0, 0, ent->origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0);
537 #else
538                 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 0, 0, 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);
539 #endif
540         }
541 }
542
543
544 void CL_ReadPointFile_f (void)
545 {
546         vec3_t org, leakorg;
547         int r, c, s;
548         char *pointfile = NULL, *pointfilepos, *t, tchar;
549         char name[MAX_OSPATH];
550
551         if (!cl.worldmodel)
552                 return;
553
554         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
555         strlcat (name, ".pts", sizeof (name));
556 #if WORKINGLQUAKE
557         pointfile = COM_LoadTempFile (name);
558 #else
559         pointfile = FS_LoadFile(name, tempmempool, true);
560 #endif
561         if (!pointfile)
562         {
563                 Con_Printf("Could not open %s\n", name);
564                 return;
565         }
566
567         Con_Printf("Reading %s...\n", name);
568         c = 0;
569         s = 0;
570         pointfilepos = pointfile;
571         while (*pointfilepos)
572         {
573                 while (*pointfilepos == '\n' || *pointfilepos == '\r')
574                         pointfilepos++;
575                 if (!*pointfilepos)
576                         break;
577                 t = pointfilepos;
578                 while (*t && *t != '\n' && *t != '\r')
579                         t++;
580                 tchar = *t;
581                 *t = 0;
582                 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
583                 *t = tchar;
584                 pointfilepos = t;
585                 if (r != 3)
586                         break;
587                 if (c == 0)
588                         VectorCopy(org, leakorg);
589                 c++;
590
591                 if (cl_numparticles < cl_maxparticles - 3)
592                 {
593                         s++;
594                         particle(particletype + pt_static, particlepalette[(-c)&15], particlepalette[(-c)&15], tex_particle, 2, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0);
595                 }
596         }
597 #ifndef WORKINGLQUAKE
598         Mem_Free(pointfile);
599 #endif
600         VectorCopy(leakorg, org);
601         Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
602
603         particle(particletype + pt_beam, 0xFF0000, 0xFF0000, tex_beam, 64, 255, 0, 0, 0, org[0] - 4096, org[1], org[2], org[0] + 4096, org[1], org[2], 0);
604         particle(particletype + pt_beam, 0x00FF00, 0x00FF00, tex_beam, 64, 255, 0, 0, 0, org[0], org[1] - 4096, org[2], org[0], org[1] + 4096, org[2], 0);
605         particle(particletype + pt_beam, 0x0000FF, 0x0000FF, tex_beam, 64, 255, 0, 0, 0, org[0], org[1], org[2] - 4096, org[0], org[1], org[2] + 4096, 0);
606 }
607
608 /*
609 ===============
610 CL_ParseParticleEffect
611
612 Parse an effect out of the server message
613 ===============
614 */
615 void CL_ParseParticleEffect (void)
616 {
617         vec3_t org, dir;
618         int i, count, msgcount, color;
619
620         MSG_ReadVector(org, cl.protocol);
621         for (i=0 ; i<3 ; i++)
622                 dir[i] = MSG_ReadChar () * (1.0/16);
623         msgcount = MSG_ReadByte ();
624         color = MSG_ReadByte ();
625
626         if (msgcount == 255)
627                 count = 1024;
628         else
629                 count = msgcount;
630
631         if (cl_particles_blood_bloodhack.integer)
632         {
633                 if (color == 73)
634                 {
635                         // regular blood
636                         CL_BloodPuff(org, dir, count / 2);
637                         return;
638                 }
639                 if (color == 225)
640                 {
641                         // lightning blood
642                         CL_BloodPuff(org, dir, count / 2);
643                         return;
644                 }
645         }
646         CL_RunParticleEffect (org, dir, color, count);
647 }
648
649 /*
650 ===============
651 CL_ParticleExplosion
652
653 ===============
654 */
655 void CL_ParticleExplosion (vec3_t org)
656 {
657         int i;
658         //vec3_t v;
659         //vec3_t v2;
660         if (cl_stainmaps.integer)
661                 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
662         CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
663
664         i = CL_PointSuperContents(org);
665         if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
666         {
667                 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
668                         for (i = 0;i < 128 * cl_particles_quality.value;i++)
669                                 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, (1.0f / cl_particles_quality.value) * lhrandom(128, 255), (1.0f / cl_particles_quality.value) * 128, -0.125, 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), (1.0 / 16.0));
670         }
671         else
672         {
673                 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
674                 // smoke puff
675                 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
676                 {
677                         for (i = 0;i < 32;i++)
678                         {
679                                 int k;
680                                 vec3_t v, v2;
681 #ifdef WORKINGLQUAKE
682                                 v2[0] = lhrandom(-48, 48);
683                                 v2[1] = lhrandom(-48, 48);
684                                 v2[2] = lhrandom(-48, 48);
685 #else
686                                 for (k = 0;k < 16;k++)
687                                 {
688                                         v[0] = org[0] + lhrandom(-48, 48);
689                                         v[1] = org[1] + lhrandom(-48, 48);
690                                         v[2] = org[2] + lhrandom(-48, 48);
691                                         if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
692                                                 break;
693                                 }
694                                 VectorSubtract(v2, org, v2);
695 #endif
696                                 VectorScale(v2, 2.0f, v2);
697                                 particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 12, 32, 64, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0);
698                         }
699                 }
700
701                 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
702                         for (i = 0;i < 128 * cl_particles_quality.value;i++)
703                                 particle(particletype + pt_spark, 0x903010, 0xFFD030, tex_particle, 1.0f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 512, 1, 0, org[0], org[1], org[2], lhrandom(-256, 256), lhrandom(-256, 256), lhrandom(-256, 256) + 80, 0.2);
704         }
705
706         if (cl_particles_explosions_shell.integer)
707                 R_NewExplosion(org);
708 }
709
710 /*
711 ===============
712 CL_ParticleExplosion2
713
714 ===============
715 */
716 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
717 {
718         vec3_t vel;
719         vec3_t offset;
720         int i, k;
721         float pscale;
722         if (!cl_particles.integer) return;
723
724         for (i = 0;i < 512 * cl_particles_quality.value;i++)
725         {
726                 VectorRandom (offset);
727                 VectorScale (offset, 192, vel);
728                 VectorScale (offset, 8, offset);
729                 k = particlepalette[colorStart + (i % colorLength)];
730                 pscale = lhrandom(0.5, 1.5);
731                 particle(particletype + pt_static, k, k, tex_particle, pscale, (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*512, 0, 0, org[0] + offset[0], org[1] + offset[1], org[2] + offset[2], vel[0], vel[1], vel[2], lhrandom(1.5, 3));
732         }
733 }
734
735 /*
736 ===============
737 CL_BlobExplosion
738
739 ===============
740 */
741 void CL_BlobExplosion (vec3_t org)
742 {
743         CL_ParticleExplosion(org);
744 }
745
746 /*
747 ===============
748 CL_RunParticleEffect
749
750 ===============
751 */
752 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
753 {
754         int k;
755
756         if (count == 1024)
757         {
758                 CL_ParticleExplosion(org);
759                 return;
760         }
761         if (!cl_particles.integer) return;
762         count *= cl_particles_quality.value;
763         while (count--)
764         {
765                 k = particlepalette[color + (rand()&7)];
766                 if (gamemode == GAME_GOODVSBAD2)
767                         particle(particletype + pt_alphastatic, k, k, tex_particle, 5, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 300, 0, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-10, 10), lhrandom(-10, 10), lhrandom(-10, 10), 0);
768                 else
769                         particle(particletype + pt_alphastatic, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 512, 0, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), dir[0] + lhrandom(-15, 15), dir[1] + lhrandom(-15, 15), dir[2] + lhrandom(-15, 15), 0);
770         }
771 }
772
773 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
774 /*
775 ===============
776 CL_SparkShower
777 ===============
778 */
779 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
780 {
781         int k;
782
783         if (!cl_particles.integer) return;
784
785         if (cl_particles_sparks.integer)
786         {
787                 // sparks
788                 count *= cl_particles_quality.value;
789                 while(count--)
790                 {
791                         k = particlepalette[0x68 + (rand() & 7)];
792                         particle(particletype + pt_spark, k, k, tex_particle, 0.4f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, gravityscale, 0, org[0], org[1], org[2], lhrandom(-64, 64) + dir[0], lhrandom(-64, 64) + dir[1], lhrandom(0, 128) + dir[2], 0);
793                 }
794         }
795 }
796
797 void CL_Smoke (vec3_t org, vec3_t dir, int count)
798 {
799         vec3_t org2, org3;
800         int k;
801
802         if (!cl_particles.integer) return;
803
804         // smoke puff
805         if (cl_particles_smoke.integer)
806         {
807                 k = count * 0.25 * cl_particles_quality.value;
808                 while(k--)
809                 {
810                         org2[0] = org[0] + 0.125f * lhrandom(-count, count);
811                         org2[1] = org[1] + 0.125f * lhrandom(-count, count);
812                         org2[2] = org[2] + 0.125f * lhrandom(-count, count);
813                         CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
814                         particle(particletype + pt_smoke, 0x101010, 0x202020, tex_smoke[rand()&7], 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 0, 0, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
815                 }
816         }
817 }
818
819 void CL_BulletMark (vec3_t org)
820 {
821         if (cl_stainmaps.integer)
822                 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
823         CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
824 }
825
826 void CL_PlasmaBurn (vec3_t org)
827 {
828         if (cl_stainmaps.integer)
829                 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
830         CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
831 }
832
833 static float bloodcount = 0;
834 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
835 {
836         float s;
837         vec3_t org2, org3;
838         // bloodcount is used to accumulate counts too small to cause a blood particle
839         if (!cl_particles.integer) return;
840         if (!cl_particles_blood.integer) return;
841
842         s = count + 64.0f;
843         count *= 5.0f;
844         if (count > 1000)
845                 count = 1000;
846         bloodcount += count;
847         while(bloodcount > 0)
848         {
849                 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
850                 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
851                 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
852                 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
853                 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, org3[0], org3[1], org3[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 1);
854                 bloodcount -= 16 / cl_particles_quality.value;
855         }
856 }
857
858 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
859 {
860         vec3_t org, vel, diff, center, velscale;
861         if (!cl_particles.integer) return;
862         if (!cl_particles_bloodshowers.integer) return;
863         if (!cl_particles_blood.integer) return;
864
865         VectorSubtract(maxs, mins, diff);
866         center[0] = (mins[0] + maxs[0]) * 0.5;
867         center[1] = (mins[1] + maxs[1]) * 0.5;
868         center[2] = (mins[2] + maxs[2]) * 0.5;
869         velscale[0] = velspeed * 2.0 / diff[0];
870         velscale[1] = velspeed * 2.0 / diff[1];
871         velscale[2] = velspeed * 2.0 / diff[2];
872
873         bloodcount += count * 5.0f;
874         while (bloodcount > 0)
875         {
876                 org[0] = lhrandom(mins[0], maxs[0]);
877                 org[1] = lhrandom(mins[1], maxs[1]);
878                 org[2] = lhrandom(mins[2], maxs[2]);
879                 vel[0] = (org[0] - center[0]) * velscale[0];
880                 vel[1] = (org[1] - center[1]) * velscale[1];
881                 vel[2] = (org[2] - center[2]) * velscale[2];
882                 bloodcount -= 16 / cl_particles_quality.value;
883                 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 1);
884         }
885 }
886
887 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
888 {
889         int k;
890         float t;
891         if (!cl_particles.integer) return;
892         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
893         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
894         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
895
896         count *= cl_particles_quality.value;
897         while (count--)
898         {
899                 k = particlepalette[colorbase + (rand()&3)];
900                 particle(particletype + pt_alphastatic, k, k, tex_particle, 2, 255 / cl_particles_quality.value, (255 / cl_particles_quality.value) / 2, gravity ? 1 : 0, 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);
901         }
902 }
903
904 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
905 {
906         int k;
907         float t, z, minz, maxz;
908         if (!cl_particles.integer) return;
909         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
910         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
911         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
912         if (dir[2] < 0) // falling
913                 z = maxs[2];
914         else // rising??
915                 z = mins[2];
916
917         minz = z - fabs(dir[2]) * 0.1;
918         maxz = z + fabs(dir[2]) * 0.1;
919         minz = bound(mins[2], minz, maxs[2]);
920         maxz = bound(mins[2], maxz, maxs[2]);
921
922         count *= cl_particles_quality.value;
923
924         switch(type)
925         {
926         case 0:
927                 count *= 4; // ick, this should be in the mod or maps?
928
929                 while(count--)
930                 {
931                         k = particlepalette[colorbase + (rand()&3)];
932                         if (gamemode == GAME_GOODVSBAD2)
933                                 particle(particletype + pt_rain, k, k, tex_particle, 20, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
934                         else
935                                 particle(particletype + pt_rain, k, k, tex_particle, 0.5, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
936                 }
937                 break;
938         case 1:
939                 while(count--)
940                 {
941                         k = particlepalette[colorbase + (rand()&3)];
942                         if (gamemode == GAME_GOODVSBAD2)
943                                 particle(particletype + pt_snow, k, k, tex_particle, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
944                         else
945                                 particle(particletype + pt_snow, k, k, tex_particle, 1, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
946                 }
947                 break;
948         default:
949                 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
950         }
951 }
952
953 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
954 {
955         int k;
956         float t;
957         vec3_t o, v, center;
958         if (!cl_particles.integer) return;
959
960         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
961         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
962         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
963
964         center[0] = (mins[0] + maxs[0]) * 0.5f;
965         center[1] = (mins[1] + maxs[1]) * 0.5f;
966         center[2] = (mins[2] + maxs[2]) * 0.5f;
967
968         count *= cl_particles_quality.value;
969         while (count--)
970         {
971                 k = particlepalette[224 + (rand()&15)];
972                 o[0] = lhrandom(mins[0], maxs[0]);
973                 o[1] = lhrandom(mins[1], maxs[1]);
974                 o[2] = lhrandom(mins[2], maxs[2]);
975                 VectorSubtract(o, center, v);
976                 VectorNormalizeFast(v);
977                 VectorScale(v, 100, v);
978                 v[2] += sv_gravity.value * 0.15f;
979                 particle(particletype + pt_static, 0x903010, 0xFFD030, tex_particle, 1.5, lhrandom(64, 128) / cl_particles_quality.value, 128 / cl_particles_quality.value, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0.2);
980         }
981 }
982
983 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
984 {
985         int k;
986         float t;
987         if (!cl_particles.integer) return;
988         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
989         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
990         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
991
992         count *= cl_particles_quality.value;
993         while (count--)
994         {
995                 k = particlepalette[224 + (rand()&15)];
996                 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(0, 64), 1);
997                 if (count & 1)
998                         particle(particletype + pt_static, 0x303030, 0x606060, tex_smoke[rand()&7], 6, lhrandom(48, 96) / cl_particles_quality.value, 64 / cl_particles_quality.value, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 32), 0);
999         }
1000 }
1001
1002 void CL_Flames (vec3_t org, vec3_t vel, int count)
1003 {
1004         int k;
1005         if (!cl_particles.integer) return;
1006
1007         count *= cl_particles_quality.value;
1008         while (count--)
1009         {
1010                 k = particlepalette[224 + (rand()&15)];
1011                 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 1);
1012         }
1013 }
1014
1015
1016
1017 /*
1018 ===============
1019 CL_LavaSplash
1020
1021 ===============
1022 */
1023 void CL_LavaSplash (vec3_t origin)
1024 {
1025         float i, j, inc, vel;
1026         int k, l;
1027         vec3_t          dir, org;
1028         if (!cl_particles.integer) return;
1029
1030         inc = 32 / cl_particles_quality.value;
1031         for (i = -128;i < 128;i += inc)
1032         {
1033                 for (j = -128;j < 128;j += inc)
1034                 {
1035                         dir[0] = j + lhrandom(0, 8);
1036                         dir[1] = i + lhrandom(0, 8);
1037                         dir[2] = 256;
1038                         org[0] = origin[0] + dir[0];
1039                         org[1] = origin[1] + dir[1];
1040                         org[2] = origin[2] + lhrandom(0, 64);
1041                         vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1042                         if (gamemode == GAME_GOODVSBAD2)
1043                         {
1044                                 k = particlepalette[0 + (rand()&255)];
1045                                 l = particlepalette[0 + (rand()&255)];
1046                                 particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 1, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0);
1047                         }
1048                         else
1049                         {
1050                                 k = l = particlepalette[224 + (rand()&7)];
1051                                 particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0);
1052                         }
1053                 }
1054         }
1055 }
1056
1057 /*
1058 ===============
1059 CL_TeleportSplash
1060
1061 ===============
1062 */
1063 void CL_TeleportSplash (vec3_t org)
1064 {
1065         float i, j, k, inc;
1066         if (!cl_particles.integer) return;
1067
1068         inc = 8 / cl_particles_quality.value;
1069         for (i = -16;i < 16;i += inc)
1070                 for (j = -16;j < 16;j += inc)
1071                         for (k = -24;k < 32;k += inc)
1072                                 particle(particletype + pt_static, 0xA0A0A0, 0xFFFFFF, tex_particle, 10, inc * lhrandom(8, 16), inc * 32, 0, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-256, 256), 1);
1073 }
1074
1075 #ifdef WORKINGLQUAKE
1076 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1077 #else
1078 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1079 #endif
1080 {
1081         vec3_t vec, dir, vel, pos;
1082         float len, dec, speed, qd;
1083         int smoke, blood, bubbles;
1084 #ifdef WORKINGLQUAKE
1085         int contents;
1086 #endif
1087
1088         if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1089                 return;
1090
1091         VectorSubtract(end, start, dir);
1092         VectorNormalize(dir);
1093
1094         VectorSubtract (end, start, vec);
1095 #ifdef WORKINGLQUAKE
1096         len = VectorNormalize (vec);
1097         dec = 0;
1098         speed = 1.0f / cl.frametime;
1099         VectorSubtract(end, start, vel);
1100 #else
1101         len = VectorNormalizeLength (vec);
1102         dec = -ent->persistent.trail_time;
1103         ent->persistent.trail_time += len;
1104         if (ent->persistent.trail_time < 0.01f)
1105                 return;
1106
1107         // if we skip out, leave it reset
1108         ent->persistent.trail_time = 0.0f;
1109
1110         speed = ent->state_current.time - ent->state_previous.time;
1111         if (speed)
1112                 speed = 1.0f / speed;
1113         VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1114         color = particlepalette[color];
1115 #endif
1116         VectorScale(vel, speed, vel);
1117
1118         // advance into this frame to reach the first puff location
1119         VectorMA(start, dec, vec, pos);
1120         len -= dec;
1121
1122         smoke = cl_particles.integer && cl_particles_smoke.integer;
1123         blood = cl_particles.integer && cl_particles_blood.integer;
1124 #ifdef WORKINGLQUAKE
1125         contents = CL_PointQ1Contents(pos);
1126         bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1127 #else
1128         bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1129 #endif
1130         qd = 1.0f / cl_particles_quality.value;
1131
1132         while (len >= 0)
1133         {
1134                 switch (type)
1135                 {
1136                         case 0: // rocket trail
1137                                 dec = qd*3;
1138                                 if (smoke)
1139                                 {
1140                                         particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*125, qd*cl_particles_smoke_alphafade.value*125, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1141                                         particle(particletype + pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*288, qd*cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], lhrandom(-20, 20), lhrandom(-20, 20), lhrandom(-20, 20), 0);
1142                                 }
1143                                 if (bubbles)
1144                                         particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, qd*lhrandom(64, 255), qd*256, -0.25, 1.5, pos[0], pos[1], pos[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), (1.0 / 16.0));
1145                                 break;
1146
1147                         case 1: // grenade trail
1148                                 // FIXME: make it gradually stop smoking
1149                                 dec = qd*3;
1150                                 if (smoke)
1151                                         particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*100, qd*cl_particles_smoke_alphafade.value*100, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1152                                 break;
1153
1154
1155                         case 2: // blood
1156                         case 4: // slight blood
1157                                 dec = qd*16;
1158                                 if (blood)
1159                                         particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], vel[0] * 0.5f + lhrandom(-64, 64), vel[1] * 0.5f + lhrandom(-64, 64), vel[2] * 0.5f + lhrandom(-64, 64), 1);
1160                                 break;
1161
1162                         case 3: // green tracer
1163                                 dec = qd*6;
1164                                 if (smoke)
1165                                 {
1166                                         if (gamemode == GAME_GOODVSBAD2)
1167                                                 particle(particletype + pt_static, 0x00002E, 0x000030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1168                                         else
1169                                                 particle(particletype + pt_static, 0x002000, 0x003000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1170                                 }
1171                                 break;
1172
1173                         case 5: // flame tracer
1174                                 dec = qd*6;
1175                                 if (smoke)
1176                                         particle(particletype + pt_static, 0x301000, 0x502000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1177                                 break;
1178
1179                         case 6: // voor trail
1180                                 dec = qd*6;
1181                                 if (smoke)
1182                                 {
1183                                         if (gamemode == GAME_GOODVSBAD2)
1184                                                 particle(particletype + pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, qd*255, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1185                                         else if (gamemode == GAME_PRYDON)
1186                                                 particle(particletype + pt_static, 0x103040, 0x204050, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1187                                         else
1188                                                 particle(particletype + pt_static, 0x502030, 0x502030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1189                                 }
1190                                 break;
1191 #ifndef WORKINGLQUAKE
1192                         case 7: // Nehahra smoke tracer
1193                                 dec = qd*7;
1194                                 if (smoke)
1195                                         particle(particletype + pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, qd*64, qd*320, 0, 0, pos[0], pos[1], pos[2], lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(0, 16), 0);
1196                                 break;
1197                         case 8: // Nexuiz plasma trail
1198                                 dec = qd*4;
1199                                 if (smoke)
1200                                         particle(particletype + pt_static, 0x283880, 0x283880, tex_particle, 4, qd*255, qd*1024, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0);
1201                                 break;
1202                         case 9: // glow trail
1203                                 dec = qd*3;
1204                                 if (smoke)
1205                                         particle(particletype + pt_alphastatic, color, color, tex_particle, 5, qd*128, qd*320, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0);
1206                                 break;
1207 #endif
1208                 }
1209
1210                 // advance to next time and position
1211                 len -= dec;
1212                 VectorMA (pos, dec, vec, pos);
1213         }
1214 #ifndef WORKINGLQUAKE
1215         ent->persistent.trail_time = len;
1216 #endif
1217 }
1218
1219 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1220 {
1221         int tempcolor2, cr, cg, cb;
1222         cr = red * 255;
1223         cg = green * 255;
1224         cb = blue * 255;
1225         tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1226         particle(particletype + pt_beam, tempcolor2, tempcolor2, tex_beam, radius, alpha * 255, alpha * 255 / lifetime, 0, 0, start[0], start[1], start[2], end[0], end[1], end[2], 0);
1227 }
1228
1229 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1230 {
1231         float f;
1232         if (!cl_particles.integer) return;
1233
1234         // smoke puff
1235         if (cl_particles_smoke.integer)
1236                 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1237                         particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count) * 0.5f, dir[1] + lhrandom(-count, count) * 0.5f, dir[2] + lhrandom(-count, count) * 0.5f, 0);
1238 }
1239
1240 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1241 {
1242         float f;
1243         if (!cl_particles.integer) return;
1244
1245         if (cl_stainmaps.integer)
1246                 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1247         CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1248
1249         // smoke puff
1250         if (cl_particles_smoke.integer)
1251                 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1252                         particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count), dir[1] + lhrandom(-count, count), dir[2] + lhrandom(-count, count), 0);
1253
1254         // sparks
1255         if (cl_particles_sparks.integer)
1256                 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1257                         particle(particletype + pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, lhrandom(64, 255) / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0], org[1], org[2], lhrandom(-count, count) * 3.0f + dir[0], lhrandom(-count, count) * 3.0f + dir[1], lhrandom(-count, count) * 3.0f + dir[2], 0);
1258 }
1259
1260 /*
1261 ===============
1262 CL_MoveParticles
1263 ===============
1264 */
1265 void CL_MoveParticles (void)
1266 {
1267         particle_t *p;
1268         int i, maxparticle, j, a, content;
1269         float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3], oldorg[3];
1270         int hitent;
1271
1272         // LordHavoc: early out condition
1273         if (!cl_numparticles)
1274         {
1275                 cl_freeparticle = 0;
1276                 return;
1277         }
1278
1279 #ifdef WORKINGLQUAKE
1280         frametime = cl.frametime;
1281 #else
1282         frametime = cl.time - cl.oldtime;
1283 #endif
1284         gravity = frametime * sv_gravity.value;
1285         dvel = 1+4*frametime;
1286         bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1287
1288         maxparticle = -1;
1289         j = 0;
1290         for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1291         {
1292                 if (!p->type)
1293                         continue;
1294                 maxparticle = i;
1295                 content = 0;
1296
1297                 p->alpha -= p->alphafade * frametime;
1298
1299                 if (p->alpha <= 0)
1300                 {
1301                         p->type = NULL;
1302                         continue;
1303                 }
1304
1305                 if (p->type->orientation != PARTICLE_BEAM)
1306                 {
1307                         VectorCopy(p->org, oldorg);
1308                         VectorMA(p->org, frametime, p->vel, p->org);
1309                         VectorCopy(p->org, org);
1310                         if (p->bounce)
1311                         {
1312                                 if (p->type == particletype + pt_rain)
1313                                 {
1314                                         // raindrop - splash on solid/water/slime/lava
1315                                         if (CL_TraceLine(oldorg, p->org, v, normal, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK) < 1)
1316                                         {
1317                                                 VectorCopy(v, p->org);
1318                                                 // splash
1319                                                 p->type = particletype + pt_raindecal;
1320                                                 // convert from a raindrop particle to a rainsplash decal
1321                                                 p->texnum = tex_rainsplash[0];
1322                                                 p->time2 = cl.time;
1323                                                 p->alphafade = p->alpha / 0.4;
1324                                                 VectorCopy(normal, p->vel);
1325                                                 VectorAdd(p->org, normal, p->org);
1326                                                 p->bounce = 0;
1327                                                 p->friction = 0;
1328                                                 p->gravity = 0;
1329                                                 p->size = 8.0;
1330                                         }
1331                                 }
1332                                 else if (p->type == particletype + pt_blood)
1333                                 {
1334                                         // blood - splash on solid
1335                                         if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1336                                         {
1337                                                 VectorCopy(v, p->org);
1338 #ifndef WORKINGLQUAKE
1339                                                 if (cl_stainmaps.integer)
1340                                                         R_Stain(v, 32, 32, 16, 16, p->alpha * p->size * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->size * (1.0f / 40.0f));
1341 #endif
1342                                                 if (!cl_decals.integer)
1343                                                 {
1344                                                         p->type = NULL;
1345                                                         continue;
1346                                                 }
1347
1348                                                 p->type = particletype + pt_decal;
1349                                                 // convert from a blood particle to a blood decal
1350                                                 p->texnum = tex_blooddecal[rand()&7];
1351 #ifndef WORKINGLQUAKE
1352                                                 p->owner = hitent;
1353                                                 p->ownermodel = cl_entities[hitent].render.model;
1354                                                 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, v, p->relativeorigin);
1355                                                 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, normal, p->relativedirection);
1356                                                 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1357 #endif
1358                                                 p->time2 = cl.time;
1359                                                 p->alphafade = 0;
1360                                                 VectorCopy(normal, p->vel);
1361                                                 VectorAdd(p->org, normal, p->org);
1362                                                 p->bounce = 0;
1363                                                 p->friction = 0;
1364                                                 p->gravity = 0;
1365                                                 p->size *= 2.0f;
1366                                         }
1367                                 }
1368                                 else
1369                                 {
1370                                         if (CL_TraceLine(oldorg, p->org, v, normal, true, NULL, SUPERCONTENTS_SOLID) < 1)
1371                                         {
1372                                                 VectorCopy(v, p->org);
1373                                                 if (p->bounce < 0)
1374                                                 {
1375                                                         p->type = NULL;
1376                                                         continue;
1377                                                 }
1378                                                 else
1379                                                 {
1380                                                         dist = DotProduct(p->vel, normal) * -p->bounce;
1381                                                         VectorMA(p->vel, dist, normal, p->vel);
1382                                                         if (DotProduct(p->vel, p->vel) < 0.03)
1383                                                                 VectorClear(p->vel);
1384                                                 }
1385                                         }
1386                                 }
1387                         }
1388                         p->vel[2] -= p->gravity * gravity;
1389
1390                         if (p->friction)
1391                         {
1392                                 f = p->friction * frametime;
1393 #ifdef WORKINGLQUAKE
1394                                 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1395 #else
1396                                 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1397 #endif
1398                                         f *= 4;
1399                                 f = 1.0f - f;
1400                                 VectorScale(p->vel, f, p->vel);
1401                         }
1402                 }
1403
1404                 if (p->type != particletype + pt_static)
1405                 {
1406                         switch (p->type - particletype)
1407                         {
1408                         case pt_entityparticle:
1409                                 // particle that removes itself after one rendered frame
1410                                 if (p->time2)
1411                                         p->type = NULL;
1412                                 else
1413                                         p->time2 = 1;
1414                                 break;
1415                         case pt_blood:
1416 #ifdef WORKINGLQUAKE
1417                                 a = CL_PointQ1Contents(p->org);
1418                                 if (a <= CONTENTS_WATER)
1419 #else
1420                                 a = CL_PointSuperContents(p->org);
1421                                 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1422 #endif
1423                                 {
1424                                         p->size += frametime * 8;
1425                                         //p->alpha -= bloodwaterfade;
1426                                 }
1427                                 else
1428                                         p->vel[2] -= gravity;
1429 #ifdef WORKINGLQUAKE
1430                                 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1431 #else
1432                                 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1433 #endif
1434                                         p->type = NULL;
1435                                 break;
1436                         case pt_bubble:
1437 #ifdef WORKINGLQUAKE
1438                                 a = CL_PointQ1Contents(p->org);
1439                                 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1440 #else
1441                                 a = CL_PointSuperContents(p->org);
1442                                 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1443 #endif
1444                                 {
1445                                         p->type = NULL;
1446                                         break;
1447                                 }
1448                                 break;
1449                         case pt_rain:
1450 #ifdef WORKINGLQUAKE
1451                                 a = CL_PointQ1Contents(p->org);
1452                                 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1453 #else
1454                                 a = CL_PointSuperContents(p->org);
1455                                 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1456 #endif
1457                                         p->type = NULL;
1458                                 break;
1459                         case pt_snow:
1460                                 if (cl.time > p->time2)
1461                                 {
1462                                         // snow flutter
1463                                         p->time2 = cl.time + (rand() & 3) * 0.1;
1464                                         p->vel[0] += lhrandom(-32, 32);
1465                                         p->vel[1] += lhrandom(-32, 32);
1466                                         p->vel[2] += lhrandom(-32, 32);
1467                                 }
1468 #ifdef WORKINGLQUAKE
1469                                 a = CL_PointQ1Contents(p->org);
1470                                 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1471 #else
1472                                 a = CL_PointSuperContents(p->org);
1473                                 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1474 #endif
1475                                         p->type = NULL;
1476                                 break;
1477                         case pt_smoke:
1478                                 //p->size += frametime * 15;
1479                                 break;
1480                         case pt_decal:
1481                                 // FIXME: this has fairly wacky handling of alpha
1482                                 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1483 #ifndef WORKINGLQUAKE
1484                                 if (cl_entities[p->owner].render.model == p->ownermodel)
1485                                 {
1486                                         Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1487                                         Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1488                                 }
1489                                 else
1490                                         p->type = NULL;
1491 #endif
1492                                 break;
1493                         case pt_raindecal:
1494                                 a = max(0, (cl.time - p->time2) * 40);
1495                                 if (a < 16)
1496                                         p->texnum = tex_rainsplash[a];
1497                                 else
1498                                         p->type = NULL;
1499                                 break;
1500                         default:
1501                                 break;
1502                         }
1503                 }
1504         }
1505         cl_numparticles = maxparticle + 1;
1506         cl_freeparticle = 0;
1507 }
1508
1509 #define MAX_PARTICLETEXTURES 64
1510 // particletexture_t is a rectangle in the particlefonttexture
1511 typedef struct
1512 {
1513         rtexture_t *texture;
1514         float s1, t1, s2, t2;
1515 }
1516 particletexture_t;
1517
1518 #if WORKINGLQUAKE
1519 static int particlefonttexture;
1520 #else
1521 static rtexturepool_t *particletexturepool;
1522 static rtexture_t *particlefonttexture;
1523 #endif
1524 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1525
1526 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1527
1528 #define PARTICLETEXTURESIZE 64
1529 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1530
1531 static qbyte shadebubble(float dx, float dy, vec3_t light)
1532 {
1533         float dz, f, dot;
1534         vec3_t normal;
1535         dz = 1 - (dx*dx+dy*dy);
1536         if (dz > 0) // it does hit the sphere
1537         {
1538                 f = 0;
1539                 // back side
1540                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1541                 VectorNormalize(normal);
1542                 dot = DotProduct(normal, light);
1543                 if (dot > 0.5) // interior reflection
1544                         f += ((dot *  2) - 1);
1545                 else if (dot < -0.5) // exterior reflection
1546                         f += ((dot * -2) - 1);
1547                 // front side
1548                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1549                 VectorNormalize(normal);
1550                 dot = DotProduct(normal, light);
1551                 if (dot > 0.5) // interior reflection
1552                         f += ((dot *  2) - 1);
1553                 else if (dot < -0.5) // exterior reflection
1554                         f += ((dot * -2) - 1);
1555                 f *= 128;
1556                 f += 16; // just to give it a haze so you can see the outline
1557                 f = bound(0, f, 255);
1558                 return (qbyte) f;
1559         }
1560         else
1561                 return 0;
1562 }
1563
1564 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1565 {
1566         int basex, basey, y;
1567         basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1568         basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1569         particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1570         particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1571         particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1572         particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1573         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1574                 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1575 }
1576
1577 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1578 {
1579         int x, y;
1580         float cx, cy, dx, dy, f, iradius;
1581         qbyte *d;
1582         cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1583         cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1584         iradius = 1.0f / radius;
1585         alpha *= (1.0f / 255.0f);
1586         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1587         {
1588                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1589                 {
1590                         dx = (x - cx);
1591                         dy = (y - cy);
1592                         f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1593                         if (f > 0)
1594                         {
1595                                 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1596                                 d[0] += f * (red   - d[0]);
1597                                 d[1] += f * (green - d[1]);
1598                                 d[2] += f * (blue  - d[2]);
1599                         }
1600                 }
1601         }
1602 }
1603
1604 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1605 {
1606         int i;
1607         for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1608         {
1609                 data[0] = bound(minr, data[0], maxr);
1610                 data[1] = bound(ming, data[1], maxg);
1611                 data[2] = bound(minb, data[2], maxb);
1612         }
1613 }
1614
1615 void particletextureinvert(qbyte *data)
1616 {
1617         int i;
1618         for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1619         {
1620                 data[0] = 255 - data[0];
1621                 data[1] = 255 - data[1];
1622                 data[2] = 255 - data[2];
1623         }
1624 }
1625
1626 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1627 static void R_InitBloodTextures (qbyte *particletexturedata)
1628 {
1629         int i, j, k, m;
1630         qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1631
1632         // blood particles
1633         for (i = 0;i < 8;i++)
1634         {
1635                 memset(&data[0][0][0], 255, sizeof(data));
1636                 for (k = 0;k < 24;k++)
1637                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1638                 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1639                 particletextureinvert(&data[0][0][0]);
1640                 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1641         }
1642
1643         // blood decals
1644         for (i = 0;i < 8;i++)
1645         {
1646                 memset(&data[0][0][0], 255, sizeof(data));
1647                 m = 8;
1648                 for (j = 1;j < 10;j++)
1649                         for (k = min(j, m - 1);k < m;k++)
1650                                 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1651                 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1652                 particletextureinvert(&data[0][0][0]);
1653                 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1654         }
1655
1656 }
1657
1658 static void R_InitParticleTexture (void)
1659 {
1660         int x, y, d, i, k, m;
1661         float dx, dy, radius, f, f2;
1662         qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1663         vec3_t light;
1664         qbyte *particletexturedata;
1665
1666         // a note: decals need to modulate (multiply) the background color to
1667         // properly darken it (stain), and they need to be able to alpha fade,
1668         // this is a very difficult challenge because it means fading to white
1669         // (no change to background) rather than black (darkening everything
1670         // behind the whole decal polygon), and to accomplish this the texture is
1671         // inverted (dark red blood on white background becomes brilliant cyan
1672         // and white on black background) so we can alpha fade it to black, then
1673         // we invert it again during the blendfunc to make it work...
1674
1675         particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1676         memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1677
1678         // smoke
1679         for (i = 0;i < 8;i++)
1680         {
1681                 memset(&data[0][0][0], 255, sizeof(data));
1682                 do
1683                 {
1684                         qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1685
1686                         fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1687                         fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1688                         m = 0;
1689                         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1690                         {
1691                                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1692                                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1693                                 {
1694                                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1695                                         d = (noise2[y][x] - 128) * 3 + 192;
1696                                         if (d > 0)
1697                                                 d = d * (1-(dx*dx+dy*dy));
1698                                         d = (d * noise1[y][x]) >> 7;
1699                                         d = bound(0, d, 255);
1700                                         data[y][x][3] = (qbyte) d;
1701                                         if (m < d)
1702                                                 m = d;
1703                                 }
1704                         }
1705                 }
1706                 while (m < 224);
1707                 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1708         }
1709
1710         // rain splash
1711         for (i = 0;i < 16;i++)
1712         {
1713                 memset(&data[0][0][0], 255, sizeof(data));
1714                 radius = i * 3.0f / 4.0f / 16.0f;
1715                 f2 = 255.0f * ((15.0f - i) / 15.0f);
1716                 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1717                 {
1718                         dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1719                         for (x = 0;x < PARTICLETEXTURESIZE;x++)
1720                         {
1721                                 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1722                                 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1723                                 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1724                         }
1725                 }
1726                 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1727         }
1728
1729         // normal particle
1730         memset(&data[0][0][0], 255, sizeof(data));
1731         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1732         {
1733                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1734                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1735                 {
1736                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1737                         d = 256 * (1 - (dx*dx+dy*dy));
1738                         d = bound(0, d, 255);
1739                         data[y][x][3] = (qbyte) d;
1740                 }
1741         }
1742         setuptex(tex_particle, &data[0][0][0], particletexturedata);
1743
1744         // rain
1745         memset(&data[0][0][0], 255, sizeof(data));
1746         light[0] = 1;light[1] = 1;light[2] = 1;
1747         VectorNormalize(light);
1748         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1749         {
1750                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1751                 // stretch upper half of bubble by +50% and shrink lower half by -50%
1752                 // (this gives an elongated teardrop shape)
1753                 if (dy > 0.5f)
1754                         dy = (dy - 0.5f) * 2.0f;
1755                 else
1756                         dy = (dy - 0.5f) / 1.5f;
1757                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1758                 {
1759                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1760                         // shrink bubble width to half
1761                         dx *= 2.0f;
1762                         data[y][x][3] = shadebubble(dx, dy, light);
1763                 }
1764         }
1765         setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1766
1767         // bubble
1768         memset(&data[0][0][0], 255, sizeof(data));
1769         light[0] = 1;light[1] = 1;light[2] = 1;
1770         VectorNormalize(light);
1771         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1772         {
1773                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1774                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1775                 {
1776                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1777                         data[y][x][3] = shadebubble(dx, dy, light);
1778                 }
1779         }
1780         setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1781
1782         // Blood particles and blood decals
1783         R_InitBloodTextures (particletexturedata);
1784
1785         // bullet decals
1786         for (i = 0;i < 8;i++)
1787         {
1788                 memset(&data[0][0][0], 255, sizeof(data));
1789                 for (k = 0;k < 12;k++)
1790                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1791                 for (k = 0;k < 3;k++)
1792                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1793                 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1794                 particletextureinvert(&data[0][0][0]);
1795                 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1796         }
1797
1798 #if WORKINGLQUAKE
1799         glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1800         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1801         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1802 #else
1803
1804 #if 0
1805         Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1806 #endif
1807
1808         particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1809         if (!particlefonttexture)
1810                 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1811         for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1812                 particletexture[i].texture = particlefonttexture;
1813
1814         // nexbeam
1815         fractalnoise(&noise3[0][0], 64, 4);
1816         m = 0;
1817         for (y = 0;y < 64;y++)
1818         {
1819                 dy = (y - 0.5f*64) / (64*0.5f-1);
1820                 for (x = 0;x < 16;x++)
1821                 {
1822                         dx = (x - 0.5f*16) / (16*0.5f-2);
1823                         d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1824                         data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1825                         data2[y][x][3] = 255;
1826                 }
1827         }
1828
1829 #if 0
1830         Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1831 #endif
1832
1833         particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1834         if (!particletexture[tex_beam].texture)
1835                 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1836         particletexture[tex_beam].s1 = 0;
1837         particletexture[tex_beam].t1 = 0;
1838         particletexture[tex_beam].s2 = 1;
1839         particletexture[tex_beam].t2 = 1;
1840 #endif
1841         Mem_Free(particletexturedata);
1842 }
1843
1844 static void r_part_start(void)
1845 {
1846         particletexturepool = R_AllocTexturePool();
1847         R_InitParticleTexture ();
1848 }
1849
1850 static void r_part_shutdown(void)
1851 {
1852         R_FreeTexturePool(&particletexturepool);
1853 }
1854
1855 static void r_part_newmap(void)
1856 {
1857         cl_numparticles = 0;
1858         cl_freeparticle = 0;
1859 }
1860
1861 void R_Particles_Init (void)
1862 {
1863         Cvar_RegisterVariable(&r_drawparticles);
1864 #ifdef WORKINGLQUAKE
1865         r_part_start();
1866 #else
1867         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1868 #endif
1869 }
1870
1871 #ifdef WORKINGLQUAKE
1872 void R_InitParticles(void)
1873 {
1874         CL_Particles_Init();
1875         R_Particles_Init();
1876 }
1877 #endif
1878
1879 float particle_vertex3f[12], particle_texcoord2f[8];
1880
1881 #ifdef WORKINGLQUAKE
1882 void R_DrawParticle(particle_t *p)
1883 {
1884 #else
1885 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1886 {
1887         const particle_t *p = calldata1;
1888         rmeshstate_t m;
1889 #endif
1890         pblend_t blendmode;
1891         float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, size;
1892         particletexture_t *tex;
1893
1894         VectorCopy(p->org, org);
1895
1896         blendmode = p->type->blendmode;
1897         tex = &particletexture[p->texnum];
1898         cr = p->color[0] * (1.0f / 255.0f);
1899         cg = p->color[1] * (1.0f / 255.0f);
1900         cb = p->color[2] * (1.0f / 255.0f);
1901         ca = p->alpha * (1.0f / 255.0f);
1902         if (blendmode == PBLEND_MOD)
1903         {
1904                 cr *= ca;
1905                 cg *= ca;
1906                 cb *= ca;
1907                 cr = min(cr, 1);
1908                 cg = min(cg, 1);
1909                 cb = min(cb, 1);
1910                 ca = 1;
1911         }
1912 #ifndef WORKINGLQUAKE
1913         if (p->type->lighting)
1914         {
1915                 float ambient[3], diffuse[3], diffusenormal[3];
1916                 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
1917                 cr *= (ambient[0] + 0.5 * diffuse[0]);
1918                 cg *= (ambient[1] + 0.5 * diffuse[1]);
1919                 cb *= (ambient[2] + 0.5 * diffuse[2]);
1920         }
1921         if (fogenabled)
1922         {
1923                 VectorSubtract(org, r_vieworigin, fogvec);
1924                 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1925                 ifog = 1 - fog;
1926                 cr = cr * ifog;
1927                 cg = cg * ifog;
1928                 cb = cb * ifog;
1929                 if (blendmode == PBLEND_ALPHA)
1930                 {
1931                         cr += fogcolor[0] * fog;
1932                         cg += fogcolor[1] * fog;
1933                         cb += fogcolor[2] * fog;
1934                 }
1935         }
1936
1937         R_Mesh_Matrix(&r_identitymatrix);
1938
1939         memset(&m, 0, sizeof(m));
1940         m.tex[0] = R_GetTexture(tex->texture);
1941         m.pointer_texcoord[0] = particle_texcoord2f;
1942         m.pointer_vertex = particle_vertex3f;
1943         R_Mesh_State(&m);
1944
1945         GL_Color(cr, cg, cb, ca);
1946
1947         if (blendmode == PBLEND_ALPHA)
1948                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1949         else if (blendmode == PBLEND_ADD)
1950                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1951         else //if (blendmode == PBLEND_MOD)
1952                 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1953         GL_DepthMask(false);
1954         GL_DepthTest(true);
1955 #endif
1956         size = p->size * cl_particles_size.value;
1957         if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1958         {
1959                 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1960                 {
1961                         // double-sided
1962                         if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1963                         {
1964                                 VectorNegate(p->vel, v);
1965                                 VectorVectors(v, right, up);
1966                         }
1967                         else
1968                                 VectorVectors(p->vel, right, up);
1969                         VectorScale(right, size, right);
1970                         VectorScale(up, size, up);
1971                 }
1972                 else
1973                 {
1974                         VectorScale(r_viewleft, -size, right);
1975                         VectorScale(r_viewup, size, up);
1976                 }
1977                 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1978                 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1979                 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1980                 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1981                 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1982                 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1983                 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1984                 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1985                 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1986                 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1987                 particle_vertex3f[10] = org[1] + right[1] - up[1];
1988                 particle_vertex3f[11] = org[2] + right[2] - up[2];
1989                 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1990                 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1991                 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1992                 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1993         }
1994         else if (p->type->orientation == PARTICLE_SPARK)
1995         {
1996                 VectorMA(p->org, -0.02, p->vel, v);
1997                 VectorMA(p->org, 0.02, p->vel, up2);
1998                 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
1999                 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2000                 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2001                 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2002                 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2003         }
2004         else if (p->type->orientation == PARTICLE_BEAM)
2005         {
2006                 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2007                 VectorSubtract(p->vel, p->org, up);
2008                 VectorNormalizeFast(up);
2009                 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2010                 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2011                 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2012                 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2013                 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2014                 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2015         }
2016         else
2017                 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2018
2019 #if WORKINGLQUAKE
2020         if (blendmode == PBLEND_ALPHA)
2021                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2022         else if (blendmode == PBLEND_ADD)
2023                 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2024         else //if (blendmode == PBLEND_MOD)
2025                 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2026         glColor4f(cr, cg, cb, ca);
2027         glBegin(GL_QUADS);
2028         glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2029         glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2030         glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2031         glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2032         glEnd();
2033 #else
2034         R_Mesh_Draw(0, 4, 2, polygonelements);
2035 #endif
2036 }
2037
2038 void R_DrawParticles (void)
2039 {
2040         int i;
2041         float minparticledist;
2042         particle_t *p;
2043
2044 #ifdef WORKINGLQUAKE
2045         CL_MoveParticles();
2046 #endif
2047
2048         // LordHavoc: early out conditions
2049         if ((!cl_numparticles) || (!r_drawparticles.integer))
2050                 return;
2051
2052         minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2053
2054 #ifdef WORKINGLQUAKE
2055         glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2056         glEnable(GL_BLEND);
2057         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2058         glDepthMask(0);
2059         // LordHavoc: only render if not too close
2060         for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2061                 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2062                         R_DrawParticle(p);
2063         glDepthMask(1);
2064         glDisable(GL_BLEND);
2065         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2066 #else
2067         // LordHavoc: only render if not too close
2068         for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2069         {
2070                 if (p->type)
2071                 {
2072                         c_particles++;
2073                         if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2074                         {
2075                                 if (p->type == particletype + pt_decal)
2076                                         R_DrawParticleCallback(p, 0);
2077                                 else
2078                                         R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);
2079                         }
2080                 }
2081         }
2082 #endif
2083 }
2084