]> icculus.org git repositories - divverent/darkplaces.git/blob - cl_particles.c
added a firstvertex parameter to R_Mesh_DrawMesh
[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) ? (255 / 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 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1641 static void R_InitBloodTextures (qbyte *particletexturedata)
1642 {
1643         int i, j, k, m;
1644         qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1645
1646         // blood particles
1647         for (i = 0;i < 8;i++)
1648         {
1649                 memset(&data[0][0][0], 255, sizeof(data));
1650                 for (k = 0;k < 24;k++)
1651                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1652                 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1653                 particletextureinvert(&data[0][0][0]);
1654                 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1655         }
1656
1657         // blood decals
1658         for (i = 0;i < 8;i++)
1659         {
1660                 memset(&data[0][0][0], 255, sizeof(data));
1661                 m = 8;
1662                 for (j = 1;j < 10;j++)
1663                         for (k = min(j, m - 1);k < m;k++)
1664                                 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1665                 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1666                 particletextureinvert(&data[0][0][0]);
1667                 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1668         }
1669
1670 }
1671
1672 static void R_InitParticleTexture (void)
1673 {
1674         int x, y, d, i, k, m;
1675         float dx, dy, radius, f, f2;
1676         qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1677         vec3_t light;
1678         qbyte *particletexturedata;
1679
1680         // a note: decals need to modulate (multiply) the background color to
1681         // properly darken it (stain), and they need to be able to alpha fade,
1682         // this is a very difficult challenge because it means fading to white
1683         // (no change to background) rather than black (darkening everything
1684         // behind the whole decal polygon), and to accomplish this the texture is
1685         // inverted (dark red blood on white background becomes brilliant cyan
1686         // and white on black background) so we can alpha fade it to black, then
1687         // we invert it again during the blendfunc to make it work...
1688
1689         particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1690         memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1691
1692         // smoke
1693         for (i = 0;i < 8;i++)
1694         {
1695                 memset(&data[0][0][0], 255, sizeof(data));
1696                 do
1697                 {
1698                         qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1699
1700                         fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1701                         fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1702                         m = 0;
1703                         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1704                         {
1705                                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1706                                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1707                                 {
1708                                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1709                                         d = (noise2[y][x] - 128) * 3 + 192;
1710                                         if (d > 0)
1711                                                 d = d * (1-(dx*dx+dy*dy));
1712                                         d = (d * noise1[y][x]) >> 7;
1713                                         d = bound(0, d, 255);
1714                                         data[y][x][3] = (qbyte) d;
1715                                         if (m < d)
1716                                                 m = d;
1717                                 }
1718                         }
1719                 }
1720                 while (m < 224);
1721                 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1722         }
1723
1724         // rain splash
1725         for (i = 0;i < 16;i++)
1726         {
1727                 memset(&data[0][0][0], 255, sizeof(data));
1728                 radius = i * 3.0f / 4.0f / 16.0f;
1729                 f2 = 255.0f * ((15.0f - i) / 15.0f);
1730                 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1731                 {
1732                         dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1733                         for (x = 0;x < PARTICLETEXTURESIZE;x++)
1734                         {
1735                                 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1736                                 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1737                                 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1738                         }
1739                 }
1740                 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1741         }
1742
1743         // normal particle
1744         memset(&data[0][0][0], 255, sizeof(data));
1745         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1746         {
1747                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1748                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1749                 {
1750                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1751                         d = 256 * (1 - (dx*dx+dy*dy));
1752                         d = bound(0, d, 255);
1753                         data[y][x][3] = (qbyte) d;
1754                 }
1755         }
1756         setuptex(tex_particle, &data[0][0][0], particletexturedata);
1757
1758         // rain
1759         memset(&data[0][0][0], 255, sizeof(data));
1760         light[0] = 1;light[1] = 1;light[2] = 1;
1761         VectorNormalize(light);
1762         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1763         {
1764                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1765                 // stretch upper half of bubble by +50% and shrink lower half by -50%
1766                 // (this gives an elongated teardrop shape)
1767                 if (dy > 0.5f)
1768                         dy = (dy - 0.5f) * 2.0f;
1769                 else
1770                         dy = (dy - 0.5f) / 1.5f;
1771                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1772                 {
1773                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1774                         // shrink bubble width to half
1775                         dx *= 2.0f;
1776                         data[y][x][3] = shadebubble(dx, dy, light);
1777                 }
1778         }
1779         setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1780
1781         // bubble
1782         memset(&data[0][0][0], 255, sizeof(data));
1783         light[0] = 1;light[1] = 1;light[2] = 1;
1784         VectorNormalize(light);
1785         for (y = 0;y < PARTICLETEXTURESIZE;y++)
1786         {
1787                 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1788                 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1789                 {
1790                         dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1791                         data[y][x][3] = shadebubble(dx, dy, light);
1792                 }
1793         }
1794         setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1795
1796         // Blood particles and blood decals
1797         R_InitBloodTextures (particletexturedata);
1798
1799         // bullet decals
1800         for (i = 0;i < 8;i++)
1801         {
1802                 memset(&data[0][0][0], 255, sizeof(data));
1803                 for (k = 0;k < 12;k++)
1804                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1805                 for (k = 0;k < 3;k++)
1806                         particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1807                 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1808                 particletextureinvert(&data[0][0][0]);
1809                 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1810         }
1811
1812 #if WORKINGLQUAKE
1813         glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1814         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1815         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1816 #else
1817
1818 #if 0
1819         Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1820 #endif
1821
1822         particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1823         if (!particlefonttexture)
1824                 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1825         for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1826                 particletexture[i].texture = particlefonttexture;
1827
1828         // nexbeam
1829         fractalnoise(&noise3[0][0], 64, 4);
1830         m = 0;
1831         for (y = 0;y < 64;y++)
1832         {
1833                 dy = (y - 0.5f*64) / (64*0.5f-1);
1834                 for (x = 0;x < 16;x++)
1835                 {
1836                         dx = (x - 0.5f*16) / (16*0.5f-2);
1837                         d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1838                         data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1839                         data2[y][x][3] = 255;
1840                 }
1841         }
1842
1843 #if 0
1844         Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1845 #endif
1846
1847         particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1848         if (!particletexture[tex_beam].texture)
1849                 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1850         particletexture[tex_beam].s1 = 0;
1851         particletexture[tex_beam].t1 = 0;
1852         particletexture[tex_beam].s2 = 1;
1853         particletexture[tex_beam].t2 = 1;
1854 #endif
1855         Mem_Free(particletexturedata);
1856 }
1857
1858 static void r_part_start(void)
1859 {
1860         particletexturepool = R_AllocTexturePool();
1861         R_InitParticleTexture ();
1862 }
1863
1864 static void r_part_shutdown(void)
1865 {
1866         R_FreeTexturePool(&particletexturepool);
1867 }
1868
1869 static void r_part_newmap(void)
1870 {
1871         cl_numparticles = 0;
1872         cl_freeparticle = 0;
1873 }
1874
1875 void R_Particles_Init (void)
1876 {
1877         Cvar_RegisterVariable(&r_drawparticles);
1878 #ifdef WORKINGLQUAKE
1879         r_part_start();
1880 #else
1881         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1882 #endif
1883 }
1884
1885 #ifdef WORKINGLQUAKE
1886 void R_InitParticles(void)
1887 {
1888         CL_Particles_Init();
1889         R_Particles_Init();
1890 }
1891 #endif
1892
1893 float particle_vertex3f[12], particle_texcoord2f[8];
1894
1895 #ifdef WORKINGLQUAKE
1896 void R_DrawParticle(particle_t *p)
1897 {
1898 #else
1899 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1900 {
1901         const particle_t *p = calldata1;
1902         rmeshstate_t m;
1903 #endif
1904         pblend_t blendmode;
1905         float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, size;
1906         particletexture_t *tex;
1907
1908         VectorCopy(p->org, org);
1909
1910         blendmode = p->type->blendmode;
1911         tex = &particletexture[p->texnum];
1912         cr = p->color[0] * (1.0f / 255.0f);
1913         cg = p->color[1] * (1.0f / 255.0f);
1914         cb = p->color[2] * (1.0f / 255.0f);
1915         ca = p->alpha * (1.0f / 255.0f);
1916         if (blendmode == PBLEND_MOD)
1917         {
1918                 cr *= ca;
1919                 cg *= ca;
1920                 cb *= ca;
1921                 cr = min(cr, 1);
1922                 cg = min(cg, 1);
1923                 cb = min(cb, 1);
1924                 ca = 1;
1925         }
1926 #ifndef WORKINGLQUAKE
1927         if (fogenabled)
1928         {
1929                 VectorSubtract(org, r_vieworigin, fogvec);
1930                 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1931                 ifog = 1 - fog;
1932                 cr = cr * ifog;
1933                 cg = cg * ifog;
1934                 cb = cb * ifog;
1935                 if (blendmode == PBLEND_ADD)
1936                 {
1937                         cr += fogcolor[0] * fog;
1938                         cg += fogcolor[1] * fog;
1939                         cb += fogcolor[2] * fog;
1940                 }
1941         }
1942
1943         R_Mesh_Matrix(&r_identitymatrix);
1944
1945         memset(&m, 0, sizeof(m));
1946         m.tex[0] = R_GetTexture(tex->texture);
1947         m.pointer_texcoord[0] = particle_texcoord2f;
1948         m.pointer_vertex = particle_vertex3f;
1949         R_Mesh_State(&m);
1950
1951         GL_Color(cr, cg, cb, ca);
1952
1953         if (blendmode == PBLEND_ALPHA)
1954                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1955         else if (blendmode == PBLEND_ADD)
1956                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1957         else //if (blendmode == PBLEND_MOD)
1958                 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1959         GL_DepthMask(false);
1960         GL_DepthTest(true);
1961 #endif
1962         size = p->size * cl_particles_size.value;
1963         if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1964         {
1965                 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1966                 {
1967                         // double-sided
1968                         if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1969                         {
1970                                 VectorNegate(p->vel, v);
1971                                 VectorVectors(v, right, up);
1972                         }
1973                         else
1974                                 VectorVectors(p->vel, right, up);
1975                         VectorScale(right, size, right);
1976                         VectorScale(up, size, up);
1977                 }
1978                 else
1979                 {
1980                         VectorScale(r_viewleft, -size, right);
1981                         VectorScale(r_viewup, size, up);
1982                 }
1983                 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1984                 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1985                 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1986                 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1987                 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1988                 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1989                 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1990                 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1991                 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1992                 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1993                 particle_vertex3f[10] = org[1] + right[1] - up[1];
1994                 particle_vertex3f[11] = org[2] + right[2] - up[2];
1995                 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1996                 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1997                 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1998                 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1999         }
2000         else if (p->type->orientation == PARTICLE_SPARK)
2001         {
2002                 VectorMA(p->org, -0.02, p->vel, v);
2003                 VectorMA(p->org, 0.02, p->vel, up2);
2004                 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2005                 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2006                 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2007                 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2008                 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2009         }
2010         else if (p->type->orientation == PARTICLE_BEAM)
2011         {
2012                 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2013                 VectorSubtract(p->vel, p->org, up);
2014                 VectorNormalizeFast(up);
2015                 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2016                 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2017                 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2018                 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2019                 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2020                 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2021         }
2022         else
2023                 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2024
2025 #if WORKINGLQUAKE
2026         if (blendmode == PBLEND_ALPHA)
2027                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2028         else if (blendmode == PBLEND_ADD)
2029                 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2030         else //if (blendmode == PBLEND_MOD)
2031                 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2032         glColor4f(cr, cg, cb, ca);
2033         glBegin(GL_QUADS);
2034         glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2035         glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2036         glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2037         glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2038         glEnd();
2039 #else
2040         R_Mesh_Draw(0, 4, 2, polygonelements);
2041 #endif
2042 }
2043
2044 void R_DrawParticles (void)
2045 {
2046         int i;
2047         float minparticledist;
2048         particle_t *p;
2049
2050 #ifdef WORKINGLQUAKE
2051         CL_MoveParticles();
2052 #endif
2053
2054         // LordHavoc: early out conditions
2055         if ((!cl_numparticles) || (!r_drawparticles.integer))
2056                 return;
2057
2058         minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2059
2060 #ifdef WORKINGLQUAKE
2061         glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2062         glEnable(GL_BLEND);
2063         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2064         glDepthMask(0);
2065         // LordHavoc: only render if not too close
2066         for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2067                 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2068                         R_DrawParticle(p);
2069         glDepthMask(1);
2070         glDisable(GL_BLEND);
2071         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2072 #else
2073         // LordHavoc: only render if not too close
2074         for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2075         {
2076                 if (p->type)
2077                 {
2078                         c_particles++;
2079                         if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2080                         {
2081                                 if (p->type == particletype + pt_decal)
2082                                         R_DrawParticleCallback(p, 0);
2083                                 else
2084                                         R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);
2085                         }
2086                 }
2087         }
2088 #endif
2089 }
2090