]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_particles.c
cleaned up edict clearing when connecting or coming back from a level change, this...
[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 unsigned char;
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(unsigned char *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++ = (unsigned char) (((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         unsigned char           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 pitch, yaw, dist = 64, beamlength = 16, org[3], v[3];
510         static vec3_t avelocities[NUMVERTEXNORMALS];
511         if (!cl_particles.integer) return;
512
513 #ifdef WORKINGLQUAKE
514         VectorCopy(ent->origin, org);
515 #else
516         Matrix4x4_OriginFromMatrix(&ent->render.matrix, org);
517 #endif
518
519         if (!avelocities[0][0])
520                 for (i = 0;i < NUMVERTEXNORMALS * 3;i++)
521                         avelocities[0][i] = lhrandom(0, 2.55);
522
523         for (i = 0;i < NUMVERTEXNORMALS;i++)
524         {
525                 yaw = cl.time * avelocities[i][0];
526                 pitch = cl.time * avelocities[i][1];
527                 v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
528                 v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
529                 v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
530                 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 0, 0, 0, v[0], v[1], v[2], 0, 0, 0, 0);
531         }
532 }
533
534
535 void CL_ReadPointFile_f (void)
536 {
537         vec3_t org, leakorg;
538         int r, c, s;
539         char *pointfile = NULL, *pointfilepos, *t, tchar;
540         char name[MAX_OSPATH];
541
542         if (!cl.worldmodel)
543                 return;
544
545         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
546         strlcat (name, ".pts", sizeof (name));
547 #if WORKINGLQUAKE
548         pointfile = COM_LoadTempFile (name);
549 #else
550         pointfile = (char *)FS_LoadFile(name, tempmempool, true, NULL);
551 #endif
552         if (!pointfile)
553         {
554                 Con_Printf("Could not open %s\n", name);
555                 return;
556         }
557
558         Con_Printf("Reading %s...\n", name);
559         VectorClear(leakorg);
560         c = 0;
561         s = 0;
562         pointfilepos = pointfile;
563         while (*pointfilepos)
564         {
565                 while (*pointfilepos == '\n' || *pointfilepos == '\r')
566                         pointfilepos++;
567                 if (!*pointfilepos)
568                         break;
569                 t = pointfilepos;
570                 while (*t && *t != '\n' && *t != '\r')
571                         t++;
572                 tchar = *t;
573                 *t = 0;
574                 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
575                 *t = tchar;
576                 pointfilepos = t;
577                 if (r != 3)
578                         break;
579                 if (c == 0)
580                         VectorCopy(org, leakorg);
581                 c++;
582
583                 if (cl_numparticles < cl_maxparticles - 3)
584                 {
585                         s++;
586                         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);
587                 }
588         }
589 #ifndef WORKINGLQUAKE
590         Mem_Free(pointfile);
591 #endif
592         VectorCopy(leakorg, org);
593         Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
594
595         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);
596         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);
597         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);
598 }
599
600 /*
601 ===============
602 CL_ParseParticleEffect
603
604 Parse an effect out of the server message
605 ===============
606 */
607 void CL_ParseParticleEffect (void)
608 {
609         vec3_t org, dir;
610         int i, count, msgcount, color;
611
612         MSG_ReadVector(org, cl.protocol);
613         for (i=0 ; i<3 ; i++)
614                 dir[i] = MSG_ReadChar () * (1.0/16);
615         msgcount = MSG_ReadByte ();
616         color = MSG_ReadByte ();
617
618         if (msgcount == 255)
619                 count = 1024;
620         else
621                 count = msgcount;
622
623         if (cl_particles_blood_bloodhack.integer)
624         {
625                 if (color == 73)
626                 {
627                         // regular blood
628                         CL_BloodPuff(org, dir, count / 2);
629                         return;
630                 }
631                 if (color == 225)
632                 {
633                         // lightning blood
634                         CL_BloodPuff(org, dir, count / 2);
635                         return;
636                 }
637         }
638         CL_RunParticleEffect (org, dir, color, count);
639 }
640
641 /*
642 ===============
643 CL_ParticleExplosion
644
645 ===============
646 */
647 void CL_ParticleExplosion (vec3_t org)
648 {
649         int i;
650         trace_t trace;
651         //vec3_t v;
652         //vec3_t v2;
653         if (cl_stainmaps.integer)
654                 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
655         CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
656
657         i = CL_PointSuperContents(org);
658         if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
659         {
660                 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
661                         for (i = 0;i < 128 * cl_particles_quality.value;i++)
662                                 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));
663         }
664         else
665         {
666                 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
667                 // smoke puff
668                 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
669                 {
670                         for (i = 0;i < 32;i++)
671                         {
672                                 int k;
673                                 vec3_t v, v2;
674 #ifdef WORKINGLQUAKE
675                                 v2[0] = lhrandom(-48, 48);
676                                 v2[1] = lhrandom(-48, 48);
677                                 v2[2] = lhrandom(-48, 48);
678 #else
679                                 for (k = 0;k < 16;k++)
680                                 {
681                                         v[0] = org[0] + lhrandom(-48, 48);
682                                         v[1] = org[1] + lhrandom(-48, 48);
683                                         v[2] = org[2] + lhrandom(-48, 48);
684                                         trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
685                                         if (trace.fraction >= 0.1)
686                                                 break;
687                                 }
688                                 VectorSubtract(trace.endpos, org, v2);
689 #endif
690                                 VectorScale(v2, 2.0f, v2);
691                                 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);
692                         }
693                 }
694
695                 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
696                         for (i = 0;i < 128 * cl_particles_quality.value;i++)
697                                 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);
698         }
699
700         if (cl_particles_explosions_shell.integer)
701                 R_NewExplosion(org);
702 }
703
704 /*
705 ===============
706 CL_ParticleExplosion2
707
708 ===============
709 */
710 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
711 {
712         vec3_t vel;
713         vec3_t offset;
714         int i, k;
715         float pscale;
716         if (!cl_particles.integer) return;
717
718         for (i = 0;i < 512 * cl_particles_quality.value;i++)
719         {
720                 VectorRandom (offset);
721                 VectorScale (offset, 192, vel);
722                 VectorScale (offset, 8, offset);
723                 k = particlepalette[colorStart + (i % colorLength)];
724                 pscale = lhrandom(0.5, 1.5);
725                 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));
726         }
727 }
728
729 /*
730 ===============
731 CL_BlobExplosion
732
733 ===============
734 */
735 void CL_BlobExplosion (vec3_t org)
736 {
737         CL_ParticleExplosion(org);
738 }
739
740 /*
741 ===============
742 CL_RunParticleEffect
743
744 ===============
745 */
746 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
747 {
748         int k;
749
750         if (count == 1024)
751         {
752                 CL_ParticleExplosion(org);
753                 return;
754         }
755         if (!cl_particles.integer) return;
756         count *= cl_particles_quality.value;
757         while (count--)
758         {
759                 k = particlepalette[color + (rand()&7)];
760                 if (gamemode == GAME_GOODVSBAD2)
761                         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);
762                 else
763                         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);
764         }
765 }
766
767 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
768 /*
769 ===============
770 CL_SparkShower
771 ===============
772 */
773 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
774 {
775         int k;
776
777         if (!cl_particles.integer) return;
778
779         if (cl_particles_sparks.integer)
780         {
781                 // sparks
782                 count *= cl_particles_quality.value;
783                 while(count--)
784                 {
785                         k = particlepalette[0x68 + (rand() & 7)];
786                         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);
787                 }
788         }
789 }
790
791 void CL_Smoke (vec3_t org, vec3_t dir, int count)
792 {
793         vec3_t org2;
794         int k;
795         trace_t trace;
796
797         if (!cl_particles.integer) return;
798
799         // smoke puff
800         if (cl_particles_smoke.integer)
801         {
802                 k = count * 0.25 * cl_particles_quality.value;
803                 while(k--)
804                 {
805                         org2[0] = org[0] + 0.125f * lhrandom(-count, count);
806                         org2[1] = org[1] + 0.125f * lhrandom(-count, count);
807                         org2[2] = org[2] + 0.125f * lhrandom(-count, count);
808                         trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
809                         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);
810                 }
811         }
812 }
813
814 void CL_BulletMark (vec3_t org)
815 {
816         if (cl_stainmaps.integer)
817                 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
818         CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
819 }
820
821 void CL_PlasmaBurn (vec3_t org)
822 {
823         if (cl_stainmaps.integer)
824                 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
825         CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
826 }
827
828 static float bloodcount = 0;
829 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
830 {
831         float s;
832         vec3_t org2;
833         trace_t trace;
834         // bloodcount is used to accumulate counts too small to cause a blood particle
835         if (!cl_particles.integer) return;
836         if (!cl_particles_blood.integer) return;
837
838         s = count + 64.0f;
839         count *= 5.0f;
840         if (count > 1000)
841                 count = 1000;
842         bloodcount += count;
843         while(bloodcount > 0)
844         {
845                 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
846                 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
847                 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
848                 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
849                 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);
850                 bloodcount -= 16 / cl_particles_quality.value;
851         }
852 }
853
854 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
855 {
856         vec3_t org, vel, diff, center, velscale;
857         if (!cl_particles.integer) return;
858         if (!cl_particles_bloodshowers.integer) return;
859         if (!cl_particles_blood.integer) return;
860
861         VectorSubtract(maxs, mins, diff);
862         center[0] = (mins[0] + maxs[0]) * 0.5;
863         center[1] = (mins[1] + maxs[1]) * 0.5;
864         center[2] = (mins[2] + maxs[2]) * 0.5;
865         velscale[0] = velspeed * 2.0 / diff[0];
866         velscale[1] = velspeed * 2.0 / diff[1];
867         velscale[2] = velspeed * 2.0 / diff[2];
868
869         bloodcount += count * 5.0f;
870         while (bloodcount > 0)
871         {
872                 org[0] = lhrandom(mins[0], maxs[0]);
873                 org[1] = lhrandom(mins[1], maxs[1]);
874                 org[2] = lhrandom(mins[2], maxs[2]);
875                 vel[0] = (org[0] - center[0]) * velscale[0];
876                 vel[1] = (org[1] - center[1]) * velscale[1];
877                 vel[2] = (org[2] - center[2]) * velscale[2];
878                 bloodcount -= 16 / cl_particles_quality.value;
879                 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);
880         }
881 }
882
883 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
884 {
885         int k;
886         float t;
887         if (!cl_particles.integer) return;
888         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
889         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
890         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
891
892         count *= cl_particles_quality.value;
893         while (count--)
894         {
895                 k = particlepalette[colorbase + (rand()&3)];
896                 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);
897         }
898 }
899
900 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
901 {
902         int k;
903         float t, z, minz, maxz;
904         particle_t *p;
905         if (!cl_particles.integer) return;
906         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
907         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
908         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
909         if (dir[2] < 0) // falling
910                 z = maxs[2];
911         else // rising??
912                 z = mins[2];
913
914         minz = z - fabs(dir[2]) * 0.1;
915         maxz = z + fabs(dir[2]) * 0.1;
916         minz = bound(mins[2], minz, maxs[2]);
917         maxz = bound(mins[2], maxz, maxs[2]);
918
919         count *= cl_particles_quality.value;
920
921         switch(type)
922         {
923         case 0:
924                 count *= 4; // ick, this should be in the mod or maps?
925
926                 while(count--)
927                 {
928                         k = particlepalette[colorbase + (rand()&3)];
929                         if (gamemode == GAME_GOODVSBAD2)
930                                 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);
931                         else
932                                 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);
933                 }
934                 break;
935         case 1:
936                 while(count--)
937                 {
938                         k = particlepalette[colorbase + (rand()&3)];
939                         if (gamemode == GAME_GOODVSBAD2)
940                                 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);
941                         else
942                                 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);
943                         if (p)
944                                 VectorCopy(p->vel, p->relativedirection);
945                 }
946                 break;
947         default:
948                 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
949         }
950 }
951
952 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
953 {
954         int k;
955         float t;
956         vec3_t o, v, center;
957         if (!cl_particles.integer) return;
958
959         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
960         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
961         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
962
963         center[0] = (mins[0] + maxs[0]) * 0.5f;
964         center[1] = (mins[1] + maxs[1]) * 0.5f;
965         center[2] = (mins[2] + maxs[2]) * 0.5f;
966
967         count *= cl_particles_quality.value;
968         while (count--)
969         {
970                 k = particlepalette[224 + (rand()&15)];
971                 o[0] = lhrandom(mins[0], maxs[0]);
972                 o[1] = lhrandom(mins[1], maxs[1]);
973                 o[2] = lhrandom(mins[2], maxs[2]);
974                 VectorSubtract(o, center, v);
975                 VectorNormalize(v);
976                 VectorScale(v, 100, v);
977                 v[2] += sv_gravity.value * 0.15f;
978                 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);
979         }
980 }
981
982 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
983 {
984         int k;
985         float t;
986         if (!cl_particles.integer) return;
987         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
988         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
989         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
990
991         count *= cl_particles_quality.value;
992         while (count--)
993         {
994                 k = particlepalette[224 + (rand()&15)];
995                 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);
996                 if (count & 1)
997                         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);
998         }
999 }
1000
1001 void CL_Flames (vec3_t org, vec3_t vel, int count)
1002 {
1003         int k;
1004         if (!cl_particles.integer) return;
1005
1006         count *= cl_particles_quality.value;
1007         while (count--)
1008         {
1009                 k = particlepalette[224 + (rand()&15)];
1010                 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);
1011         }
1012 }
1013
1014
1015
1016 /*
1017 ===============
1018 CL_LavaSplash
1019
1020 ===============
1021 */
1022 void CL_LavaSplash (vec3_t origin)
1023 {
1024         float i, j, inc, vel;
1025         int k, l;
1026         vec3_t          dir, org;
1027         if (!cl_particles.integer) return;
1028
1029         inc = 32 / cl_particles_quality.value;
1030         for (i = -128;i < 128;i += inc)
1031         {
1032                 for (j = -128;j < 128;j += inc)
1033                 {
1034                         dir[0] = j + lhrandom(0, 8);
1035                         dir[1] = i + lhrandom(0, 8);
1036                         dir[2] = 256;
1037                         org[0] = origin[0] + dir[0];
1038                         org[1] = origin[1] + dir[1];
1039                         org[2] = origin[2] + lhrandom(0, 64);
1040                         vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1041                         if (gamemode == GAME_GOODVSBAD2)
1042                         {
1043                                 k = particlepalette[0 + (rand()&255)];
1044                                 l = particlepalette[0 + (rand()&255)];
1045                                 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);
1046                         }
1047                         else
1048                         {
1049                                 k = l = particlepalette[224 + (rand()&7)];
1050                                 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);
1051                         }
1052                 }
1053         }
1054 }
1055
1056 /*
1057 ===============
1058 CL_TeleportSplash
1059
1060 ===============
1061 */
1062 void CL_TeleportSplash (vec3_t org)
1063 {
1064         float i, j, k, inc;
1065         if (!cl_particles.integer) return;
1066
1067         inc = 8 / cl_particles_quality.value;
1068         for (i = -16;i < 16;i += inc)
1069                 for (j = -16;j < 16;j += inc)
1070                         for (k = -24;k < 32;k += inc)
1071                                 particle(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);
1072 }
1073
1074 #ifdef WORKINGLQUAKE
1075 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1076 #else
1077 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1078 #endif
1079 {
1080         vec3_t vec, dir, vel, pos;
1081         float len, dec, speed, qd;
1082         int smoke, blood, bubbles;
1083 #ifdef WORKINGLQUAKE
1084         int contents;
1085 #endif
1086
1087         if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1088                 return;
1089
1090         VectorSubtract(end, start, dir);
1091         VectorNormalize(dir);
1092
1093         VectorSubtract (end, start, vec);
1094 #ifdef WORKINGLQUAKE
1095         len = VectorNormalize (vec);
1096         dec = 0;
1097         speed = 1.0f / cl.frametime;
1098         VectorSubtract(end, start, vel);
1099 #else
1100         len = VectorNormalizeLength (vec);
1101         dec = -ent->persistent.trail_time;
1102         ent->persistent.trail_time += len;
1103         if (ent->persistent.trail_time < 0.01f)
1104                 return;
1105
1106         // if we skip out, leave it reset
1107         ent->persistent.trail_time = 0.0f;
1108
1109         speed = ent->state_current.time - ent->state_previous.time;
1110         if (speed)
1111                 speed = 1.0f / speed;
1112         VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1113         color = particlepalette[color];
1114 #endif
1115         VectorScale(vel, speed, vel);
1116
1117         // advance into this frame to reach the first puff location
1118         VectorMA(start, dec, vec, pos);
1119         len -= dec;
1120
1121         smoke = cl_particles.integer && cl_particles_smoke.integer;
1122         blood = cl_particles.integer && cl_particles_blood.integer;
1123 #ifdef WORKINGLQUAKE
1124         contents = CL_PointQ1Contents(pos);
1125         bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1126 #else
1127         bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1128 #endif
1129         qd = 1.0f / cl_particles_quality.value;
1130
1131         while (len >= 0)
1132         {
1133                 switch (type)
1134                 {
1135                         case 0: // rocket trail
1136                                 dec = qd*3;
1137                                 if (smoke)
1138                                 {
1139                                         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);
1140                                         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);
1141                                 }
1142                                 if (bubbles)
1143                                         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));
1144                                 break;
1145
1146                         case 1: // grenade trail
1147                                 // FIXME: make it gradually stop smoking
1148                                 dec = qd*3;
1149                                 if (smoke)
1150                                         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);
1151                                 break;
1152
1153
1154                         case 2: // blood
1155                         case 4: // slight blood
1156                                 dec = qd*16;
1157                                 if (blood)
1158                                         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);
1159                                 break;
1160
1161                         case 3: // green tracer
1162                                 dec = qd*6;
1163                                 if (smoke)
1164                                 {
1165                                         if (gamemode == GAME_GOODVSBAD2)
1166                                                 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);
1167                                         else
1168                                                 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);
1169                                 }
1170                                 break;
1171
1172                         case 5: // flame tracer
1173                                 dec = qd*6;
1174                                 if (smoke)
1175                                         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);
1176                                 break;
1177
1178                         case 6: // voor trail
1179                                 dec = qd*6;
1180                                 if (smoke)
1181                                 {
1182                                         if (gamemode == GAME_GOODVSBAD2)
1183                                                 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);
1184                                         else if (gamemode == GAME_PRYDON)
1185                                                 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);
1186                                         else
1187                                                 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);
1188                                 }
1189                                 break;
1190 #ifndef WORKINGLQUAKE
1191                         case 7: // Nehahra smoke tracer
1192                                 dec = qd*7;
1193                                 if (smoke)
1194                                         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);
1195                                 break;
1196                         case 8: // Nexuiz plasma trail
1197                                 dec = qd*4;
1198                                 if (smoke)
1199                                         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);
1200                                 break;
1201                         case 9: // glow trail
1202                                 dec = qd*3;
1203                                 if (smoke)
1204                                         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);
1205                                 break;
1206 #endif
1207                         default:
1208                                 Sys_Error("CL_RocketTrail: unknown trail type %i", type);
1209                 }
1210
1211                 // advance to next time and position
1212                 len -= dec;
1213                 VectorMA (pos, dec, vec, pos);
1214         }
1215 #ifndef WORKINGLQUAKE
1216         ent->persistent.trail_time = len;
1217 #endif
1218 }
1219
1220 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1221 {
1222         int tempcolor2, cr, cg, cb;
1223         cr = red * 255;
1224         cg = green * 255;
1225         cb = blue * 255;
1226         tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1227         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);
1228 }
1229
1230 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1231 {
1232         float f;
1233         if (!cl_particles.integer) return;
1234
1235         // smoke puff
1236         if (cl_particles_smoke.integer)
1237                 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1238                         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);
1239 }
1240
1241 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1242 {
1243         float f;
1244         if (!cl_particles.integer) return;
1245
1246         if (cl_stainmaps.integer)
1247                 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1248         CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1249
1250         // smoke puff
1251         if (cl_particles_smoke.integer)
1252                 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1253                         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);
1254
1255         // sparks
1256         if (cl_particles_sparks.integer)
1257                 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1258                         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);
1259 }
1260
1261 /*
1262 ===============
1263 CL_MoveParticles
1264 ===============
1265 */
1266 void CL_MoveParticles (void)
1267 {
1268         particle_t *p;
1269         int i, maxparticle, j, a, content;
1270         float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
1271         int hitent;
1272         trace_t trace;
1273
1274         // LordHavoc: early out condition
1275         if (!cl_numparticles)
1276         {
1277                 cl_freeparticle = 0;
1278                 return;
1279         }
1280
1281 #ifdef WORKINGLQUAKE
1282         frametime = cl.frametime;
1283 #else
1284         frametime = cl.time - cl.oldtime;
1285 #endif
1286         gravity = frametime * sv_gravity.value;
1287         dvel = 1+4*frametime;
1288         bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1289
1290         maxparticle = -1;
1291         j = 0;
1292         for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1293         {
1294                 if (!p->type)
1295                         continue;
1296                 maxparticle = i;
1297                 content = 0;
1298
1299                 p->alpha -= p->alphafade * frametime;
1300
1301                 if (p->alpha <= 0)
1302                 {
1303                         p->type = NULL;
1304                         continue;
1305                 }
1306
1307                 if (p->type->orientation != PARTICLE_BEAM)
1308                 {
1309                         VectorCopy(p->org, oldorg);
1310                         VectorMA(p->org, frametime, p->vel, p->org);
1311                         VectorCopy(p->org, org);
1312                         if (p->bounce)
1313                         {
1314                                 if (p->type == particletype + pt_rain)
1315                                 {
1316                                         // raindrop - splash on solid/water/slime/lava
1317                                         trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
1318                                         if (trace.fraction < 1)
1319                                         {
1320                                                 // convert from a raindrop particle to a rainsplash decal
1321                                                 VectorCopy(trace.endpos, p->org);
1322                                                 VectorCopy(trace.plane.normal, p->vel);
1323                                                 VectorAdd(p->org, p->vel, p->org);
1324                                                 p->type = particletype + pt_raindecal;
1325                                                 p->texnum = tex_rainsplash[0];
1326                                                 p->time2 = cl.time;
1327                                                 p->alphafade = p->alpha / 0.4;
1328                                                 p->bounce = 0;
1329                                                 p->friction = 0;
1330                                                 p->gravity = 0;
1331                                                 p->size = 8.0;
1332                                         }
1333                                 }
1334                                 else if (p->type == particletype + pt_blood)
1335                                 {
1336                                         // blood - splash on solid
1337                                         trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
1338                                         if (trace.fraction < 1)
1339                                         {
1340                                                 // convert from a blood particle to a blood decal
1341                                                 VectorCopy(trace.endpos, p->org);
1342                                                 VectorCopy(trace.plane.normal, p->vel);
1343                                                 VectorAdd(p->org, p->vel, p->org);
1344 #ifndef WORKINGLQUAKE
1345                                                 if (cl_stainmaps.integer)
1346                                                         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));
1347 #endif
1348                                                 if (!cl_decals.integer)
1349                                                 {
1350                                                         p->type = NULL;
1351                                                         continue;
1352                                                 }
1353
1354                                                 p->type = particletype + pt_decal;
1355                                                 p->texnum = tex_blooddecal[rand()&7];
1356 #ifndef WORKINGLQUAKE
1357                                                 p->owner = hitent;
1358                                                 p->ownermodel = cl_entities[hitent].render.model;
1359                                                 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1360                                                 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1361 #endif
1362                                                 p->time2 = cl.time;
1363                                                 p->alphafade = 0;
1364                                                 p->bounce = 0;
1365                                                 p->friction = 0;
1366                                                 p->gravity = 0;
1367                                                 p->size *= 2.0f;
1368                                         }
1369                                 }
1370                                 else
1371                                 {
1372                                         trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
1373                                         if (trace.fraction < 1)
1374                                         {
1375                                                 VectorCopy(trace.endpos, p->org);
1376                                                 if (p->bounce < 0)
1377                                                 {
1378                                                         p->type = NULL;
1379                                                         continue;
1380                                                 }
1381                                                 else
1382                                                 {
1383                                                         dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1384                                                         VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1385                                                         if (DotProduct(p->vel, p->vel) < 0.03)
1386                                                                 VectorClear(p->vel);
1387                                                 }
1388                                         }
1389                                 }
1390                         }
1391                         p->vel[2] -= p->gravity * gravity;
1392
1393                         if (p->friction)
1394                         {
1395                                 f = p->friction * frametime;
1396 #ifdef WORKINGLQUAKE
1397                                 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1398 #else
1399                                 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1400 #endif
1401                                         f *= 4;
1402                                 f = 1.0f - f;
1403                                 VectorScale(p->vel, f, p->vel);
1404                         }
1405                 }
1406
1407                 if (p->type != particletype + pt_static)
1408                 {
1409                         switch (p->type - particletype)
1410                         {
1411                         case pt_entityparticle:
1412                                 // particle that removes itself after one rendered frame
1413                                 if (p->time2)
1414                                         p->type = NULL;
1415                                 else
1416                                         p->time2 = 1;
1417                                 break;
1418                         case pt_blood:
1419 #ifdef WORKINGLQUAKE
1420                                 a = CL_PointQ1Contents(p->org);
1421                                 if (a <= CONTENTS_WATER)
1422 #else
1423                                 a = CL_PointSuperContents(p->org);
1424                                 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1425 #endif
1426                                 {
1427                                         p->size += frametime * 8;
1428                                         //p->alpha -= bloodwaterfade;
1429                                 }
1430                                 else
1431                                         p->vel[2] -= gravity;
1432 #ifdef WORKINGLQUAKE
1433                                 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1434 #else
1435                                 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1436 #endif
1437                                         p->type = NULL;
1438                                 break;
1439                         case pt_bubble:
1440 #ifdef WORKINGLQUAKE
1441                                 a = CL_PointQ1Contents(p->org);
1442                                 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1443 #else
1444                                 a = CL_PointSuperContents(p->org);
1445                                 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1446 #endif
1447                                 {
1448                                         p->type = NULL;
1449                                         break;
1450                                 }
1451                                 break;
1452                         case pt_rain:
1453 #ifdef WORKINGLQUAKE
1454                                 a = CL_PointQ1Contents(p->org);
1455                                 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1456 #else
1457                                 a = CL_PointSuperContents(p->org);
1458                                 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1459 #endif
1460                                         p->type = NULL;
1461                                 break;
1462                         case pt_snow:
1463                                 if (cl.time > p->time2)
1464                                 {
1465                                         // snow flutter
1466                                         p->time2 = cl.time + (rand() & 3) * 0.1;
1467                                         p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
1468                                         p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1469                                         //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
1470                                 }
1471 #ifdef WORKINGLQUAKE
1472                                 a = CL_PointQ1Contents(p->org);
1473                                 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1474 #else
1475                                 a = CL_PointSuperContents(p->org);
1476                                 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1477 #endif
1478                                         p->type = NULL;
1479                                 break;
1480                         case pt_smoke:
1481                                 //p->size += frametime * 15;
1482                                 break;
1483                         case pt_decal:
1484                                 // FIXME: this has fairly wacky handling of alpha
1485                                 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1486 #ifndef WORKINGLQUAKE
1487                                 if (cl_entities[p->owner].render.model == p->ownermodel)
1488                                 {
1489                                         Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1490                                         Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1491                                 }
1492                                 else
1493                                         p->type = NULL;
1494 #endif
1495                                 break;
1496                         case pt_raindecal:
1497                                 a = max(0, (cl.time - p->time2) * 40);
1498                                 if (a < 16)
1499                                         p->texnum = tex_rainsplash[a];
1500                                 else
1501                                         p->type = NULL;
1502                                 break;
1503                         default:
1504                                 break;
1505                         }
1506                 }
1507         }
1508         cl_numparticles = maxparticle + 1;
1509         cl_freeparticle = 0;
1510 }
1511
1512 #define MAX_PARTICLETEXTURES 64
1513 // particletexture_t is a rectangle in the particlefonttexture
1514 typedef struct particletexture_s
1515 {
1516         rtexture_t *texture;
1517         float s1, t1, s2, t2;
1518 }
1519 particletexture_t;
1520
1521 #if WORKINGLQUAKE
1522 static int particlefonttexture;
1523 #else
1524 static rtexturepool_t *particletexturepool;
1525 static rtexture_t *particlefonttexture;
1526 #endif
1527 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1528
1529 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1530
1531 #define PARTICLETEXTURESIZE 64
1532 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1533
1534 static unsigned char shadebubble(float dx, float dy, vec3_t light)
1535 {
1536         float dz, f, dot;
1537         vec3_t normal;
1538         dz = 1 - (dx*dx+dy*dy);
1539         if (dz > 0) // it does hit the sphere
1540         {
1541                 f = 0;
1542                 // back side
1543                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1544                 VectorNormalize(normal);
1545                 dot = DotProduct(normal, light);
1546                 if (dot > 0.5) // interior reflection
1547                         f += ((dot *  2) - 1);
1548                 else if (dot < -0.5) // exterior reflection
1549                         f += ((dot * -2) - 1);
1550                 // front side
1551                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1552                 VectorNormalize(normal);
1553                 dot = DotProduct(normal, light);
1554                 if (dot > 0.5) // interior reflection
1555                         f += ((dot *  2) - 1);
1556                 else if (dot < -0.5) // exterior reflection
1557                         f += ((dot * -2) - 1);
1558                 f *= 128;
1559                 f += 16; // just to give it a haze so you can see the outline
1560                 f = bound(0, f, 255);
1561                 return (unsigned char) f;
1562         }
1563         else
1564                 return 0;
1565 }
1566
1567 static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
1568 {
1569         int basex, basey, y;
1570         basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1571         basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1572         particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1573         particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1574         particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1575         particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1576         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1577                 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1578 }
1579
1580 void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
1581 {
1582         int x, y;
1583         float cx, cy, dx, dy, f, iradius;
1584         unsigned char *d;
1585         cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1586         cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1587         iradius = 1.0f / radius;
1588         alpha *= (1.0f / 255.0f);
1589         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1590         {
1591                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1592                 {
1593                         dx = (x - cx);
1594                         dy = (y - cy);
1595                         f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1596                         if (f > 0)
1597                         {
1598                                 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1599                                 d[0] += f * (red   - d[0]);
1600                                 d[1] += f * (green - d[1]);
1601                                 d[2] += f * (blue  - d[2]);
1602                         }
1603                 }
1604         }
1605 }
1606
1607 void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1608 {
1609         int i;
1610         for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1611         {
1612                 data[0] = bound(minr, data[0], maxr);
1613                 data[1] = bound(ming, data[1], maxg);
1614                 data[2] = bound(minb, data[2], maxb);
1615         }
1616 }
1617
1618 void particletextureinvert(unsigned char *data)
1619 {
1620         int i;
1621         for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1622         {
1623                 data[0] = 255 - data[0];
1624                 data[1] = 255 - data[1];
1625                 data[2] = 255 - data[2];
1626         }
1627 }
1628
1629 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1630 static void R_InitBloodTextures (unsigned char *particletexturedata)
1631 {
1632         int i, j, k, m;
1633         unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1634
1635         // blood particles
1636         for (i = 0;i < 8;i++)
1637         {
1638                 memset(&data[0][0][0], 255, sizeof(data));
1639                 for (k = 0;k < 24;k++)
1640                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1641                 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1642                 particletextureinvert(&data[0][0][0]);
1643                 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1644         }
1645
1646         // blood decals
1647         for (i = 0;i < 8;i++)
1648         {
1649                 memset(&data[0][0][0], 255, sizeof(data));
1650                 m = 8;
1651                 for (j = 1;j < 10;j++)
1652                         for (k = min(j, m - 1);k < m;k++)
1653                                 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1654                 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1655                 particletextureinvert(&data[0][0][0]);
1656                 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1657         }
1658
1659 }
1660
1661 static void R_InitParticleTexture (void)
1662 {
1663         int x, y, d, i, k, m;
1664         float dx, dy, radius, f, f2;
1665         unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1666         vec3_t light;
1667         unsigned char *particletexturedata;
1668
1669         // a note: decals need to modulate (multiply) the background color to
1670         // properly darken it (stain), and they need to be able to alpha fade,
1671         // this is a very difficult challenge because it means fading to white
1672         // (no change to background) rather than black (darkening everything
1673         // behind the whole decal polygon), and to accomplish this the texture is
1674         // inverted (dark red blood on white background becomes brilliant cyan
1675         // and white on black background) so we can alpha fade it to black, then
1676         // we invert it again during the blendfunc to make it work...
1677
1678         particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1679         memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1680
1681         // smoke
1682         for (i = 0;i < 8;i++)
1683         {
1684                 memset(&data[0][0][0], 255, sizeof(data));
1685                 do
1686                 {
1687                         unsigned char noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1688
1689                         fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1690                         fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1691                         m = 0;
1692                         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1693                         {
1694                                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1695                                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1696                                 {
1697                                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1698                                         d = (noise2[y][x] - 128) * 3 + 192;
1699                                         if (d > 0)
1700                                                 d = d * (1-(dx*dx+dy*dy));
1701                                         d = (d * noise1[y][x]) >> 7;
1702                                         d = bound(0, d, 255);
1703                                         data[y][x][3] = (unsigned char) d;
1704                                         if (m < d)
1705                                                 m = d;
1706                                 }
1707                         }
1708                 }
1709                 while (m < 224);
1710                 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1711         }
1712
1713         // rain splash
1714         for (i = 0;i < 16;i++)
1715         {
1716                 memset(&data[0][0][0], 255, sizeof(data));
1717                 radius = i * 3.0f / 4.0f / 16.0f;
1718                 f2 = 255.0f * ((15.0f - i) / 15.0f);
1719                 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1720                 {
1721                         dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1722                         for (x = 0;x < PARTICLETEXTURESIZE;x++)
1723                         {
1724                                 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1725                                 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1726                                 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1727                         }
1728                 }
1729                 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1730         }
1731
1732         // normal particle
1733         memset(&data[0][0][0], 255, sizeof(data));
1734         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1735         {
1736                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1737                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1738                 {
1739                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1740                         d = 256 * (1 - (dx*dx+dy*dy));
1741                         d = bound(0, d, 255);
1742                         data[y][x][3] = (unsigned char) d;
1743                 }
1744         }
1745         setuptex(tex_particle, &data[0][0][0], particletexturedata);
1746
1747         // rain
1748         memset(&data[0][0][0], 255, sizeof(data));
1749         light[0] = 1;light[1] = 1;light[2] = 1;
1750         VectorNormalize(light);
1751         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1752         {
1753                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1754                 // stretch upper half of bubble by +50% and shrink lower half by -50%
1755                 // (this gives an elongated teardrop shape)
1756                 if (dy > 0.5f)
1757                         dy = (dy - 0.5f) * 2.0f;
1758                 else
1759                         dy = (dy - 0.5f) / 1.5f;
1760                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1761                 {
1762                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1763                         // shrink bubble width to half
1764                         dx *= 2.0f;
1765                         data[y][x][3] = shadebubble(dx, dy, light);
1766                 }
1767         }
1768         setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1769
1770         // bubble
1771         memset(&data[0][0][0], 255, sizeof(data));
1772         light[0] = 1;light[1] = 1;light[2] = 1;
1773         VectorNormalize(light);
1774         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1775         {
1776                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1777                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1778                 {
1779                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1780                         data[y][x][3] = shadebubble(dx, dy, light);
1781                 }
1782         }
1783         setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1784
1785         // Blood particles and blood decals
1786         R_InitBloodTextures (particletexturedata);
1787
1788         // bullet decals
1789         for (i = 0;i < 8;i++)
1790         {
1791                 memset(&data[0][0][0], 255, sizeof(data));
1792                 for (k = 0;k < 12;k++)
1793                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1794                 for (k = 0;k < 3;k++)
1795                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1796                 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1797                 particletextureinvert(&data[0][0][0]);
1798                 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1799         }
1800
1801 #if WORKINGLQUAKE
1802         glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1803         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1804         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1805 #else
1806
1807 #if 0
1808         Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1809 #endif
1810
1811         particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1812         if (!particlefonttexture)
1813                 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1814         for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1815                 particletexture[i].texture = particlefonttexture;
1816
1817         // nexbeam
1818         fractalnoise(&noise3[0][0], 64, 4);
1819         m = 0;
1820         for (y = 0;y < 64;y++)
1821         {
1822                 dy = (y - 0.5f*64) / (64*0.5f-1);
1823                 for (x = 0;x < 16;x++)
1824                 {
1825                         dx = (x - 0.5f*16) / (16*0.5f-2);
1826                         d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1827                         data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
1828                         data2[y][x][3] = 255;
1829                 }
1830         }
1831
1832 #if 0
1833         Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1834 #endif
1835
1836         particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1837         if (!particletexture[tex_beam].texture)
1838                 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1839         particletexture[tex_beam].s1 = 0;
1840         particletexture[tex_beam].t1 = 0;
1841         particletexture[tex_beam].s2 = 1;
1842         particletexture[tex_beam].t2 = 1;
1843 #endif
1844         Mem_Free(particletexturedata);
1845 }
1846
1847 static void r_part_start(void)
1848 {
1849         particletexturepool = R_AllocTexturePool();
1850         R_InitParticleTexture ();
1851 }
1852
1853 static void r_part_shutdown(void)
1854 {
1855         R_FreeTexturePool(&particletexturepool);
1856 }
1857
1858 static void r_part_newmap(void)
1859 {
1860         cl_numparticles = 0;
1861         cl_freeparticle = 0;
1862 }
1863
1864 void R_Particles_Init (void)
1865 {
1866         Cvar_RegisterVariable(&r_drawparticles);
1867 #ifdef WORKINGLQUAKE
1868         r_part_start();
1869 #else
1870         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1871 #endif
1872 }
1873
1874 #ifdef WORKINGLQUAKE
1875 void R_InitParticles(void)
1876 {
1877         CL_Particles_Init();
1878         R_Particles_Init();
1879 }
1880 #endif
1881
1882 float particle_vertex3f[12], particle_texcoord2f[8];
1883
1884 #ifdef WORKINGLQUAKE
1885 void R_DrawParticle(particle_t *p)
1886 {
1887 #else
1888 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1889 {
1890         const particle_t *p = (particle_t *)calldata1;
1891         rmeshstate_t m;
1892 #endif
1893         pblend_t blendmode;
1894         float org[3], up2[3], v[3], right[3], up[3], fog, ifog, cr, cg, cb, ca, size;
1895         particletexture_t *tex;
1896
1897         VectorCopy(p->org, org);
1898
1899         blendmode = p->type->blendmode;
1900         tex = &particletexture[p->texnum];
1901         cr = p->color[0] * (1.0f / 255.0f);
1902         cg = p->color[1] * (1.0f / 255.0f);
1903         cb = p->color[2] * (1.0f / 255.0f);
1904         ca = p->alpha * (1.0f / 255.0f);
1905         if (blendmode == PBLEND_MOD)
1906         {
1907                 cr *= ca;
1908                 cg *= ca;
1909                 cb *= ca;
1910                 cr = min(cr, 1);
1911                 cg = min(cg, 1);
1912                 cb = min(cb, 1);
1913                 ca = 1;
1914         }
1915 #ifndef WORKINGLQUAKE
1916         if (p->type->lighting)
1917         {
1918                 float ambient[3], diffuse[3], diffusenormal[3];
1919                 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
1920                 cr *= (ambient[0] + 0.5 * diffuse[0]);
1921                 cg *= (ambient[1] + 0.5 * diffuse[1]);
1922                 cb *= (ambient[2] + 0.5 * diffuse[2]);
1923         }
1924         if (fogenabled)
1925         {
1926                 fog = VERTEXFOGTABLE(VectorDistance(org, r_vieworigin));
1927                 ifog = 1 - fog;
1928                 cr = cr * ifog;
1929                 cg = cg * ifog;
1930                 cb = cb * ifog;
1931                 if (blendmode == PBLEND_ALPHA)
1932                 {
1933                         cr += fogcolor[0] * fog;
1934                         cg += fogcolor[1] * fog;
1935                         cb += fogcolor[2] * fog;
1936                 }
1937         }
1938
1939         R_Mesh_Matrix(&r_identitymatrix);
1940
1941         memset(&m, 0, sizeof(m));
1942         m.tex[0] = R_GetTexture(tex->texture);
1943         m.pointer_texcoord[0] = particle_texcoord2f;
1944         m.pointer_vertex = particle_vertex3f;
1945         R_Mesh_State(&m);
1946
1947         GL_Color(cr, cg, cb, ca);
1948
1949         if (blendmode == PBLEND_ALPHA)
1950                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1951         else if (blendmode == PBLEND_ADD)
1952                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1953         else //if (blendmode == PBLEND_MOD)
1954                 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1955         GL_DepthMask(false);
1956         GL_DepthTest(true);
1957 #endif
1958         size = p->size * cl_particles_size.value;
1959         if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1960         {
1961                 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1962                 {
1963                         // double-sided
1964                         if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1965                         {
1966                                 VectorNegate(p->vel, v);
1967                                 VectorVectors(v, right, up);
1968                         }
1969                         else
1970                                 VectorVectors(p->vel, right, up);
1971                         VectorScale(right, size, right);
1972                         VectorScale(up, size, up);
1973                 }
1974                 else
1975                 {
1976                         VectorScale(r_viewleft, -size, right);
1977                         VectorScale(r_viewup, size, up);
1978                 }
1979                 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1980                 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1981                 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1982                 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1983                 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1984                 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1985                 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1986                 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1987                 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1988                 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1989                 particle_vertex3f[10] = org[1] + right[1] - up[1];
1990                 particle_vertex3f[11] = org[2] + right[2] - up[2];
1991                 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1992                 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1993                 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1994                 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1995         }
1996         else if (p->type->orientation == PARTICLE_SPARK)
1997         {
1998                 VectorMA(p->org, -0.02, p->vel, v);
1999                 VectorMA(p->org, 0.02, p->vel, up2);
2000                 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2001                 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2002                 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2003                 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2004                 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2005         }
2006         else if (p->type->orientation == PARTICLE_BEAM)
2007         {
2008                 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2009                 VectorSubtract(p->vel, p->org, up);
2010                 VectorNormalize(up);
2011                 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2012                 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2013                 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2014                 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2015                 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2016                 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2017         }
2018         else
2019         {
2020                 Con_Printf("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2021                 return;
2022         }
2023
2024 #if WORKINGLQUAKE
2025         if (blendmode == PBLEND_ALPHA)
2026                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2027         else if (blendmode == PBLEND_ADD)
2028                 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2029         else //if (blendmode == PBLEND_MOD)
2030                 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2031         glColor4f(cr, cg, cb, ca);
2032         glBegin(GL_QUADS);
2033         glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2034         glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2035         glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2036         glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2037         glEnd();
2038 #else
2039         R_Mesh_Draw(0, 4, 2, polygonelements);
2040 #endif
2041 }
2042
2043 void R_DrawParticles (void)
2044 {
2045         int i;
2046         float minparticledist;
2047         particle_t *p;
2048
2049 #ifdef WORKINGLQUAKE
2050         CL_MoveParticles();
2051 #endif
2052
2053         // LordHavoc: early out conditions
2054         if ((!cl_numparticles) || (!r_drawparticles.integer))
2055                 return;
2056
2057         minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2058
2059 #ifdef WORKINGLQUAKE
2060         glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2061         glEnable(GL_BLEND);
2062         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2063         glDepthMask(0);
2064         // LordHavoc: only render if not too close
2065         for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2066                 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2067                         R_DrawParticle(p);
2068         glDepthMask(1);
2069         glDisable(GL_BLEND);
2070         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2071 #else
2072         // LordHavoc: only render if not too close
2073         for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2074         {
2075                 if (p->type)
2076                 {
2077                         renderstats.particles++;
2078                         if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2079                         {
2080                                 if (p->type == particletype + pt_decal)
2081                                         R_DrawParticleCallback(p, 0);
2082                                 else
2083                                         R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);
2084                         }
2085                 }
2086         }
2087 #endif
2088 }
2089