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