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