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