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