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_origin, org1, diff);
53 VectorNormalizeFast (diff);
54 CrossProduct (normal, diff, right1);
56 // calculate 'right' vector for end
57 VectorSubtract (r_origin, 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_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
288 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
289 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
290 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
291 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
292 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
293 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
294 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
295 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
297 #ifndef WORKINGLQUAKE
298 static mempool_t *cl_part_mempool;
301 void CL_Particles_Clear(void)
311 void CL_ReadPointFile_f (void);
312 void CL_Particles_Init (void)
316 i = COM_CheckParm ("-particles");
318 if (i && i < com_argc - 1)
320 cl_maxparticles = (int)(atoi(com_argv[i+1]));
321 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
322 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
325 cl_maxparticles = MAX_PARTICLES;
327 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
329 Cvar_RegisterVariable (&cl_particles);
330 Cvar_RegisterVariable (&cl_particles_quality);
331 Cvar_RegisterVariable (&cl_particles_size);
332 Cvar_RegisterVariable (&cl_particles_bloodshowers);
333 Cvar_RegisterVariable (&cl_particles_blood);
334 Cvar_RegisterVariable (&cl_particles_blood_alpha);
335 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
336 Cvar_RegisterVariable (&cl_particles_smoke);
337 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
338 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
339 Cvar_RegisterVariable (&cl_particles_sparks);
340 Cvar_RegisterVariable (&cl_particles_bubbles);
341 Cvar_RegisterVariable (&cl_decals);
342 Cvar_RegisterVariable (&cl_decals_time);
343 Cvar_RegisterVariable (&cl_decals_fadetime);
346 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
347 freeparticles = (void *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t *), "particles");
349 cl_part_mempool = Mem_AllocPool("CL_Part");
350 particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
351 freeparticles = (void *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t *));
356 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)
358 if (cl_numparticles < cl_maxparticles)
361 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
362 ptempcolor = (pcolor1);
363 ptempcolor2 = (pcolor2);
364 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
365 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
366 pcb2 = (ptempcolor2) & 0xFF;
367 if (ptempcolor != ptempcolor2)
369 pcr1 = ((ptempcolor) >> 16) & 0xFF;
370 pcg1 = ((ptempcolor) >> 8) & 0xFF;
371 pcb1 = (ptempcolor) & 0xFF;
372 ptempcolor = rand() & 0xFF;
373 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
374 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
375 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
377 part = &particles[cl_numparticles++];
378 memset(part, 0, sizeof(*part));
379 part->type = (ptype);
380 part->color[0] = pcr2;
381 part->color[1] = pcg2;
382 part->color[2] = pcb2;
383 part->color[3] = 0xFF;
384 part->orientation = porientation;
386 part->blendmode = pblendmode;
387 part->scalex = (pscalex);
388 part->scaley = (pscaley);
389 part->alpha = (palpha);
390 part->alphafade = (palphafade);
391 part->die = cl.time + (ptime);
392 part->gravity = (pgravity);
393 part->bounce = (pbounce);
397 part->vel[0] = (pvx);
398 part->vel[1] = (pvy);
399 part->vel[2] = (pvz);
400 part->time2 = (ptime2);
401 part->vel2[0] = (pvx2);
402 part->vel2[1] = (pvy2);
403 part->vel2[2] = (pvz2);
404 part->friction = (pfriction);
405 part->pressure = (ppressure);
411 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
414 if (!cl_decals.integer)
416 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);
417 #ifndef WORKINGLQUAKE
421 p->ownermodel = p->owner->model;
422 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
423 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
424 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
429 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
432 float bestfrac, bestorg[3], bestnormal[3];
433 float frac, v[3], normal[3], org2[3];
435 void *besthitent = NULL, *hitent;
437 entity_render_t *besthitent = NULL, *hitent;
440 for (i = 0;i < 32;i++)
443 VectorMA(org, maxdist, org2, org2);
444 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
449 VectorCopy(v, bestorg);
450 VectorCopy(normal, bestnormal);
454 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
462 void CL_EntityParticles (entity_t *ent)
466 float sp, sy, cp, cy;
470 static vec3_t avelocities[NUMVERTEXNORMALS];
471 if (!cl_particles.integer) return;
476 if (!avelocities[0][0])
477 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
478 avelocities[0][i] = (rand()&255) * 0.01;
480 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
482 angle = cl.time * avelocities[i][0];
485 angle = cl.time * avelocities[i][1];
494 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);
496 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);
502 void CL_ReadPointFile_f (void)
506 char *pointfile = NULL, *pointfilepos, *t, tchar;
507 char name[MAX_OSPATH];
512 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
513 strlcat (name, ".pts", sizeof (name));
515 pointfile = COM_LoadTempFile (name);
517 pointfile = FS_LoadFile(name, true);
521 Con_Printf ("Could not open %s\n", name);
525 Con_Printf ("Reading %s...\n", name);
528 pointfilepos = pointfile;
529 while (*pointfilepos)
531 while (*pointfilepos == '\n' || *pointfilepos == '\r')
536 while (*t && *t != '\n' && *t != '\r')
540 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
546 VectorCopy(org, leakorg);
549 if (cl_numparticles < cl_maxparticles - 3)
552 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);
555 #ifndef WORKINGLQUAKE
558 VectorCopy(leakorg, org);
559 Con_Printf ("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
561 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);
562 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);
563 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);
568 CL_ParseParticleEffect
570 Parse an effect out of the server message
573 void CL_ParseParticleEffect (void)
576 int i, count, msgcount, color;
578 for (i=0 ; i<3 ; i++)
579 org[i] = MSG_ReadCoord ();
580 for (i=0 ; i<3 ; i++)
581 dir[i] = MSG_ReadChar () * (1.0/16);
582 msgcount = MSG_ReadByte ();
583 color = MSG_ReadByte ();
590 CL_RunParticleEffect (org, dir, color, count);
599 void CL_ParticleExplosion (vec3_t org)
604 if (cl_stainmaps.integer)
605 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
606 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
608 i = CL_PointQ1Contents(org);
609 if ((i == CONTENTS_SLIME || i == CONTENTS_WATER) && cl_particles.integer && cl_particles_bubbles.integer)
611 for (i = 0;i < 128 * cl_particles_quality.value;i++)
612 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);
617 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
619 if (cl_particles.integer && cl_particles_smoke.integer)
621 for (i = 0;i < 64;i++)
624 v2[0] = lhrandom(-64, 64);
625 v2[1] = lhrandom(-64, 64);
626 v2[2] = lhrandom(-8, 24);
628 for (k = 0;k < 16;k++)
630 v[0] = org[0] + lhrandom(-64, 64);
631 v[1] = org[1] + lhrandom(-64, 64);
632 v[2] = org[2] + lhrandom(-8, 24);
633 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
636 VectorSubtract(v2, org, v2);
638 VectorScale(v2, 2.0f, v2);
639 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);
644 if (cl_particles.integer && cl_particles_sparks.integer)
647 for (i = 0;i < 256 * cl_particles_quality.value;i++)
649 k = particlepalette[0x68 + (rand() & 7)];
650 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);
655 if (cl_explosions.integer)
661 CL_ParticleExplosion2
665 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
668 if (!cl_particles.integer) return;
670 for (i = 0;i < 512 * cl_particles_quality.value;i++)
672 k = particlepalette[colorStart + (i % colorLength)];
673 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);
683 void CL_BlobExplosion (vec3_t org)
685 if (cl_stainmaps.integer)
686 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
687 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
689 if (cl_explosions.integer)
699 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
705 CL_ParticleExplosion(org);
708 if (!cl_particles.integer) return;
709 count *= cl_particles_quality.value;
712 k = particlepalette[color + (rand()&7)];
713 if (gamemode == GAME_GOODVSBAD2)
714 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);
716 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);
720 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
726 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
731 if (cl_stainmaps.integer)
732 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
733 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
735 if (!cl_particles.integer) return;
737 if (cl_particles_bulletimpacts.integer)
740 if (cl_particles_smoke.integer)
742 k = count * 0.25 * cl_particles_quality.value;
745 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
746 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
747 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
748 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
749 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);
753 if (cl_particles_sparks.integer)
756 count *= cl_particles_quality.value;
759 k = particlepalette[0x68 + (rand() & 7)];
760 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);
766 void CL_PlasmaBurn (vec3_t org)
768 if (cl_stainmaps.integer)
769 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
770 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
773 static float bloodcount = 0;
774 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
778 // bloodcount is used to accumulate counts too small to cause a blood particle
779 if (!cl_particles.integer) return;
780 if (!cl_particles_blood.integer) return;
787 while(bloodcount > 0)
789 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
790 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
791 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
792 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
793 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);
794 bloodcount -= 16 / cl_particles_quality.value;
798 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
800 vec3_t org, vel, diff, center, velscale;
801 if (!cl_particles.integer) return;
802 if (!cl_particles_bloodshowers.integer) return;
803 if (!cl_particles_blood.integer) return;
805 VectorSubtract(maxs, mins, diff);
806 center[0] = (mins[0] + maxs[0]) * 0.5;
807 center[1] = (mins[1] + maxs[1]) * 0.5;
808 center[2] = (mins[2] + maxs[2]) * 0.5;
809 velscale[0] = velspeed * 2.0 / diff[0];
810 velscale[1] = velspeed * 2.0 / diff[1];
811 velscale[2] = velspeed * 2.0 / diff[2];
813 bloodcount += count * 5.0f;
814 while (bloodcount > 0)
816 org[0] = lhrandom(mins[0], maxs[0]);
817 org[1] = lhrandom(mins[1], maxs[1]);
818 org[2] = lhrandom(mins[2], maxs[2]);
819 vel[0] = (org[0] - center[0]) * velscale[0];
820 vel[1] = (org[1] - center[1]) * velscale[1];
821 vel[2] = (org[2] - center[2]) * velscale[2];
822 bloodcount -= 16 / cl_particles_quality.value;
823 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);
827 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
831 if (!cl_particles.integer) return;
832 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
833 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
834 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
836 count *= cl_particles_quality.value;
839 k = particlepalette[colorbase + (rand()&3)];
840 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);
844 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
847 float t, z, minz, maxz;
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;}
852 if (dir[2] < 0) // falling
854 t = (maxs[2] - mins[2]) / -dir[2];
859 t = (maxs[2] - mins[2]) / dir[2];
862 if (t < 0 || t > 2) // sanity check
865 minz = z - fabs(dir[2]) * 0.1;
866 maxz = z + fabs(dir[2]) * 0.1;
867 minz = bound(mins[2], minz, maxs[2]);
868 maxz = bound(mins[2], maxz, maxs[2]);
870 count *= cl_particles_quality.value;
875 count *= 4; // ick, this should be in the mod or maps?
879 k = particlepalette[colorbase + (rand()&3)];
880 if (gamemode == GAME_GOODVSBAD2)
882 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);
886 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);
893 k = particlepalette[colorbase + (rand()&3)];
894 if (gamemode == GAME_GOODVSBAD2)
896 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);
900 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);
905 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
909 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
914 if (!cl_particles.integer) return;
916 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
917 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
918 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
920 center[0] = (mins[0] + maxs[0]) * 0.5f;
921 center[1] = (mins[1] + maxs[1]) * 0.5f;
922 center[2] = (mins[2] + maxs[2]) * 0.5f;
924 count *= cl_particles_quality.value;
927 k = particlepalette[224 + (rand()&15)];
928 o[0] = lhrandom(mins[0], maxs[0]);
929 o[1] = lhrandom(mins[1], maxs[1]);
930 o[2] = lhrandom(mins[2], maxs[2]);
931 VectorSubtract(o, center, v);
932 VectorNormalizeFast(v);
933 VectorScale(v, 100, v);
934 v[2] += sv_gravity.value * 0.15f;
935 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);
939 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
943 if (!cl_particles.integer) return;
944 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
945 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
946 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
948 count *= cl_particles_quality.value;
951 k = particlepalette[224 + (rand()&15)];
952 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);
954 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);
958 void CL_Flames (vec3_t org, vec3_t vel, int count)
961 if (!cl_particles.integer) return;
963 count *= cl_particles_quality.value;
966 k = particlepalette[224 + (rand()&15)];
967 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);
979 void CL_LavaSplash (vec3_t origin)
981 float i, j, inc, vel;
984 if (!cl_particles.integer) return;
986 inc = 32 / cl_particles_quality.value;
987 for (i = -128;i < 128;i += inc)
989 for (j = -128;j < 128;j += inc)
991 dir[0] = j + lhrandom(0, 8);
992 dir[1] = i + lhrandom(0, 8);
994 org[0] = origin[0] + dir[0];
995 org[1] = origin[1] + dir[1];
996 org[2] = origin[2] + lhrandom(0, 64);
997 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
998 if (gamemode == GAME_GOODVSBAD2)
1000 k = particlepalette[0 + (rand()&255)];
1001 l = particlepalette[0 + (rand()&255)];
1002 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);
1006 k = l = particlepalette[224 + (rand()&7)];
1007 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);
1020 void R_TeleportSplash (vec3_t org)
1023 if (!cl_particles.integer) return;
1025 inc = 8 / cl_particles_quality.value;
1026 for (i = -16;i < 16;i += inc)
1027 for (j = -16;j < 16;j += inc)
1028 for (k = -24;k < 32;k += inc)
1029 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);
1033 #ifdef WORKINGLQUAKE
1034 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1036 void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
1039 vec3_t vec, dir, vel, pos;
1040 float len, dec, speed, qd;
1041 int contents, smoke, blood, bubbles;
1043 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1046 VectorSubtract(end, start, dir);
1047 VectorNormalize(dir);
1049 VectorSubtract (end, start, vec);
1050 #ifdef WORKINGLQUAKE
1051 len = VectorNormalize (vec);
1053 speed = 1.0f / cl.frametime;
1054 VectorSubtract(end, start, vel);
1056 len = VectorNormalizeLength (vec);
1057 dec = -ent->persistent.trail_time;
1058 ent->persistent.trail_time += len;
1059 if (ent->persistent.trail_time < 0.01f)
1062 // if we skip out, leave it reset
1063 ent->persistent.trail_time = 0.0f;
1065 speed = 1.0f / (ent->state_current.time - ent->state_previous.time);
1066 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1068 VectorScale(vel, speed, vel);
1070 // advance into this frame to reach the first puff location
1071 VectorMA(start, dec, vec, pos);
1074 contents = CL_PointQ1Contents(pos);
1075 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1078 smoke = cl_particles.integer && cl_particles_smoke.integer;
1079 blood = cl_particles.integer && cl_particles_blood.integer;
1080 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1081 qd = 1.0f / cl_particles_quality.value;
1087 case 0: // rocket trail
1091 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);
1092 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);
1095 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);
1098 case 1: // grenade trail
1099 // FIXME: make it gradually stop smoking
1102 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);
1107 case 4: // slight blood
1110 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);
1113 case 3: // green tracer
1117 if (gamemode == GAME_GOODVSBAD2)
1118 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);
1120 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);
1124 case 5: // flame tracer
1127 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);
1130 case 6: // voor trail
1134 if (gamemode == GAME_GOODVSBAD2)
1135 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);
1137 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);
1141 case 7: // Nehahra smoke tracer
1144 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);
1146 case 8: // Nexuiz plasma trail
1149 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);
1153 // advance to next time and position
1155 VectorMA (pos, dec, vec, pos);
1157 #ifndef WORKINGLQUAKE
1158 ent->persistent.trail_time = len;
1162 void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1166 if (!cl_particles.integer) return;
1167 if (!cl_particles_smoke.integer) return;
1169 VectorCopy(start, pos);
1170 VectorSubtract(end, start, vec);
1171 #ifdef WORKINGLQUAKE
1172 len = VectorNormalize(vec);
1174 len = VectorNormalizeLength(vec);
1176 color = particlepalette[color];
1177 dec = 3.0f / cl_particles_quality.value;
1180 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);
1182 VectorMA(pos, dec, vec, pos);
1186 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1188 int tempcolor2, cr, cg, cb;
1192 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1193 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);
1196 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1199 if (!cl_particles.integer) return;
1202 if (cl_particles_smoke.integer)
1203 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1204 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);
1207 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1210 if (!cl_particles.integer) return;
1212 if (cl_stainmaps.integer)
1213 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1214 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1217 if (cl_particles_smoke.integer)
1218 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1219 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);
1222 if (cl_particles_sparks.integer)
1223 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1224 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);
1232 void CL_MoveParticles (void)
1235 int i, activeparticles, maxparticle, j, a, pressureused = false, content;
1236 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1237 #ifdef WORKINGLQUAKE
1240 entity_render_t *hitent;
1243 // LordHavoc: early out condition
1244 if (!cl_numparticles)
1247 #ifdef WORKINGLQUAKE
1248 frametime = cl.frametime;
1250 frametime = cl.time - cl.oldtime;
1252 gravity = frametime * sv_gravity.value;
1253 dvel = 1+4*frametime;
1254 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1256 activeparticles = 0;
1259 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1262 VectorCopy(p->org, p->oldorg);
1263 VectorMA(p->org, frametime, p->vel, p->org);
1264 VectorCopy(p->org, org);
1267 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1269 VectorCopy(v, p->org);
1272 // assume it's blood (lame, but...)
1273 #ifndef WORKINGLQUAKE
1274 if (cl_stainmaps.integer)
1275 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));
1277 if (cl_decals.integer)
1280 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1281 // convert from a blood particle to a blood decal
1282 p->texnum = tex_blooddecal[rand()&7];
1283 #ifndef WORKINGLQUAKE
1285 p->ownermodel = hitent->model;
1286 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1287 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1288 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1290 p->time2 = cl.time + cl_decals_time.value;
1291 p->die = p->time2 + cl_decals_fadetime.value;
1293 VectorCopy(normal, p->vel2);
1294 VectorClear(p->vel);
1295 VectorAdd(p->org, normal, p->org);
1305 freeparticles[j++] = p;
1311 dist = DotProduct(p->vel, normal) * -p->bounce;
1312 VectorMA(p->vel, dist, normal, p->vel);
1313 if (DotProduct(p->vel, p->vel) < 0.03)
1314 VectorClear(p->vel);
1318 p->vel[2] -= p->gravity * gravity;
1319 p->alpha -= p->alphafade * frametime;
1322 f = p->friction * frametime;
1324 content = CL_PointQ1Contents(p->org);
1325 if (content != CONTENTS_EMPTY)
1328 VectorScale(p->vel, f, p->vel);
1331 if (p->type != pt_static)
1337 content = CL_PointQ1Contents(p->org);
1339 if (a != CONTENTS_EMPTY)
1341 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1343 p->scalex += frametime * 8;
1344 p->scaley += frametime * 8;
1345 //p->alpha -= bloodwaterfade;
1351 p->vel[2] -= gravity;
1355 content = CL_PointQ1Contents(p->org);
1356 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1363 if (cl.time > p->time2)
1366 p->time2 = cl.time + (rand() & 3) * 0.1;
1367 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1368 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1369 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1372 content = CL_PointQ1Contents(p->org);
1374 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1378 p->scalex += frametime * p->time2;
1379 p->scaley += frametime * p->time2;
1382 #ifndef WORKINGLQUAKE
1383 if (p->owner->model == p->ownermodel)
1385 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1386 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1391 if (cl.time > p->time2)
1393 p->alphafade = p->alpha / (p->die - cl.time);
1394 p->type = pt_decalfade;
1398 #ifndef WORKINGLQUAKE
1399 if (p->owner->model == p->ownermodel)
1401 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1402 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1409 Con_Printf("unknown particle type %i\n", p->type);
1415 // remove dead particles
1416 if (p->alpha < 1 || p->die < cl.time)
1417 freeparticles[j++] = p;
1423 pressureused = true;
1426 // fill in gaps to compact the array
1428 while (maxparticle >= activeparticles)
1430 *freeparticles[i++] = particles[maxparticle--];
1431 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1434 cl_numparticles = activeparticles;
1438 activeparticles = 0;
1439 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1441 freeparticles[activeparticles++] = p;
1443 if (activeparticles)
1445 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1447 for (j = 0;j < activeparticles;j++)
1449 if (freeparticles[j] != p)
1451 float dist, diff[3];
1452 VectorSubtract(p->org, freeparticles[j]->org, diff);
1453 dist = DotProduct(diff, diff);
1454 if (dist < 4096 && dist >= 1)
1456 dist = freeparticles[j]->scalex * 4.0f * frametime / sqrt(dist);
1457 VectorMA(p->vel, dist, diff, p->vel);
1466 #define MAX_PARTICLETEXTURES 64
1467 // particletexture_t is a rectangle in the particlefonttexture
1470 rtexture_t *texture;
1471 float s1, t1, s2, t2;
1476 static int particlefonttexture;
1478 static rtexturepool_t *particletexturepool;
1479 static rtexture_t *particlefonttexture;
1481 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1483 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1485 static qbyte shadebubble(float dx, float dy, vec3_t light)
1489 dz = 1 - (dx*dx+dy*dy);
1490 if (dz > 0) // it does hit the sphere
1494 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1495 VectorNormalize(normal);
1496 dot = DotProduct(normal, light);
1497 if (dot > 0.5) // interior reflection
1498 f += ((dot * 2) - 1);
1499 else if (dot < -0.5) // exterior reflection
1500 f += ((dot * -2) - 1);
1502 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1503 VectorNormalize(normal);
1504 dot = DotProduct(normal, light);
1505 if (dot > 0.5) // interior reflection
1506 f += ((dot * 2) - 1);
1507 else if (dot < -0.5) // exterior reflection
1508 f += ((dot * -2) - 1);
1510 f += 16; // just to give it a haze so you can see the outline
1511 f = bound(0, f, 255);
1518 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1520 int basex, basey, y;
1521 basex = ((texnum >> 0) & 7) * 32;
1522 basey = ((texnum >> 3) & 7) * 32;
1523 particletexture[texnum].s1 = (basex + 1) / 256.0f;
1524 particletexture[texnum].t1 = (basey + 1) / 256.0f;
1525 particletexture[texnum].s2 = (basex + 31) / 256.0f;
1526 particletexture[texnum].t2 = (basey + 31) / 256.0f;
1527 for (y = 0;y < 32;y++)
1528 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 4);
1531 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1534 float cx, cy, dx, dy, f, iradius;
1536 cx = lhrandom(radius + 1, 30 - radius);
1537 cy = lhrandom(radius + 1, 30 - radius);
1538 iradius = 1.0f / radius;
1539 alpha *= (1.0f / 255.0f);
1540 for (y = 0;y < 32;y++)
1542 for (x = 0;x < 32;x++)
1546 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1549 d = data + (y * 32 + x) * 4;
1550 d[0] += f * (red - d[0]);
1551 d[1] += f * (green - d[1]);
1552 d[2] += f * (blue - d[2]);
1558 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1561 for (i = 0;i < 32*32;i++, data += 4)
1563 data[0] = bound(minr, data[0], maxr);
1564 data[1] = bound(ming, data[1], maxg);
1565 data[2] = bound(minb, data[2], maxb);
1569 void particletextureinvert(qbyte *data)
1572 for (i = 0;i < 32*32;i++, data += 4)
1574 data[0] = 255 - data[0];
1575 data[1] = 255 - data[1];
1576 data[2] = 255 - data[2];
1580 static void R_InitParticleTexture (void)
1582 int x, y, d, i, j, k, m;
1583 float dx, dy, radius, f, f2;
1584 qbyte data[32][32][4], noise1[64][64], noise2[64][64], data2[64][16][4];
1586 qbyte particletexturedata[256*256*4];
1588 // a note: decals need to modulate (multiply) the background color to
1589 // properly darken it (stain), and they need to be able to alpha fade,
1590 // this is a very difficult challenge because it means fading to white
1591 // (no change to background) rather than black (darkening everything
1592 // behind the whole decal polygon), and to accomplish this the texture is
1593 // inverted (dark red blood on white background becomes brilliant cyan
1594 // and white on black background) so we can alpha fade it to black, then
1595 // we invert it again during the blendfunc to make it work...
1597 memset(particletexturedata, 255, sizeof(particletexturedata));
1600 for (i = 0;i < 8;i++)
1602 memset(&data[0][0][0], 255, sizeof(data));
1605 fractalnoise(&noise1[0][0], 64, 4);
1606 fractalnoise(&noise2[0][0], 64, 8);
1608 for (y = 0;y < 32;y++)
1611 for (x = 0;x < 32;x++)
1614 d = (noise2[y][x] - 128) * 3 + 192;
1616 d = d * (256 - (int) (dx*dx+dy*dy)) / 256;
1617 d = (d * noise1[y][x]) >> 7;
1618 d = bound(0, d, 255);
1619 data[y][x][3] = (qbyte) d;
1626 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1630 for (i = 0;i < 16;i++)
1632 memset(&data[0][0][0], 255, sizeof(data));
1633 radius = i * 3.0f / 16.0f;
1634 f2 = 255.0f * ((15.0f - i) / 15.0f);
1635 for (y = 0;y < 32;y++)
1637 dy = (y - 16) * 0.25f;
1638 for (x = 0;x < 32;x++)
1640 dx = (x - 16) * 0.25f;
1641 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
1642 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1645 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1649 memset(&data[0][0][0], 255, sizeof(data));
1650 for (y = 0;y < 32;y++)
1653 for (x = 0;x < 32;x++)
1656 d = (256 - (dx*dx+dy*dy));
1657 d = bound(0, d, 255);
1658 data[y][x][3] = (qbyte) d;
1661 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1664 memset(&data[0][0][0], 255, sizeof(data));
1665 light[0] = 1;light[1] = 1;light[2] = 1;
1666 VectorNormalize(light);
1667 for (y = 0;y < 32;y++)
1668 for (x = 0;x < 32;x++)
1669 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);
1670 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1673 memset(&data[0][0][0], 255, sizeof(data));
1674 light[0] = 1;light[1] = 1;light[2] = 1;
1675 VectorNormalize(light);
1676 for (y = 0;y < 32;y++)
1677 for (x = 0;x < 32;x++)
1678 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
1679 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1682 for (i = 0;i < 8;i++)
1684 memset(&data[0][0][0], 255, sizeof(data));
1685 for (k = 0;k < 24;k++)
1686 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 160);
1687 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1688 particletextureinvert(&data[0][0][0]);
1689 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1693 for (i = 0;i < 8;i++)
1695 memset(&data[0][0][0], 255, sizeof(data));
1696 for (k = 0;k < 24;k++)
1697 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 96);
1698 for (j = 3;j < 7;j++)
1699 for (k = 0, m = rand() % 12;k < m;k++)
1700 particletextureblotch(&data[0][0][0], j, 96, 0, 0, 192);
1701 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1702 particletextureinvert(&data[0][0][0]);
1703 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1707 for (i = 0;i < 8;i++)
1709 memset(&data[0][0][0], 255, sizeof(data));
1710 for (k = 0;k < 12;k++)
1711 particletextureblotch(&data[0][0][0], 2, 0, 0, 0, 128);
1712 for (k = 0;k < 3;k++)
1713 particletextureblotch(&data[0][0][0], 14, 0, 0, 0, 160);
1714 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1715 particletextureinvert(&data[0][0][0]);
1716 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1720 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1721 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1722 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1724 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1725 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1726 particletexture[i].texture = particlefonttexture;
1729 fractalnoise(&noise1[0][0], 64, 4);
1731 for (y = 0;y < 64;y++)
1733 for (x = 0;x < 16;x++)
1739 d = d * d * noise1[y][x] / (7 * 7);
1740 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1741 data2[y][x][3] = 255;
1745 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "beam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1746 particletexture[tex_beam].s1 = 0;
1747 particletexture[tex_beam].t1 = 0;
1748 particletexture[tex_beam].s2 = 1;
1749 particletexture[tex_beam].t2 = 1;
1753 static void r_part_start(void)
1755 particletexturepool = R_AllocTexturePool();
1756 R_InitParticleTexture ();
1759 static void r_part_shutdown(void)
1761 R_FreeTexturePool(&particletexturepool);
1764 static void r_part_newmap(void)
1766 cl_numparticles = 0;
1769 void R_Particles_Init (void)
1771 Cvar_RegisterVariable(&r_drawparticles);
1772 #ifdef WORKINGLQUAKE
1775 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1779 #ifdef WORKINGLQUAKE
1780 void R_InitParticles(void)
1782 CL_Particles_Init();
1787 float particle_vertex3f[12], particle_texcoord2f[8];
1789 #ifdef WORKINGLQUAKE
1790 void R_DrawParticle(particle_t *p)
1793 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1795 const particle_t *p = calldata1;
1798 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1799 particletexture_t *tex;
1801 VectorCopy(p->org, org);
1803 tex = &particletexture[p->texnum];
1804 cr = p->color[0] * (1.0f / 255.0f);
1805 cg = p->color[1] * (1.0f / 255.0f);
1806 cb = p->color[2] * (1.0f / 255.0f);
1807 ca = p->alpha * (1.0f / 255.0f);
1808 if (p->blendmode == PBLEND_MOD)
1819 #ifndef WORKINGLQUAKE
1820 if (fogenabled && p->blendmode != PBLEND_MOD)
1822 VectorSubtract(org, r_origin, fogvec);
1823 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1828 if (p->blendmode == 0)
1830 cr += fogcolor[0] * fog;
1831 cg += fogcolor[1] * fog;
1832 cb += fogcolor[2] * fog;
1836 GL_Color(cr, cg, cb, ca);
1838 R_Mesh_Matrix(&r_identitymatrix);
1840 memset(&m, 0, sizeof(m));
1841 m.tex[0] = R_GetTexture(tex->texture);
1842 m.pointer_texcoord[0] = particle_texcoord2f;
1843 R_Mesh_State_Texture(&m);
1845 if (p->blendmode == 0)
1846 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1847 else if (p->blendmode == 1)
1848 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1850 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1851 GL_DepthMask(false);
1853 GL_VertexPointer(particle_vertex3f);
1855 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1857 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1860 if (DotProduct(p->vel2, r_origin) > DotProduct(p->vel2, org))
1862 VectorNegate(p->vel2, v);
1863 VectorVectors(v, right, up);
1866 VectorVectors(p->vel2, right, up);
1867 VectorScale(right, p->scalex, right);
1868 VectorScale(up, p->scaley, up);
1872 VectorScale(vright, p->scalex, right);
1873 VectorScale(vup, p->scaley, up);
1875 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1876 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1877 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1878 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1879 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1880 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1881 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1882 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1883 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1884 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1885 particle_vertex3f[10] = org[1] + right[1] - up[1];
1886 particle_vertex3f[11] = org[2] + right[2] - up[2];
1887 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1888 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1889 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1890 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1892 else if (p->orientation == PARTICLE_SPARK)
1894 VectorMA(p->org, -p->scaley, p->vel, v);
1895 VectorMA(p->org, p->scaley, p->vel, up2);
1896 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1897 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1898 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1899 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1900 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1902 else if (p->orientation == PARTICLE_BEAM)
1904 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1905 VectorSubtract(p->vel2, p->org, up);
1906 VectorNormalizeFast(up);
1907 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1908 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1909 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1910 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1911 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1912 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1915 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1918 if (p->blendmode == 0)
1919 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1920 else if (p->blendmode == 1)
1921 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1923 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1924 glColor4f(cr, cg, cb, ca);
1926 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1927 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1928 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1929 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1932 R_Mesh_Draw(4, 2, polygonelements);
1936 void R_DrawParticles (void)
1939 float minparticledist;
1942 #ifdef WORKINGLQUAKE
1946 // LordHavoc: early out conditions
1947 if ((!cl_numparticles) || (!r_drawparticles.integer))
1950 minparticledist = DotProduct(r_origin, vpn) + 4.0f;
1952 #ifdef WORKINGLQUAKE
1953 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1955 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1957 // LordHavoc: only render if not too close
1958 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1959 if (DotProduct(p->org, vpn) >= minparticledist)
1962 glDisable(GL_BLEND);
1963 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1965 // LordHavoc: only render if not too close
1966 c_particles += cl_numparticles;
1967 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1968 if (DotProduct(p->org, vpn) >= minparticledist || p->orientation == PARTICLE_BEAM)
1969 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);