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