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