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