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_PointContents(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 contents, int hitbmodels, void **hitent)
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];
438 entity_render_t *hitent;
441 for (i = 0;i < 32;i++)
444 VectorMA(org, maxdist, org2, org2);
445 frac = CL_TraceLine(org, org2, v, normal, 0, true, &hitent);
450 VectorCopy(v, bestorg);
451 VectorCopy(normal, bestnormal);
455 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
463 void CL_EntityParticles (entity_t *ent)
467 float sp, sy, cp, cy;
471 static vec3_t avelocities[NUMVERTEXNORMALS];
472 if (!cl_particles.integer) return;
477 if (!avelocities[0][0])
478 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
479 avelocities[0][i] = (rand()&255) * 0.01;
481 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
483 angle = cl.time * avelocities[i][0];
486 angle = cl.time * avelocities[i][1];
495 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);
497 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);
503 void CL_ReadPointFile_f (void)
507 char *pointfile = NULL, *pointfilepos, *t, tchar;
508 char name[MAX_OSPATH];
513 FS_StripExtension(cl.worldmodel->name, name);
514 strcat(name, ".pts");
516 pointfile = COM_LoadTempFile (name);
518 pointfile = FS_LoadFile(name, true);
522 Con_Printf ("Could not open %s\n", name);
526 Con_Printf ("Reading %s...\n", name);
529 pointfilepos = pointfile;
530 while (*pointfilepos)
532 while (*pointfilepos == '\n' || *pointfilepos == '\r')
537 while (*t && *t != '\n' && *t != '\r')
541 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
547 VectorCopy(org, leakorg);
550 if (cl_numparticles < cl_maxparticles - 3)
553 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);
556 #ifndef WORKINGLQUAKE
559 VectorCopy(leakorg, org);
560 Con_Printf ("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
562 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);
563 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);
564 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);
569 CL_ParseParticleEffect
571 Parse an effect out of the server message
574 void CL_ParseParticleEffect (void)
577 int i, count, msgcount, color;
579 for (i=0 ; i<3 ; i++)
580 org[i] = MSG_ReadCoord ();
581 for (i=0 ; i<3 ; i++)
582 dir[i] = MSG_ReadChar () * (1.0/16);
583 msgcount = MSG_ReadByte ();
584 color = MSG_ReadByte ();
591 CL_RunParticleEffect (org, dir, color, count);
600 void CL_ParticleExplosion (vec3_t org)
605 if (cl_stainmaps.integer)
606 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
607 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
609 i = CL_PointContents(org);
610 if ((i == CONTENTS_SLIME || i == CONTENTS_WATER) && cl_particles.integer && cl_particles_bubbles.integer)
612 for (i = 0;i < 128 * cl_particles_quality.value;i++)
613 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);
618 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
620 if (cl_particles.integer && cl_particles_smoke.integer)
622 for (i = 0;i < 64;i++)
625 v2[0] = lhrandom(-64, 64);
626 v2[1] = lhrandom(-64, 64);
627 v2[2] = lhrandom(-8, 24);
629 for (k = 0;k < 16;k++)
631 v[0] = org[0] + lhrandom(-64, 64);
632 v[1] = org[1] + lhrandom(-64, 64);
633 v[2] = org[2] + lhrandom(-8, 24);
634 if (CL_TraceLine(org, v, v2, NULL, 0, true, NULL) >= 0.1)
637 VectorSubtract(v2, org, v2);
639 VectorScale(v2, 2.0f, v2);
640 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);
645 if (cl_particles.integer && cl_particles_sparks.integer)
648 for (i = 0;i < 256 * cl_particles_quality.value;i++)
650 k = particlepalette[0x68 + (rand() & 7)];
651 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);
656 if (cl_explosions.integer)
662 CL_ParticleExplosion2
666 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
669 if (!cl_particles.integer) return;
671 for (i = 0;i < 512 * cl_particles_quality.value;i++)
673 k = particlepalette[colorStart + (i % colorLength)];
674 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);
684 void CL_BlobExplosion (vec3_t org)
686 if (cl_stainmaps.integer)
687 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
688 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
690 if (cl_explosions.integer)
700 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
706 CL_ParticleExplosion(org);
709 if (!cl_particles.integer) return;
710 count *= cl_particles_quality.value;
713 k = particlepalette[color + (rand()&7)];
714 if (gamemode == GAME_GOODVSBAD2)
715 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);
717 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);
721 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
727 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
732 if (cl_stainmaps.integer)
733 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
734 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
736 if (!cl_particles.integer) return;
738 if (cl_particles_bulletimpacts.integer)
741 if (cl_particles_smoke.integer)
743 k = count * 0.25 * cl_particles_quality.value;
746 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
747 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
748 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
749 CL_TraceLine(org, org2, org3, NULL, 0, true, NULL);
750 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);
754 if (cl_particles_sparks.integer)
757 count *= cl_particles_quality.value;
760 k = particlepalette[0x68 + (rand() & 7)];
761 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);
767 void CL_PlasmaBurn (vec3_t org)
769 if (cl_stainmaps.integer)
770 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
771 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
774 static float bloodcount = 0;
775 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
779 // bloodcount is used to accumulate counts too small to cause a blood particle
780 if (!cl_particles.integer) return;
781 if (!cl_particles_blood.integer) return;
788 while(bloodcount > 0)
790 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
791 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
792 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
793 CL_TraceLine(org, org2, org3, NULL, 0, true, NULL);
794 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);
795 bloodcount -= 16 / cl_particles_quality.value;
799 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
801 vec3_t org, vel, diff, center, velscale;
802 if (!cl_particles.integer) return;
803 if (!cl_particles_bloodshowers.integer) return;
804 if (!cl_particles_blood.integer) return;
806 VectorSubtract(maxs, mins, diff);
807 center[0] = (mins[0] + maxs[0]) * 0.5;
808 center[1] = (mins[1] + maxs[1]) * 0.5;
809 center[2] = (mins[2] + maxs[2]) * 0.5;
810 velscale[0] = velspeed * 2.0 / diff[0];
811 velscale[1] = velspeed * 2.0 / diff[1];
812 velscale[2] = velspeed * 2.0 / diff[2];
814 bloodcount += count * 5.0f;
815 while (bloodcount > 0)
817 org[0] = lhrandom(mins[0], maxs[0]);
818 org[1] = lhrandom(mins[1], maxs[1]);
819 org[2] = lhrandom(mins[2], maxs[2]);
820 vel[0] = (org[0] - center[0]) * velscale[0];
821 vel[1] = (org[1] - center[1]) * velscale[1];
822 vel[2] = (org[2] - center[2]) * velscale[2];
823 bloodcount -= 16 / cl_particles_quality.value;
824 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);
828 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
832 if (!cl_particles.integer) return;
833 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
834 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
835 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
837 count *= cl_particles_quality.value;
840 k = particlepalette[colorbase + (rand()&3)];
841 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);
845 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
848 float t, z, minz, maxz;
849 if (!cl_particles.integer) return;
850 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
851 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
852 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
853 if (dir[2] < 0) // falling
855 t = (maxs[2] - mins[2]) / -dir[2];
860 t = (maxs[2] - mins[2]) / dir[2];
863 if (t < 0 || t > 2) // sanity check
866 minz = z - fabs(dir[2]) * 0.1;
867 maxz = z + fabs(dir[2]) * 0.1;
868 minz = bound(mins[2], minz, maxs[2]);
869 maxz = bound(mins[2], maxz, maxs[2]);
871 count *= cl_particles_quality.value;
876 count *= 4; // ick, this should be in the mod or maps?
880 k = particlepalette[colorbase + (rand()&3)];
881 if (gamemode == GAME_GOODVSBAD2)
883 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);
887 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);
894 k = particlepalette[colorbase + (rand()&3)];
895 if (gamemode == GAME_GOODVSBAD2)
897 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);
901 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);
906 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
910 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
915 if (!cl_particles.integer) return;
917 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
918 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
919 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
921 center[0] = (mins[0] + maxs[0]) * 0.5f;
922 center[1] = (mins[1] + maxs[1]) * 0.5f;
923 center[2] = (mins[2] + maxs[2]) * 0.5f;
925 count *= cl_particles_quality.value;
928 k = particlepalette[224 + (rand()&15)];
929 o[0] = lhrandom(mins[0], maxs[0]);
930 o[1] = lhrandom(mins[1], maxs[1]);
931 o[2] = lhrandom(mins[2], maxs[2]);
932 VectorSubtract(o, center, v);
933 VectorNormalizeFast(v);
934 VectorScale(v, 100, v);
935 v[2] += sv_gravity.value * 0.15f;
936 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);
940 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
944 if (!cl_particles.integer) return;
945 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
946 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
947 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
949 count *= cl_particles_quality.value;
952 k = particlepalette[224 + (rand()&15)];
953 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);
955 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);
959 void CL_Flames (vec3_t org, vec3_t vel, int count)
962 if (!cl_particles.integer) return;
964 count *= cl_particles_quality.value;
967 k = particlepalette[224 + (rand()&15)];
968 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);
980 void CL_LavaSplash (vec3_t origin)
982 float i, j, inc, vel;
985 if (!cl_particles.integer) return;
987 inc = 32 / cl_particles_quality.value;
988 for (i = -128;i < 128;i += inc)
990 for (j = -128;j < 128;j += inc)
992 dir[0] = j + lhrandom(0, 8);
993 dir[1] = i + lhrandom(0, 8);
995 org[0] = origin[0] + dir[0];
996 org[1] = origin[1] + dir[1];
997 org[2] = origin[2] + lhrandom(0, 64);
998 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
999 if (gamemode == GAME_GOODVSBAD2)
1001 k = particlepalette[0 + (rand()&255)];
1002 l = particlepalette[0 + (rand()&255)];
1003 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);
1007 k = l = particlepalette[224 + (rand()&7)];
1008 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);
1021 void R_TeleportSplash (vec3_t org)
1024 if (!cl_particles.integer) return;
1026 inc = 8 / cl_particles_quality.value;
1027 for (i = -16;i < 16;i += inc)
1028 for (j = -16;j < 16;j += inc)
1029 for (k = -24;k < 32;k += inc)
1030 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);
1034 #ifdef WORKINGLQUAKE
1035 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1037 void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
1040 vec3_t vec, dir, vel, pos;
1041 float len, dec, speed, qd;
1042 int contents, smoke, blood, bubbles;
1044 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1047 VectorSubtract(end, start, dir);
1048 VectorNormalize(dir);
1050 VectorSubtract (end, start, vec);
1051 #ifdef WORKINGLQUAKE
1052 len = VectorNormalize (vec);
1054 speed = 1.0f / cl.frametime;
1055 VectorSubtract(end, start, vel);
1057 len = VectorNormalizeLength (vec);
1058 dec = -ent->persistent.trail_time;
1059 ent->persistent.trail_time += len;
1060 if (ent->persistent.trail_time < 0.01f)
1063 // if we skip out, leave it reset
1064 ent->persistent.trail_time = 0.0f;
1066 speed = 1.0f / (ent->state_current.time - ent->state_previous.time);
1067 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1069 VectorScale(vel, speed, vel);
1071 // advance into this frame to reach the first puff location
1072 VectorMA(start, dec, vec, pos);
1075 contents = CL_PointContents(pos);
1076 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1079 smoke = cl_particles.integer && cl_particles_smoke.integer;
1080 blood = cl_particles.integer && cl_particles_blood.integer;
1081 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1082 qd = 1.0f / cl_particles_quality.value;
1088 case 0: // rocket trail
1092 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);
1093 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);
1096 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);
1099 case 1: // grenade trail
1100 // FIXME: make it gradually stop smoking
1103 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);
1108 case 4: // slight blood
1111 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);
1114 case 3: // green tracer
1118 if (gamemode == GAME_GOODVSBAD2)
1119 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);
1121 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);
1125 case 5: // flame tracer
1128 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);
1131 case 6: // voor trail
1135 if (gamemode == GAME_GOODVSBAD2)
1136 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);
1138 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);
1142 case 7: // Nehahra smoke tracer
1145 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);
1147 case 8: // Nexuiz plasma trail
1150 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);
1154 // advance to next time and position
1156 VectorMA (pos, dec, vec, pos);
1158 #ifndef WORKINGLQUAKE
1159 ent->persistent.trail_time = len;
1163 void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1167 if (!cl_particles.integer) return;
1168 if (!cl_particles_smoke.integer) return;
1170 VectorCopy(start, pos);
1171 VectorSubtract(end, start, vec);
1172 #ifdef WORKINGLQUAKE
1173 len = VectorNormalize(vec);
1175 len = VectorNormalizeLength(vec);
1177 color = particlepalette[color];
1178 dec = 3.0f / cl_particles_quality.value;
1181 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);
1183 VectorMA(pos, dec, vec, pos);
1187 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1189 int tempcolor2, cr, cg, cb;
1193 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1194 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);
1197 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1200 if (!cl_particles.integer) return;
1203 if (cl_particles_smoke.integer)
1204 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1205 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);
1208 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1211 if (!cl_particles.integer) return;
1213 if (cl_stainmaps.integer)
1214 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1215 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1218 if (cl_particles_smoke.integer)
1219 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1220 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);
1223 if (cl_particles_sparks.integer)
1224 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1225 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);
1233 void CL_MoveParticles (void)
1236 int i, activeparticles, maxparticle, j, a, pressureused = false, content;
1237 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1238 #ifdef WORKINGLQUAKE
1241 entity_render_t *hitent;
1244 // LordHavoc: early out condition
1245 if (!cl_numparticles)
1248 #ifdef WORKINGLQUAKE
1249 frametime = cl.frametime;
1251 frametime = cl.time - cl.oldtime;
1253 gravity = frametime * sv_gravity.value;
1254 dvel = 1+4*frametime;
1255 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1257 activeparticles = 0;
1260 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1263 VectorCopy(p->org, p->oldorg);
1264 VectorMA(p->org, frametime, p->vel, p->org);
1265 VectorCopy(p->org, org);
1268 if (CL_TraceLine(p->oldorg, p->org, v, normal, 0, true, &hitent) < 1)
1270 VectorCopy(v, p->org);
1273 // assume it's blood (lame, but...)
1274 #ifndef WORKINGLQUAKE
1275 if (cl_stainmaps.integer)
1276 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));
1278 if (cl_decals.integer)
1281 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1282 // convert from a blood particle to a blood decal
1283 p->texnum = tex_blooddecal[rand()&7];
1284 #ifndef WORKINGLQUAKE
1286 p->ownermodel = hitent->model;
1287 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1288 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1289 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1291 p->time2 = cl.time + cl_decals_time.value;
1292 p->die = p->time2 + cl_decals_fadetime.value;
1294 VectorCopy(normal, p->vel2);
1295 VectorClear(p->vel);
1296 VectorAdd(p->org, normal, p->org);
1306 freeparticles[j++] = p;
1312 dist = DotProduct(p->vel, normal) * -p->bounce;
1313 VectorMA(p->vel, dist, normal, p->vel);
1314 if (DotProduct(p->vel, p->vel) < 0.03)
1315 VectorClear(p->vel);
1319 p->vel[2] -= p->gravity * gravity;
1320 p->alpha -= p->alphafade * frametime;
1323 f = p->friction * frametime;
1325 content = CL_PointContents(p->org);
1326 if (content != CONTENTS_EMPTY)
1329 VectorScale(p->vel, f, p->vel);
1332 if (p->type != pt_static)
1338 content = CL_PointContents(p->org);
1340 if (a != CONTENTS_EMPTY)
1342 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1344 p->scalex += frametime * 8;
1345 p->scaley += frametime * 8;
1346 //p->alpha -= bloodwaterfade;
1352 p->vel[2] -= gravity;
1356 content = CL_PointContents(p->org);
1357 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1364 if (cl.time > p->time2)
1367 p->time2 = cl.time + (rand() & 3) * 0.1;
1368 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1369 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1370 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1373 content = CL_PointContents(p->org);
1375 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1379 p->scalex += frametime * p->time2;
1380 p->scaley += frametime * p->time2;
1383 #ifndef WORKINGLQUAKE
1384 if (p->owner->model == p->ownermodel)
1386 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1387 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1392 if (cl.time > p->time2)
1394 p->alphafade = p->alpha / (p->die - cl.time);
1395 p->type = pt_decalfade;
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);
1410 Con_Printf("unknown particle type %i\n", p->type);
1416 // remove dead particles
1417 if (p->alpha < 1 || p->die < cl.time)
1418 freeparticles[j++] = p;
1424 pressureused = true;
1427 // fill in gaps to compact the array
1429 while (maxparticle >= activeparticles)
1431 *freeparticles[i++] = particles[maxparticle--];
1432 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1435 cl_numparticles = activeparticles;
1439 activeparticles = 0;
1440 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1442 freeparticles[activeparticles++] = p;
1444 if (activeparticles)
1446 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1448 for (j = 0;j < activeparticles;j++)
1450 if (freeparticles[j] != p)
1452 float dist, diff[3];
1453 VectorSubtract(p->org, freeparticles[j]->org, diff);
1454 dist = DotProduct(diff, diff);
1455 if (dist < 4096 && dist >= 1)
1457 dist = freeparticles[j]->scalex * 4.0f * frametime / sqrt(dist);
1458 VectorMA(p->vel, dist, diff, p->vel);
1467 #define MAX_PARTICLETEXTURES 64
1468 // particletexture_t is a rectangle in the particlefonttexture
1471 rtexture_t *texture;
1472 float s1, t1, s2, t2;
1477 static int particlefonttexture;
1479 static rtexturepool_t *particletexturepool;
1480 static rtexture_t *particlefonttexture;
1482 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1484 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1486 static qbyte shadebubble(float dx, float dy, vec3_t light)
1490 dz = 1 - (dx*dx+dy*dy);
1491 if (dz > 0) // it does hit the sphere
1495 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1496 VectorNormalize(normal);
1497 dot = DotProduct(normal, light);
1498 if (dot > 0.5) // interior reflection
1499 f += ((dot * 2) - 1);
1500 else if (dot < -0.5) // exterior reflection
1501 f += ((dot * -2) - 1);
1503 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1504 VectorNormalize(normal);
1505 dot = DotProduct(normal, light);
1506 if (dot > 0.5) // interior reflection
1507 f += ((dot * 2) - 1);
1508 else if (dot < -0.5) // exterior reflection
1509 f += ((dot * -2) - 1);
1511 f += 16; // just to give it a haze so you can see the outline
1512 f = bound(0, f, 255);
1519 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1521 int basex, basey, y;
1522 basex = ((texnum >> 0) & 7) * 32;
1523 basey = ((texnum >> 3) & 7) * 32;
1524 particletexture[texnum].s1 = (basex + 1) / 256.0f;
1525 particletexture[texnum].t1 = (basey + 1) / 256.0f;
1526 particletexture[texnum].s2 = (basex + 31) / 256.0f;
1527 particletexture[texnum].t2 = (basey + 31) / 256.0f;
1528 for (y = 0;y < 32;y++)
1529 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 4);
1532 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1535 float cx, cy, dx, dy, f, iradius;
1537 cx = lhrandom(radius + 1, 30 - radius);
1538 cy = lhrandom(radius + 1, 30 - radius);
1539 iradius = 1.0f / radius;
1540 alpha *= (1.0f / 255.0f);
1541 for (y = 0;y < 32;y++)
1543 for (x = 0;x < 32;x++)
1547 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1550 d = data + (y * 32 + x) * 4;
1551 d[0] += f * (red - d[0]);
1552 d[1] += f * (green - d[1]);
1553 d[2] += f * (blue - d[2]);
1559 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1562 for (i = 0;i < 32*32;i++, data += 4)
1564 data[0] = bound(minr, data[0], maxr);
1565 data[1] = bound(ming, data[1], maxg);
1566 data[2] = bound(minb, data[2], maxb);
1570 void particletextureinvert(qbyte *data)
1573 for (i = 0;i < 32*32;i++, data += 4)
1575 data[0] = 255 - data[0];
1576 data[1] = 255 - data[1];
1577 data[2] = 255 - data[2];
1581 static void R_InitParticleTexture (void)
1583 int x, y, d, i, j, k, m;
1584 float dx, dy, radius, f, f2;
1585 qbyte data[32][32][4], noise1[64][64], noise2[64][64], data2[64][16][4];
1587 qbyte particletexturedata[256*256*4];
1589 // a note: decals need to modulate (multiply) the background color to
1590 // properly darken it (stain), and they need to be able to alpha fade,
1591 // this is a very difficult challenge because it means fading to white
1592 // (no change to background) rather than black (darkening everything
1593 // behind the whole decal polygon), and to accomplish this the texture is
1594 // inverted (dark red blood on white background becomes brilliant cyan
1595 // and white on black background) so we can alpha fade it to black, then
1596 // we invert it again during the blendfunc to make it work...
1598 memset(particletexturedata, 255, sizeof(particletexturedata));
1601 for (i = 0;i < 8;i++)
1603 memset(&data[0][0][0], 255, sizeof(data));
1606 fractalnoise(&noise1[0][0], 64, 4);
1607 fractalnoise(&noise2[0][0], 64, 8);
1609 for (y = 0;y < 32;y++)
1612 for (x = 0;x < 32;x++)
1615 d = (noise2[y][x] - 128) * 3 + 192;
1617 d = d * (256 - (int) (dx*dx+dy*dy)) / 256;
1618 d = (d * noise1[y][x]) >> 7;
1619 d = bound(0, d, 255);
1620 data[y][x][3] = (qbyte) d;
1627 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1631 for (i = 0;i < 16;i++)
1633 memset(&data[0][0][0], 255, sizeof(data));
1634 radius = i * 3.0f / 16.0f;
1635 f2 = 255.0f * ((15.0f - i) / 15.0f);
1636 for (y = 0;y < 32;y++)
1638 dy = (y - 16) * 0.25f;
1639 for (x = 0;x < 32;x++)
1641 dx = (x - 16) * 0.25f;
1642 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
1643 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1646 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1650 memset(&data[0][0][0], 255, sizeof(data));
1651 for (y = 0;y < 32;y++)
1654 for (x = 0;x < 32;x++)
1657 d = (256 - (dx*dx+dy*dy));
1658 d = bound(0, d, 255);
1659 data[y][x][3] = (qbyte) d;
1662 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1665 memset(&data[0][0][0], 255, sizeof(data));
1666 light[0] = 1;light[1] = 1;light[2] = 1;
1667 VectorNormalize(light);
1668 for (y = 0;y < 32;y++)
1669 for (x = 0;x < 32;x++)
1670 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);
1671 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1674 memset(&data[0][0][0], 255, sizeof(data));
1675 light[0] = 1;light[1] = 1;light[2] = 1;
1676 VectorNormalize(light);
1677 for (y = 0;y < 32;y++)
1678 for (x = 0;x < 32;x++)
1679 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
1680 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1683 for (i = 0;i < 8;i++)
1685 memset(&data[0][0][0], 255, sizeof(data));
1686 for (k = 0;k < 24;k++)
1687 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 160);
1688 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1689 particletextureinvert(&data[0][0][0]);
1690 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1694 for (i = 0;i < 8;i++)
1696 memset(&data[0][0][0], 255, sizeof(data));
1697 for (k = 0;k < 24;k++)
1698 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 96);
1699 for (j = 3;j < 7;j++)
1700 for (k = 0, m = rand() % 12;k < m;k++)
1701 particletextureblotch(&data[0][0][0], j, 96, 0, 0, 192);
1702 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1703 particletextureinvert(&data[0][0][0]);
1704 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1708 for (i = 0;i < 8;i++)
1710 memset(&data[0][0][0], 255, sizeof(data));
1711 for (k = 0;k < 12;k++)
1712 particletextureblotch(&data[0][0][0], 2, 0, 0, 0, 128);
1713 for (k = 0;k < 3;k++)
1714 particletextureblotch(&data[0][0][0], 14, 0, 0, 0, 160);
1715 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1716 particletextureinvert(&data[0][0][0]);
1717 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1721 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1722 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1723 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1725 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1726 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1727 particletexture[i].texture = particlefonttexture;
1730 fractalnoise(&noise1[0][0], 64, 4);
1732 for (y = 0;y < 64;y++)
1734 for (x = 0;x < 16;x++)
1740 d = d * d * noise1[y][x] / (7 * 7);
1741 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1742 data2[y][x][3] = 255;
1746 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "beam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1747 particletexture[tex_beam].s1 = 0;
1748 particletexture[tex_beam].t1 = 0;
1749 particletexture[tex_beam].s2 = 1;
1750 particletexture[tex_beam].t2 = 1;
1754 static void r_part_start(void)
1756 particletexturepool = R_AllocTexturePool();
1757 R_InitParticleTexture ();
1760 static void r_part_shutdown(void)
1762 R_FreeTexturePool(&particletexturepool);
1765 static void r_part_newmap(void)
1767 cl_numparticles = 0;
1770 void R_Particles_Init (void)
1772 Cvar_RegisterVariable(&r_drawparticles);
1773 #ifdef WORKINGLQUAKE
1776 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1780 #ifdef WORKINGLQUAKE
1781 void R_InitParticles(void)
1783 CL_Particles_Init();
1788 float particle_vertex3f[12], particle_texcoord2f[8];
1790 #ifdef WORKINGLQUAKE
1791 void R_DrawParticle(particle_t *p)
1794 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1796 const particle_t *p = calldata1;
1799 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1800 particletexture_t *tex;
1802 VectorCopy(p->org, org);
1804 tex = &particletexture[p->texnum];
1805 cr = p->color[0] * (1.0f / 255.0f);
1806 cg = p->color[1] * (1.0f / 255.0f);
1807 cb = p->color[2] * (1.0f / 255.0f);
1808 ca = p->alpha * (1.0f / 255.0f);
1809 if (p->blendmode == PBLEND_MOD)
1820 #ifndef WORKINGLQUAKE
1821 if (fogenabled && p->blendmode != PBLEND_MOD)
1823 VectorSubtract(org, r_origin, fogvec);
1824 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1829 if (p->blendmode == 0)
1831 cr += fogcolor[0] * fog;
1832 cg += fogcolor[1] * fog;
1833 cb += fogcolor[2] * fog;
1840 GL_Color(cr, cg, cb, ca);
1842 R_Mesh_Matrix(&r_identitymatrix);
1844 memset(&m, 0, sizeof(m));
1845 m.tex[0] = R_GetTexture(tex->texture);
1846 m.pointer_texcoord[0] = particle_texcoord2f;
1847 R_Mesh_State_Texture(&m);
1849 if (p->blendmode == 0)
1850 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1851 else if (p->blendmode == 1)
1852 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1854 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1855 GL_DepthMask(false);
1857 GL_VertexPointer(particle_vertex3f);
1859 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1861 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1864 if (DotProduct(p->vel2, r_origin) > DotProduct(p->vel2, org))
1866 VectorNegate(p->vel2, v);
1867 VectorVectors(v, right, up);
1870 VectorVectors(p->vel2, right, up);
1871 VectorScale(right, p->scalex, right);
1872 VectorScale(up, p->scaley, up);
1876 VectorScale(vright, p->scalex, right);
1877 VectorScale(vup, p->scaley, up);
1879 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1880 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1881 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1882 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1883 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1884 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1885 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1886 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1887 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1888 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1889 particle_vertex3f[10] = org[1] + right[1] - up[1];
1890 particle_vertex3f[11] = org[2] + right[2] - up[2];
1891 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1892 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1893 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1894 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1896 else if (p->orientation == PARTICLE_SPARK)
1898 VectorMA(p->org, -p->scaley, p->vel, v);
1899 VectorMA(p->org, p->scaley, p->vel, up2);
1900 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1901 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1902 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1903 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1904 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1906 else if (p->orientation == PARTICLE_BEAM)
1908 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1909 VectorSubtract(p->vel2, p->org, up);
1910 VectorNormalizeFast(up);
1911 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1912 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1913 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1914 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1915 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1916 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1919 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1922 if (p->blendmode == 0)
1923 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1924 else if (p->blendmode == 1)
1925 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1927 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1928 glColor4f(cr, cg, cb, ca);
1930 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1931 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1932 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1933 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1936 R_Mesh_Draw(4, 2, polygonelements);
1940 void R_DrawParticles (void)
1943 float minparticledist;
1946 #ifdef WORKINGLQUAKE
1950 // LordHavoc: early out conditions
1951 if ((!cl_numparticles) || (!r_drawparticles.integer))
1954 minparticledist = DotProduct(r_origin, vpn) + 4.0f;
1956 #ifdef WORKINGLQUAKE
1957 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1959 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1961 // LordHavoc: only render if not too close
1962 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1963 if (DotProduct(p->org, vpn) >= minparticledist)
1966 glDisable(GL_BLEND);
1967 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1969 // LordHavoc: only render if not too close
1970 c_particles += cl_numparticles;
1971 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1972 if (DotProduct(p->org, vpn) >= minparticledist || p->orientation == PARTICLE_BEAM)
1973 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);