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