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