2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
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)
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 #define CL_RocketTrail2 R_RocketTrail2
44 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
46 vec3_t right1, right2, diff, normal;
48 VectorSubtract (org2, org1, normal);
49 VectorNormalizeFast (normal);
51 // calculate 'right' vector for start
52 VectorSubtract (r_vieworigin, org1, diff);
53 VectorNormalizeFast (diff);
54 CrossProduct (normal, diff, right1);
56 // calculate 'right' vector for end
57 VectorSubtract (r_vieworigin, org2, diff);
58 VectorNormalizeFast (diff);
59 CrossProduct (normal, diff, right2);
61 vert[ 0] = org1[0] + width * right1[0];
62 vert[ 1] = org1[1] + width * right1[1];
63 vert[ 2] = org1[2] + width * right1[2];
64 vert[ 3] = org1[0] - width * right1[0];
65 vert[ 4] = org1[1] - width * right1[1];
66 vert[ 5] = org1[2] - width * right1[2];
67 vert[ 6] = org2[0] - width * right2[0];
68 vert[ 7] = org2[1] - width * right2[1];
69 vert[ 8] = org2[2] - width * right2[2];
70 vert[ 9] = org2[0] + width * right2[0];
71 vert[10] = org2[1] + width * right2[1];
72 vert[11] = org2[2] + width * right2[2];
74 void fractalnoise(qbyte *noise, int size, int startgrid)
76 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
78 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
80 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
81 if (size != (1 << sizepower))
82 Sys_Error("fractalnoise: size must be power of 2\n");
84 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
85 if (startgrid != (1 << gridpower))
86 Sys_Error("fractalnoise: grid must be power of 2\n");
88 startgrid = bound(0, startgrid, size);
90 amplitude = 0xFFFF; // this gets halved before use
91 noisebuf = malloc(size*size*sizeof(int));
92 memset(noisebuf, 0, size*size*sizeof(int));
94 for (g2 = startgrid;g2;g2 >>= 1)
96 // brownian motion (at every smaller level there is random behavior)
98 for (y = 0;y < size;y += g2)
99 for (x = 0;x < size;x += g2)
100 n(x,y) += (rand()&litude);
105 // subdivide, diamond-square algorithm (really this has little to do with squares)
107 for (y = 0;y < size;y += g2)
108 for (x = 0;x < size;x += g2)
109 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
111 for (y = 0;y < size;y += g2)
112 for (x = 0;x < size;x += g2)
114 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
115 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
119 // find range of noise values
121 for (y = 0;y < size;y++)
122 for (x = 0;x < size;x++)
124 if (n(x,y) < min) min = n(x,y);
125 if (n(x,y) > max) max = n(x,y);
129 // normalize noise and copy to output
130 for (y = 0;y < size;y++)
131 for (x = 0;x < size;x++)
132 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
136 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
140 right[0] = forward[2];
141 right[1] = -forward[0];
142 right[2] = forward[1];
144 d = DotProduct(forward, right);
145 right[0] -= d * forward[0];
146 right[1] -= d * forward[1];
147 right[2] -= d * forward[2];
148 VectorNormalizeFast(right);
149 CrossProduct(right, forward, up);
153 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
155 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int hitbmodels, void **hitent, int hitsupercontentsmask)
162 memset (&trace, 0, sizeof(trace));
164 VectorCopy (end, trace.endpos);
166 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
168 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
170 VectorCopy(trace.endpos, impact);
171 VectorCopy(trace.plane.normal, normal);
172 return trace.fraction;
175 #include "cl_collision.h"
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
183 pt_static, pt_rain, pt_bubble, pt_blood, pt_grow, pt_decal, pt_decalfade
187 #define PARTICLE_INVALID 0
188 #define PARTICLE_BILLBOARD 1
189 #define PARTICLE_SPARK 2
190 #define PARTICLE_ORIENTED_DOUBLESIDED 3
191 #define PARTICLE_BEAM 4
193 #define PBLEND_ALPHA 0
197 typedef struct particle_s
208 float alpha; // 0-255
209 float alphafade; // how much alpha reduces per second
210 float time2; // used for various things (snow fluttering, for example)
211 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)
212 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
214 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
215 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
216 float pressure; // if non-zero, apply pressure to other particles
218 #ifndef WORKINGLQUAKE
219 entity_render_t *owner; // decal stuck to this entity
220 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
221 vec3_t relativeorigin; // decal at this location in entity's coordinate space
222 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
227 static int particlepalette[256] =
229 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
230 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
231 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
232 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
233 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
234 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
235 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
236 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
237 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
238 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
239 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
240 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
241 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
242 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
243 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
244 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
245 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
246 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
247 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
248 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
249 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
250 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
251 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
252 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
253 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
254 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
255 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
256 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
257 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
258 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
259 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
260 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
263 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
265 // texture numbers in particle font
266 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
267 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
268 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
269 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
270 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
271 static const int tex_particle = 63;
272 static const int tex_bubble = 62;
273 static const int tex_raindrop = 61;
274 static const int tex_beam = 60;
276 static int cl_maxparticles;
277 static int cl_numparticles;
278 static particle_t *particles;
279 static particle_t **freeparticles; // list used only in compacting particles array
281 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
282 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
283 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
284 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
285 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
286 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
287 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
288 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
289 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
290 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
291 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
292 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
293 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
294 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
295 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
296 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
298 #ifndef WORKINGLQUAKE
299 static mempool_t *cl_part_mempool;
302 void CL_Particles_Clear(void)
312 void CL_ReadPointFile_f (void);
313 void CL_Particles_Init (void)
317 i = COM_CheckParm ("-particles");
319 if (i && i < com_argc - 1)
321 cl_maxparticles = (int)(atoi(com_argv[i+1]));
322 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
323 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
326 cl_maxparticles = MAX_PARTICLES;
328 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
330 Cvar_RegisterVariable (&cl_particles);
331 Cvar_RegisterVariable (&cl_particles_quality);
332 Cvar_RegisterVariable (&cl_particles_size);
333 Cvar_RegisterVariable (&cl_particles_bloodshowers);
334 Cvar_RegisterVariable (&cl_particles_blood);
335 Cvar_RegisterVariable (&cl_particles_blood_alpha);
336 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
337 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
338 Cvar_RegisterVariable (&cl_particles_smoke);
339 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
340 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
341 Cvar_RegisterVariable (&cl_particles_sparks);
342 Cvar_RegisterVariable (&cl_particles_bubbles);
343 Cvar_RegisterVariable (&cl_decals);
344 Cvar_RegisterVariable (&cl_decals_time);
345 Cvar_RegisterVariable (&cl_decals_fadetime);
348 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
349 freeparticles = (void *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t *), "particles");
351 cl_part_mempool = Mem_AllocPool("CL_Part");
352 particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
353 freeparticles = (void *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t *));
358 particle_t *particle(int ptype, int porientation, int pcolor1, int pcolor2, int ptex, int plight, int 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)
360 if (cl_numparticles < cl_maxparticles)
363 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
364 ptempcolor = (pcolor1);
365 ptempcolor2 = (pcolor2);
366 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
367 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
368 pcb2 = (ptempcolor2) & 0xFF;
369 if (ptempcolor != ptempcolor2)
371 pcr1 = ((ptempcolor) >> 16) & 0xFF;
372 pcg1 = ((ptempcolor) >> 8) & 0xFF;
373 pcb1 = (ptempcolor) & 0xFF;
374 ptempcolor = rand() & 0xFF;
375 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
376 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
377 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
379 part = &particles[cl_numparticles++];
380 memset(part, 0, sizeof(*part));
381 part->type = (ptype);
382 part->color[0] = pcr2;
383 part->color[1] = pcg2;
384 part->color[2] = pcb2;
385 part->color[3] = 0xFF;
386 part->orientation = porientation;
388 part->blendmode = pblendmode;
389 part->scalex = (pscalex);
390 part->scaley = (pscaley);
391 part->alpha = (palpha);
392 part->alphafade = (palphafade);
393 part->die = cl.time + (ptime);
394 part->gravity = (pgravity);
395 part->bounce = (pbounce);
399 part->vel[0] = (pvx);
400 part->vel[1] = (pvy);
401 part->vel[2] = (pvz);
402 part->time2 = (ptime2);
403 part->vel2[0] = (pvx2);
404 part->vel2[1] = (pvy2);
405 part->vel2[2] = (pvz2);
406 part->friction = (pfriction);
407 part->pressure = (ppressure);
413 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
416 if (!cl_decals.integer)
418 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);
419 #ifndef WORKINGLQUAKE
423 p->ownermodel = p->owner->model;
424 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
425 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
426 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
431 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
434 float bestfrac, bestorg[3], bestnormal[3];
435 float frac, v[3], normal[3], org2[3];
437 void *besthitent = NULL, *hitent;
439 entity_render_t *besthitent = NULL, *hitent;
442 for (i = 0;i < 32;i++)
445 VectorMA(org, maxdist, org2, org2);
446 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
451 VectorCopy(v, bestorg);
452 VectorCopy(normal, bestnormal);
456 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
464 void CL_EntityParticles (entity_t *ent)
468 float sp, sy, cp, cy;
472 static vec3_t avelocities[NUMVERTEXNORMALS];
473 if (!cl_particles.integer) return;
478 if (!avelocities[0][0])
479 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
480 avelocities[0][i] = (rand()&255) * 0.01;
482 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
484 angle = cl.time * avelocities[i][0];
487 angle = cl.time * avelocities[i][1];
496 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);
498 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);
504 void CL_ReadPointFile_f (void)
508 char *pointfile = NULL, *pointfilepos, *t, tchar;
509 char name[MAX_OSPATH];
514 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
515 strlcat (name, ".pts", sizeof (name));
517 pointfile = COM_LoadTempFile (name);
519 pointfile = FS_LoadFile(name, true);
523 Con_Printf ("Could not open %s\n", name);
527 Con_Printf ("Reading %s...\n", name);
530 pointfilepos = pointfile;
531 while (*pointfilepos)
533 while (*pointfilepos == '\n' || *pointfilepos == '\r')
538 while (*t && *t != '\n' && *t != '\r')
542 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
548 VectorCopy(org, leakorg);
551 if (cl_numparticles < cl_maxparticles - 3)
554 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);
557 #ifndef WORKINGLQUAKE
560 VectorCopy(leakorg, org);
561 Con_Printf ("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
563 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);
564 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);
565 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);
570 CL_ParseParticleEffect
572 Parse an effect out of the server message
575 void CL_ParseParticleEffect (void)
578 int i, count, msgcount, color;
580 for (i=0 ; i<3 ; i++)
581 org[i] = MSG_ReadCoord ();
582 for (i=0 ; i<3 ; i++)
583 dir[i] = MSG_ReadChar () * (1.0/16);
584 msgcount = MSG_ReadByte ();
585 color = MSG_ReadByte ();
592 if (cl_particles_blood_bloodhack.integer)
597 CL_BloodPuff(org, dir, count / 2);
603 CL_BloodPuff(org, dir, count / 2);
607 CL_RunParticleEffect (org, dir, color, count);
616 void CL_ParticleExplosion (vec3_t org)
621 if (cl_stainmaps.integer)
622 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
623 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
625 i = CL_PointQ1Contents(org);
626 if ((i == CONTENTS_SLIME || i == CONTENTS_WATER) && cl_particles.integer && cl_particles_bubbles.integer)
628 for (i = 0;i < 128 * cl_particles_quality.value;i++)
629 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);
634 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
636 if (cl_particles.integer && cl_particles_smoke.integer)
638 for (i = 0;i < 64;i++)
641 v2[0] = lhrandom(-64, 64);
642 v2[1] = lhrandom(-64, 64);
643 v2[2] = lhrandom(-8, 24);
645 for (k = 0;k < 16;k++)
647 v[0] = org[0] + lhrandom(-64, 64);
648 v[1] = org[1] + lhrandom(-64, 64);
649 v[2] = org[2] + lhrandom(-8, 24);
650 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
653 VectorSubtract(v2, org, v2);
655 VectorScale(v2, 2.0f, v2);
656 particle(pt_static, PARTICLE_BILLBOARD, 0x101010, 0x202020, tex_smoke[rand()&7], true, PBLEND_ADD, 12, 12, 255, 512, 9999, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0, 0, 0, 0, 0, 0);
661 if (cl_particles.integer && cl_particles_sparks.integer)
664 for (i = 0;i < 256 * cl_particles_quality.value;i++)
666 k = particlepalette[0x68 + (rand() & 7)];
667 particle(pt_static, PARTICLE_SPARK, k, k, 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, 0);
672 if (cl_explosions.integer)
678 CL_ParticleExplosion2
682 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
685 if (!cl_particles.integer) return;
687 for (i = 0;i < 512 * cl_particles_quality.value;i++)
689 k = particlepalette[colorStart + (i % colorLength)];
690 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ALPHA, 1.5, 1.5, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 384, 0.3, 0, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192), 0, 0, 0, 0, 1, 0);
700 void CL_BlobExplosion (vec3_t org)
702 if (cl_stainmaps.integer)
703 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
704 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
706 if (cl_explosions.integer)
716 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
722 CL_ParticleExplosion(org);
725 if (!cl_particles.integer) return;
726 count *= cl_particles_quality.value;
729 k = particlepalette[color + (rand()&7)];
730 if (gamemode == GAME_GOODVSBAD2)
731 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);
733 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);
737 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
743 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
748 if (cl_stainmaps.integer)
749 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
750 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
752 if (!cl_particles.integer) return;
754 if (cl_particles_bulletimpacts.integer)
757 if (cl_particles_smoke.integer)
759 k = count * 0.25 * cl_particles_quality.value;
762 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
763 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
764 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
765 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
766 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.2, 0, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 16), 15, 0, 0, 0, 0, 0);
770 if (cl_particles_sparks.integer)
773 count *= cl_particles_quality.value;
776 k = particlepalette[0x68 + (rand() & 7)];
777 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, 1, 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);
783 void CL_PlasmaBurn (vec3_t org)
785 if (cl_stainmaps.integer)
786 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
787 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
790 static float bloodcount = 0;
791 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
795 // bloodcount is used to accumulate counts too small to cause a blood particle
796 if (!cl_particles.integer) return;
797 if (!cl_particles_blood.integer) return;
804 while(bloodcount > 0)
806 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
807 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
808 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
809 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
810 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);
811 bloodcount -= 16 / cl_particles_quality.value;
815 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
817 vec3_t org, vel, diff, center, velscale;
818 if (!cl_particles.integer) return;
819 if (!cl_particles_bloodshowers.integer) return;
820 if (!cl_particles_blood.integer) return;
822 VectorSubtract(maxs, mins, diff);
823 center[0] = (mins[0] + maxs[0]) * 0.5;
824 center[1] = (mins[1] + maxs[1]) * 0.5;
825 center[2] = (mins[2] + maxs[2]) * 0.5;
826 velscale[0] = velspeed * 2.0 / diff[0];
827 velscale[1] = velspeed * 2.0 / diff[1];
828 velscale[2] = velspeed * 2.0 / diff[2];
830 bloodcount += count * 5.0f;
831 while (bloodcount > 0)
833 org[0] = lhrandom(mins[0], maxs[0]);
834 org[1] = lhrandom(mins[1], maxs[1]);
835 org[2] = lhrandom(mins[2], maxs[2]);
836 vel[0] = (org[0] - center[0]) * velscale[0];
837 vel[1] = (org[1] - center[1]) * velscale[1];
838 vel[2] = (org[2] - center[2]) * velscale[2];
839 bloodcount -= 16 / cl_particles_quality.value;
840 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);
844 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
848 if (!cl_particles.integer) return;
849 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
850 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
851 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
853 count *= cl_particles_quality.value;
856 k = particlepalette[colorbase + (rand()&3)];
857 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);
861 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
864 float t, z, minz, maxz;
865 if (!cl_particles.integer) return;
866 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
867 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
868 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
869 if (dir[2] < 0) // falling
871 t = (maxs[2] - mins[2]) / -dir[2];
876 t = (maxs[2] - mins[2]) / dir[2];
879 if (t < 0 || t > 2) // sanity check
882 minz = z - fabs(dir[2]) * 0.1;
883 maxz = z + fabs(dir[2]) * 0.1;
884 minz = bound(mins[2], minz, maxs[2]);
885 maxz = bound(mins[2], maxz, maxs[2]);
887 count *= cl_particles_quality.value;
892 count *= 4; // ick, this should be in the mod or maps?
896 k = particlepalette[colorbase + (rand()&3)];
897 if (gamemode == GAME_GOODVSBAD2)
899 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);
903 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);
910 k = particlepalette[colorbase + (rand()&3)];
911 if (gamemode == GAME_GOODVSBAD2)
913 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);
917 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);
922 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
926 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
931 if (!cl_particles.integer) return;
933 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
934 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
935 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
937 center[0] = (mins[0] + maxs[0]) * 0.5f;
938 center[1] = (mins[1] + maxs[1]) * 0.5f;
939 center[2] = (mins[2] + maxs[2]) * 0.5f;
941 count *= cl_particles_quality.value;
944 k = particlepalette[224 + (rand()&15)];
945 o[0] = lhrandom(mins[0], maxs[0]);
946 o[1] = lhrandom(mins[1], maxs[1]);
947 o[2] = lhrandom(mins[2], maxs[2]);
948 VectorSubtract(o, center, v);
949 VectorNormalizeFast(v);
950 VectorScale(v, 100, v);
951 v[2] += sv_gravity.value * 0.15f;
952 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, 0);
956 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
960 if (!cl_particles.integer) return;
961 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
962 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
963 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
965 count *= cl_particles_quality.value;
968 k = particlepalette[224 + (rand()&15)];
969 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);
971 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);
975 void CL_Flames (vec3_t org, vec3_t vel, int count)
978 if (!cl_particles.integer) return;
980 count *= cl_particles_quality.value;
983 k = particlepalette[224 + (rand()&15)];
984 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);
996 void CL_LavaSplash (vec3_t origin)
998 float i, j, inc, vel;
1001 if (!cl_particles.integer) return;
1003 inc = 32 / cl_particles_quality.value;
1004 for (i = -128;i < 128;i += inc)
1006 for (j = -128;j < 128;j += inc)
1008 dir[0] = j + lhrandom(0, 8);
1009 dir[1] = i + lhrandom(0, 8);
1011 org[0] = origin[0] + dir[0];
1012 org[1] = origin[1] + dir[1];
1013 org[2] = origin[2] + lhrandom(0, 64);
1014 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1015 if (gamemode == GAME_GOODVSBAD2)
1017 k = particlepalette[0 + (rand()&255)];
1018 l = particlepalette[0 + (rand()&255)];
1019 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);
1023 k = l = particlepalette[224 + (rand()&7)];
1024 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);
1037 void R_TeleportSplash (vec3_t org)
1040 if (!cl_particles.integer) return;
1042 inc = 8 / cl_particles_quality.value;
1043 for (i = -16;i < 16;i += inc)
1044 for (j = -16;j < 16;j += inc)
1045 for (k = -24;k < 32;k += inc)
1046 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);
1050 #ifdef WORKINGLQUAKE
1051 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1053 void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
1056 vec3_t vec, dir, vel, pos;
1057 float len, dec, speed, qd;
1058 int contents, smoke, blood, bubbles;
1060 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1063 VectorSubtract(end, start, dir);
1064 VectorNormalize(dir);
1066 VectorSubtract (end, start, vec);
1067 #ifdef WORKINGLQUAKE
1068 len = VectorNormalize (vec);
1070 speed = 1.0f / cl.frametime;
1071 VectorSubtract(end, start, vel);
1073 len = VectorNormalizeLength (vec);
1074 dec = -ent->persistent.trail_time;
1075 ent->persistent.trail_time += len;
1076 if (ent->persistent.trail_time < 0.01f)
1079 // if we skip out, leave it reset
1080 ent->persistent.trail_time = 0.0f;
1082 speed = 1.0f / (ent->state_current.time - ent->state_previous.time);
1083 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1085 VectorScale(vel, speed, vel);
1087 // advance into this frame to reach the first puff location
1088 VectorMA(start, dec, vec, pos);
1091 contents = CL_PointQ1Contents(pos);
1092 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1095 smoke = cl_particles.integer && cl_particles_smoke.integer;
1096 blood = cl_particles.integer && cl_particles_blood.integer;
1097 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1098 qd = 1.0f / cl_particles_quality.value;
1104 case 0: // rocket trail
1108 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);
1109 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);
1112 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);
1115 case 1: // grenade trail
1116 // FIXME: make it gradually stop smoking
1119 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);
1124 case 4: // slight blood
1127 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);
1130 case 3: // green tracer
1134 if (gamemode == GAME_GOODVSBAD2)
1135 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);
1137 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);
1141 case 5: // flame tracer
1144 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);
1147 case 6: // voor trail
1151 if (gamemode == GAME_GOODVSBAD2)
1152 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);
1154 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);
1158 case 7: // Nehahra smoke tracer
1161 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);
1163 case 8: // Nexuiz plasma trail
1166 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);
1170 // advance to next time and position
1172 VectorMA (pos, dec, vec, pos);
1174 #ifndef WORKINGLQUAKE
1175 ent->persistent.trail_time = len;
1179 void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1183 if (!cl_particles.integer) return;
1184 if (!cl_particles_smoke.integer) return;
1186 VectorCopy(start, pos);
1187 VectorSubtract(end, start, vec);
1188 #ifdef WORKINGLQUAKE
1189 len = VectorNormalize(vec);
1191 len = VectorNormalizeLength(vec);
1193 color = particlepalette[color];
1194 dec = 3.0f / cl_particles_quality.value;
1197 particle(pt_static, PARTICLE_BILLBOARD, color, color, tex_particle, false, PBLEND_ALPHA, 5, 5, 128 / cl_particles_quality.value, 320 / cl_particles_quality.value, 9999, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1199 VectorMA(pos, dec, vec, pos);
1203 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1205 int tempcolor2, cr, cg, cb;
1209 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1210 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);
1213 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1216 if (!cl_particles.integer) return;
1219 if (cl_particles_smoke.integer)
1220 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1221 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);
1224 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1227 if (!cl_particles.integer) return;
1229 if (cl_stainmaps.integer)
1230 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1231 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1234 if (cl_particles_smoke.integer)
1235 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1236 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);
1239 if (cl_particles_sparks.integer)
1240 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1241 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);
1249 void CL_MoveParticles (void)
1252 int i, activeparticles, maxparticle, j, a, pressureused = false, content;
1253 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1254 #ifdef WORKINGLQUAKE
1257 entity_render_t *hitent;
1260 // LordHavoc: early out condition
1261 if (!cl_numparticles)
1264 #ifdef WORKINGLQUAKE
1265 frametime = cl.frametime;
1267 frametime = cl.time - cl.oldtime;
1269 gravity = frametime * sv_gravity.value;
1270 dvel = 1+4*frametime;
1271 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1273 activeparticles = 0;
1276 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1279 VectorCopy(p->org, p->oldorg);
1280 VectorMA(p->org, frametime, p->vel, p->org);
1281 VectorCopy(p->org, org);
1284 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1286 VectorCopy(v, p->org);
1289 // assume it's blood (lame, but...)
1290 #ifndef WORKINGLQUAKE
1291 if (cl_stainmaps.integer)
1292 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));
1294 if (cl_decals.integer)
1297 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1298 // convert from a blood particle to a blood decal
1299 p->texnum = tex_blooddecal[rand()&7];
1300 #ifndef WORKINGLQUAKE
1302 p->ownermodel = hitent->model;
1303 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1304 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1305 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1307 p->time2 = cl.time + cl_decals_time.value;
1308 p->die = p->time2 + cl_decals_fadetime.value;
1310 VectorCopy(normal, p->vel2);
1311 VectorClear(p->vel);
1312 VectorAdd(p->org, normal, p->org);
1322 freeparticles[j++] = p;
1328 dist = DotProduct(p->vel, normal) * -p->bounce;
1329 VectorMA(p->vel, dist, normal, p->vel);
1330 if (DotProduct(p->vel, p->vel) < 0.03)
1331 VectorClear(p->vel);
1335 p->vel[2] -= p->gravity * gravity;
1336 p->alpha -= p->alphafade * frametime;
1339 f = p->friction * frametime;
1341 content = CL_PointQ1Contents(p->org);
1342 if (content != CONTENTS_EMPTY)
1345 VectorScale(p->vel, f, p->vel);
1348 if (p->type != pt_static)
1354 content = CL_PointQ1Contents(p->org);
1356 if (a != CONTENTS_EMPTY)
1358 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1360 p->scalex += frametime * 8;
1361 p->scaley += frametime * 8;
1362 //p->alpha -= bloodwaterfade;
1368 p->vel[2] -= gravity;
1372 content = CL_PointQ1Contents(p->org);
1373 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1380 if (cl.time > p->time2)
1383 p->time2 = cl.time + (rand() & 3) * 0.1;
1384 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1385 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1386 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1389 content = CL_PointQ1Contents(p->org);
1391 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1395 p->scalex += frametime * p->time2;
1396 p->scaley += frametime * p->time2;
1399 #ifndef WORKINGLQUAKE
1400 if (p->owner->model == p->ownermodel)
1402 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1403 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1408 if (cl.time > p->time2)
1410 p->alphafade = p->alpha / (p->die - cl.time);
1411 p->type = pt_decalfade;
1415 #ifndef WORKINGLQUAKE
1416 if (p->owner->model == p->ownermodel)
1418 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1419 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1426 Con_Printf("unknown particle type %i\n", p->type);
1432 // remove dead particles
1433 if (p->alpha < 1 || p->die < cl.time)
1434 freeparticles[j++] = p;
1440 pressureused = true;
1443 // fill in gaps to compact the array
1445 while (maxparticle >= activeparticles)
1447 *freeparticles[i++] = particles[maxparticle--];
1448 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1451 cl_numparticles = activeparticles;
1455 activeparticles = 0;
1456 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1458 freeparticles[activeparticles++] = p;
1460 if (activeparticles)
1462 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1464 for (j = 0;j < activeparticles;j++)
1466 if (freeparticles[j] != p)
1468 float dist, diff[3];
1469 VectorSubtract(p->org, freeparticles[j]->org, diff);
1470 dist = DotProduct(diff, diff);
1471 if (dist < 4096 && dist >= 1)
1473 dist = freeparticles[j]->scalex * 4.0f * frametime / sqrt(dist);
1474 VectorMA(p->vel, dist, diff, p->vel);
1483 #define MAX_PARTICLETEXTURES 64
1484 // particletexture_t is a rectangle in the particlefonttexture
1487 rtexture_t *texture;
1488 float s1, t1, s2, t2;
1493 static int particlefonttexture;
1495 static rtexturepool_t *particletexturepool;
1496 static rtexture_t *particlefonttexture;
1498 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1500 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1502 static qbyte shadebubble(float dx, float dy, vec3_t light)
1506 dz = 1 - (dx*dx+dy*dy);
1507 if (dz > 0) // it does hit the sphere
1511 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1512 VectorNormalize(normal);
1513 dot = DotProduct(normal, light);
1514 if (dot > 0.5) // interior reflection
1515 f += ((dot * 2) - 1);
1516 else if (dot < -0.5) // exterior reflection
1517 f += ((dot * -2) - 1);
1519 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1520 VectorNormalize(normal);
1521 dot = DotProduct(normal, light);
1522 if (dot > 0.5) // interior reflection
1523 f += ((dot * 2) - 1);
1524 else if (dot < -0.5) // exterior reflection
1525 f += ((dot * -2) - 1);
1527 f += 16; // just to give it a haze so you can see the outline
1528 f = bound(0, f, 255);
1535 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1537 int basex, basey, y;
1538 basex = ((texnum >> 0) & 7) * 32;
1539 basey = ((texnum >> 3) & 7) * 32;
1540 particletexture[texnum].s1 = (basex + 1) / 256.0f;
1541 particletexture[texnum].t1 = (basey + 1) / 256.0f;
1542 particletexture[texnum].s2 = (basex + 31) / 256.0f;
1543 particletexture[texnum].t2 = (basey + 31) / 256.0f;
1544 for (y = 0;y < 32;y++)
1545 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 4);
1548 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1551 float cx, cy, dx, dy, f, iradius;
1553 cx = lhrandom(radius + 1, 30 - radius);
1554 cy = lhrandom(radius + 1, 30 - radius);
1555 iradius = 1.0f / radius;
1556 alpha *= (1.0f / 255.0f);
1557 for (y = 0;y < 32;y++)
1559 for (x = 0;x < 32;x++)
1563 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1566 d = data + (y * 32 + x) * 4;
1567 d[0] += f * (red - d[0]);
1568 d[1] += f * (green - d[1]);
1569 d[2] += f * (blue - d[2]);
1575 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1578 for (i = 0;i < 32*32;i++, data += 4)
1580 data[0] = bound(minr, data[0], maxr);
1581 data[1] = bound(ming, data[1], maxg);
1582 data[2] = bound(minb, data[2], maxb);
1586 void particletextureinvert(qbyte *data)
1589 for (i = 0;i < 32*32;i++, data += 4)
1591 data[0] = 255 - data[0];
1592 data[1] = 255 - data[1];
1593 data[2] = 255 - data[2];
1597 static void R_InitParticleTexture (void)
1599 int x, y, d, i, j, k, m;
1600 float dx, dy, radius, f, f2;
1601 qbyte data[32][32][4], noise1[64][64], noise2[64][64], data2[64][16][4];
1603 qbyte particletexturedata[256*256*4];
1605 // a note: decals need to modulate (multiply) the background color to
1606 // properly darken it (stain), and they need to be able to alpha fade,
1607 // this is a very difficult challenge because it means fading to white
1608 // (no change to background) rather than black (darkening everything
1609 // behind the whole decal polygon), and to accomplish this the texture is
1610 // inverted (dark red blood on white background becomes brilliant cyan
1611 // and white on black background) so we can alpha fade it to black, then
1612 // we invert it again during the blendfunc to make it work...
1614 memset(particletexturedata, 255, sizeof(particletexturedata));
1617 for (i = 0;i < 8;i++)
1619 memset(&data[0][0][0], 255, sizeof(data));
1622 fractalnoise(&noise1[0][0], 64, 4);
1623 fractalnoise(&noise2[0][0], 64, 8);
1625 for (y = 0;y < 32;y++)
1628 for (x = 0;x < 32;x++)
1631 d = (noise2[y][x] - 128) * 3 + 192;
1633 d = d * (256 - (int) (dx*dx+dy*dy)) / 256;
1634 d = (d * noise1[y][x]) >> 7;
1635 d = bound(0, d, 255);
1636 data[y][x][3] = (qbyte) d;
1643 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1647 for (i = 0;i < 16;i++)
1649 memset(&data[0][0][0], 255, sizeof(data));
1650 radius = i * 3.0f / 16.0f;
1651 f2 = 255.0f * ((15.0f - i) / 15.0f);
1652 for (y = 0;y < 32;y++)
1654 dy = (y - 16) * 0.25f;
1655 for (x = 0;x < 32;x++)
1657 dx = (x - 16) * 0.25f;
1658 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
1659 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1662 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1666 memset(&data[0][0][0], 255, sizeof(data));
1667 for (y = 0;y < 32;y++)
1670 for (x = 0;x < 32;x++)
1673 d = (256 - (dx*dx+dy*dy));
1674 d = bound(0, d, 255);
1675 data[y][x][3] = (qbyte) d;
1678 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1681 memset(&data[0][0][0], 255, sizeof(data));
1682 light[0] = 1;light[1] = 1;light[2] = 1;
1683 VectorNormalize(light);
1684 for (y = 0;y < 32;y++)
1685 for (x = 0;x < 32;x++)
1686 data[y][x][3] = shadebubble((x - 16) * (1.0 / 8.0), y < 24 ? (y - 24) * (1.0 / 24.0) : (y - 24) * (1.0 / 8.0), light);
1687 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1690 memset(&data[0][0][0], 255, sizeof(data));
1691 light[0] = 1;light[1] = 1;light[2] = 1;
1692 VectorNormalize(light);
1693 for (y = 0;y < 32;y++)
1694 for (x = 0;x < 32;x++)
1695 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
1696 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1699 for (i = 0;i < 8;i++)
1701 memset(&data[0][0][0], 255, sizeof(data));
1702 for (k = 0;k < 24;k++)
1703 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 160);
1704 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1705 particletextureinvert(&data[0][0][0]);
1706 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1710 for (i = 0;i < 8;i++)
1712 memset(&data[0][0][0], 255, sizeof(data));
1713 for (k = 0;k < 24;k++)
1714 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 96);
1715 for (j = 3;j < 7;j++)
1716 for (k = 0, m = rand() % 12;k < m;k++)
1717 particletextureblotch(&data[0][0][0], j, 96, 0, 0, 192);
1718 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1719 particletextureinvert(&data[0][0][0]);
1720 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1724 for (i = 0;i < 8;i++)
1726 memset(&data[0][0][0], 255, sizeof(data));
1727 for (k = 0;k < 12;k++)
1728 particletextureblotch(&data[0][0][0], 2, 0, 0, 0, 128);
1729 for (k = 0;k < 3;k++)
1730 particletextureblotch(&data[0][0][0], 14, 0, 0, 0, 160);
1731 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1732 particletextureinvert(&data[0][0][0]);
1733 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1737 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1738 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1739 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1741 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1742 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1743 particletexture[i].texture = particlefonttexture;
1746 fractalnoise(&noise1[0][0], 64, 4);
1748 for (y = 0;y < 64;y++)
1750 for (x = 0;x < 16;x++)
1756 d = d * d * noise1[y][x] / (7 * 7);
1757 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1758 data2[y][x][3] = 255;
1762 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "beam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1763 particletexture[tex_beam].s1 = 0;
1764 particletexture[tex_beam].t1 = 0;
1765 particletexture[tex_beam].s2 = 1;
1766 particletexture[tex_beam].t2 = 1;
1770 static void r_part_start(void)
1772 particletexturepool = R_AllocTexturePool();
1773 R_InitParticleTexture ();
1776 static void r_part_shutdown(void)
1778 R_FreeTexturePool(&particletexturepool);
1781 static void r_part_newmap(void)
1783 cl_numparticles = 0;
1786 void R_Particles_Init (void)
1788 Cvar_RegisterVariable(&r_drawparticles);
1789 #ifdef WORKINGLQUAKE
1792 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1796 #ifdef WORKINGLQUAKE
1797 void R_InitParticles(void)
1799 CL_Particles_Init();
1804 float particle_vertex3f[12], particle_texcoord2f[8];
1806 #ifdef WORKINGLQUAKE
1807 void R_DrawParticle(particle_t *p)
1810 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1812 const particle_t *p = calldata1;
1815 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1816 particletexture_t *tex;
1818 VectorCopy(p->org, org);
1820 tex = &particletexture[p->texnum];
1821 cr = p->color[0] * (1.0f / 255.0f);
1822 cg = p->color[1] * (1.0f / 255.0f);
1823 cb = p->color[2] * (1.0f / 255.0f);
1824 ca = p->alpha * (1.0f / 255.0f);
1825 if (p->blendmode == PBLEND_MOD)
1836 #ifndef WORKINGLQUAKE
1837 if (fogenabled && p->blendmode != PBLEND_MOD)
1839 VectorSubtract(org, r_vieworigin, fogvec);
1840 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1845 if (p->blendmode == 0)
1847 cr += fogcolor[0] * fog;
1848 cg += fogcolor[1] * fog;
1849 cb += fogcolor[2] * fog;
1853 GL_Color(cr, cg, cb, ca);
1855 R_Mesh_Matrix(&r_identitymatrix);
1857 memset(&m, 0, sizeof(m));
1858 m.tex[0] = R_GetTexture(tex->texture);
1859 m.pointer_texcoord[0] = particle_texcoord2f;
1860 R_Mesh_State_Texture(&m);
1862 if (p->blendmode == 0)
1863 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1864 else if (p->blendmode == 1)
1865 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1867 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1868 GL_DepthMask(false);
1870 GL_VertexPointer(particle_vertex3f);
1872 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1874 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1877 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1879 VectorNegate(p->vel2, v);
1880 VectorVectors(v, right, up);
1883 VectorVectors(p->vel2, right, up);
1884 VectorScale(right, p->scalex, right);
1885 VectorScale(up, p->scaley, up);
1889 VectorScale(r_viewleft, -p->scalex, right);
1890 VectorScale(r_viewup, p->scaley, up);
1892 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1893 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1894 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1895 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1896 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1897 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1898 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1899 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1900 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1901 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1902 particle_vertex3f[10] = org[1] + right[1] - up[1];
1903 particle_vertex3f[11] = org[2] + right[2] - up[2];
1904 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1905 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1906 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1907 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1909 else if (p->orientation == PARTICLE_SPARK)
1911 VectorMA(p->org, -p->scaley, p->vel, v);
1912 VectorMA(p->org, p->scaley, p->vel, up2);
1913 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1914 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1915 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1916 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1917 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1919 else if (p->orientation == PARTICLE_BEAM)
1921 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1922 VectorSubtract(p->vel2, p->org, up);
1923 VectorNormalizeFast(up);
1924 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1925 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1926 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1927 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1928 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1929 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1932 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1935 if (p->blendmode == 0)
1936 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1937 else if (p->blendmode == 1)
1938 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1940 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1941 glColor4f(cr, cg, cb, ca);
1943 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1944 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1945 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1946 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1949 R_Mesh_Draw(4, 2, polygonelements);
1953 void R_DrawParticles (void)
1956 float minparticledist;
1959 #ifdef WORKINGLQUAKE
1963 // LordHavoc: early out conditions
1964 if ((!cl_numparticles) || (!r_drawparticles.integer))
1967 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1969 #ifdef WORKINGLQUAKE
1970 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1972 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1974 // LordHavoc: only render if not too close
1975 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1976 if (DotProduct(p->org, r_viewforward) >= minparticledist)
1979 glDisable(GL_BLEND);
1980 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1982 // LordHavoc: only render if not too close
1983 c_particles += cl_numparticles;
1984 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1985 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
1986 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);