2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
25 #define NUMVERTEXNORMALS 162
26 siextern float r_avertexnormals[NUMVERTEXNORMALS][3];
27 #define m_bytenormals r_avertexnormals
28 #define VectorNormalizeFast VectorNormalize
29 #define CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
30 typedef unsigned char qbyte;
31 #define cl_stainmaps.integer 0
32 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
35 #define CL_EntityParticles R_EntityParticles
36 #define CL_ReadPointFile_f R_ReadPointFile_f
37 #define CL_ParseParticleEffect R_ParseParticleEffect
38 #define CL_ParticleExplosion R_ParticleExplosion
39 #define CL_ParticleExplosion2 R_ParticleExplosion2
40 #define CL_BlobExplosion R_BlobExplosion
41 #define CL_RunParticleEffect R_RunParticleEffect
42 #define CL_LavaSplash R_LavaSplash
43 #define CL_RocketTrail2 R_RocketTrail2
44 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
46 vec3_t right1, right2, diff, normal;
48 VectorSubtract (org2, org1, normal);
49 VectorNormalizeFast (normal);
51 // calculate 'right' vector for start
52 VectorSubtract (r_vieworigin, org1, diff);
53 VectorNormalizeFast (diff);
54 CrossProduct (normal, diff, right1);
56 // calculate 'right' vector for end
57 VectorSubtract (r_vieworigin, org2, diff);
58 VectorNormalizeFast (diff);
59 CrossProduct (normal, diff, right2);
61 vert[ 0] = org1[0] + width * right1[0];
62 vert[ 1] = org1[1] + width * right1[1];
63 vert[ 2] = org1[2] + width * right1[2];
64 vert[ 3] = org1[0] - width * right1[0];
65 vert[ 4] = org1[1] - width * right1[1];
66 vert[ 5] = org1[2] - width * right1[2];
67 vert[ 6] = org2[0] - width * right2[0];
68 vert[ 7] = org2[1] - width * right2[1];
69 vert[ 8] = org2[2] - width * right2[2];
70 vert[ 9] = org2[0] + width * right2[0];
71 vert[10] = org2[1] + width * right2[1];
72 vert[11] = org2[2] + width * right2[2];
74 void fractalnoise(qbyte *noise, int size, int startgrid)
76 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
78 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
80 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
81 if (size != (1 << sizepower))
82 Sys_Error("fractalnoise: size must be power of 2\n");
84 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
85 if (startgrid != (1 << gridpower))
86 Sys_Error("fractalnoise: grid must be power of 2\n");
88 startgrid = bound(0, startgrid, size);
90 amplitude = 0xFFFF; // this gets halved before use
91 noisebuf = malloc(size*size*sizeof(int));
92 memset(noisebuf, 0, size*size*sizeof(int));
94 for (g2 = startgrid;g2;g2 >>= 1)
96 // brownian motion (at every smaller level there is random behavior)
98 for (y = 0;y < size;y += g2)
99 for (x = 0;x < size;x += g2)
100 n(x,y) += (rand()&litude);
105 // subdivide, diamond-square algorithm (really this has little to do with squares)
107 for (y = 0;y < size;y += g2)
108 for (x = 0;x < size;x += g2)
109 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
111 for (y = 0;y < size;y += g2)
112 for (x = 0;x < size;x += g2)
114 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
115 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
119 // find range of noise values
121 for (y = 0;y < size;y++)
122 for (x = 0;x < size;x++)
124 if (n(x,y) < min) min = n(x,y);
125 if (n(x,y) > max) max = n(x,y);
129 // normalize noise and copy to output
130 for (y = 0;y < size;y++)
131 for (x = 0;x < size;x++)
132 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
136 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
140 right[0] = forward[2];
141 right[1] = -forward[0];
142 right[2] = forward[1];
144 d = DotProduct(forward, right);
145 right[0] -= d * forward[0];
146 right[1] -= d * forward[1];
147 right[2] -= d * forward[2];
148 VectorNormalizeFast(right);
149 CrossProduct(right, forward, up);
153 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
155 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int hitbmodels, void **hitent, int hitsupercontentsmask)
162 memset (&trace, 0, sizeof(trace));
164 VectorCopy (end, trace.endpos);
166 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
168 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
170 VectorCopy(trace.endpos, impact);
171 VectorCopy(trace.plane.normal, normal);
172 return trace.fraction;
175 #include "cl_collision.h"
179 #define MAX_PARTICLES 32768 // default max # of particles at one time
180 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
184 pt_dead, pt_static, pt_rain, pt_bubble, pt_blood, pt_grow, pt_decal, pt_decalfade, pt_ember
190 PARTICLE_BILLBOARD = 0,
192 PARTICLE_ORIENTED_DOUBLESIDED = 2,
205 typedef struct particle_s
216 float alpha; // 0-255
217 float alphafade; // how much alpha reduces per second
218 float time2; // used for various things (snow fluttering, for example)
219 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)
220 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
222 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
223 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
224 float pressure; // if non-zero, apply pressure to other particles
226 #ifndef WORKINGLQUAKE
227 entity_render_t *owner; // decal stuck to this entity
228 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
229 vec3_t relativeorigin; // decal at this location in entity's coordinate space
230 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
235 static int particlepalette[256] =
237 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
238 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
239 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
240 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
241 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
242 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
243 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
244 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
245 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
246 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
247 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
248 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
249 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
250 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
251 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
252 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
253 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
254 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
255 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
256 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
257 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
258 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
259 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
260 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
261 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
262 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
263 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
264 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
265 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
266 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
267 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
268 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
271 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
273 // texture numbers in particle font
274 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
275 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
276 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
277 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
278 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
279 static const int tex_particle = 63;
280 static const int tex_bubble = 62;
281 static const int tex_raindrop = 61;
282 static const int tex_beam = 60;
284 static int cl_maxparticles;
285 static int cl_numparticles;
286 static int cl_freeparticle;
287 static particle_t *particles;
289 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
290 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
291 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
292 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
293 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
294 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
295 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
296 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
297 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
298 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
299 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
300 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
301 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
302 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
303 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
304 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
306 #ifndef WORKINGLQUAKE
307 static mempool_t *cl_part_mempool;
310 void CL_Particles_Clear(void)
321 void CL_ReadPointFile_f (void);
322 void CL_Particles_Init (void)
326 i = COM_CheckParm ("-particles");
328 if (i && i < com_argc - 1)
330 cl_maxparticles = (int)(atoi(com_argv[i+1]));
331 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
332 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
335 cl_maxparticles = MAX_PARTICLES;
337 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
339 Cvar_RegisterVariable (&cl_particles);
340 Cvar_RegisterVariable (&cl_particles_quality);
341 Cvar_RegisterVariable (&cl_particles_size);
342 Cvar_RegisterVariable (&cl_particles_bloodshowers);
343 Cvar_RegisterVariable (&cl_particles_blood);
344 Cvar_RegisterVariable (&cl_particles_blood_alpha);
345 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
346 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
347 Cvar_RegisterVariable (&cl_particles_smoke);
348 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
349 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
350 Cvar_RegisterVariable (&cl_particles_sparks);
351 Cvar_RegisterVariable (&cl_particles_bubbles);
352 Cvar_RegisterVariable (&cl_decals);
353 Cvar_RegisterVariable (&cl_decals_time);
354 Cvar_RegisterVariable (&cl_decals_fadetime);
357 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
359 cl_part_mempool = Mem_AllocPool("CL_Part", 0, NULL);
360 particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
366 // list of all 26 parameters:
367 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
368 // porientation - PARTICLE_ enum values (PARTICLE_BILLBOARD, PARTICLE_SPARK, etc)
369 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
370 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
371 // plight - no longer used (this used to turn on particle lighting)
372 // pblendmode - PBLEND_ enum values (PBLEND_ALPHA, PBLEND_ADD, etc)
373 // pscalex,pscaley - width and height of particle (according to orientation), these are normally the same except when making sparks and beams
374 // palpha - opacity of particle as 0-255 (can be more than 255)
375 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
376 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
377 // pgravity - how much effect gravity has on the particle (0-1)
378 // pbounce - how much bounce the particle has when it hits a surface (0-1), -1 makes a blood splat when it hits a surface, 0 does not even check for collisions
379 // px,py,pz - starting origin of particle
380 // pvx,pvy,pvz - starting velocity of particle
381 // ptime2 - extra time parameter for certain particle types (pt_decal delayed fades and pt_rain snowflutter use this)
382 // pvx2,pvy2,pvz2 - for PARTICLE_ORIENTED_DOUBLESIDED this is the surface normal of the orientation (forward vector), pt_rain uses this for snow fluttering
383 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
384 // ppressure - pushes other particles away if they are within 64 units distance, the force is based on scalex, this feature is supported but not currently used
385 particle_t *particle(ptype_t ptype, porientation_t porientation, int pcolor1, int pcolor2, int ptex, int plight, pblend_t pblendmode, float pscalex, float pscaley, float palpha, float palphafade, float ptime, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float ptime2, float pvx2, float pvy2, float pvz2, float pfriction, float ppressure)
388 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
389 ptempcolor = (pcolor1);
390 ptempcolor2 = (pcolor2);
391 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
392 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
393 pcb2 = (ptempcolor2) & 0xFF;
394 if (ptempcolor != ptempcolor2)
396 pcr1 = ((ptempcolor) >> 16) & 0xFF;
397 pcg1 = ((ptempcolor) >> 8) & 0xFF;
398 pcb1 = (ptempcolor) & 0xFF;
399 ptempcolor = rand() & 0xFF;
400 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
401 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
402 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
404 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
405 if (cl_freeparticle >= cl_maxparticles)
407 part = &particles[cl_freeparticle++];
408 if (cl_numparticles < cl_freeparticle)
409 cl_numparticles = cl_freeparticle;
410 memset(part, 0, sizeof(*part));
411 part->type = (ptype);
412 part->color[0] = pcr2;
413 part->color[1] = pcg2;
414 part->color[2] = pcb2;
415 part->color[3] = 0xFF;
416 part->orientation = porientation;
418 part->blendmode = pblendmode;
419 part->scalex = (pscalex);
420 part->scaley = (pscaley);
421 part->alpha = (palpha);
422 part->alphafade = (palphafade);
423 part->die = cl.time + (ptime);
424 part->gravity = (pgravity);
425 part->bounce = (pbounce);
429 part->vel[0] = (pvx);
430 part->vel[1] = (pvy);
431 part->vel[2] = (pvz);
432 part->time2 = (ptime2);
433 part->vel2[0] = (pvx2);
434 part->vel2[1] = (pvy2);
435 part->vel2[2] = (pvz2);
436 part->friction = (pfriction);
437 part->pressure = (ppressure);
441 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
444 if (!cl_decals.integer)
446 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);
447 #ifndef WORKINGLQUAKE
451 p->ownermodel = p->owner->model;
452 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
453 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
454 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
459 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
462 float bestfrac, bestorg[3], bestnormal[3];
463 float frac, v[3], normal[3], org2[3];
465 void *besthitent = NULL, *hitent;
467 entity_render_t *besthitent = NULL, *hitent;
470 for (i = 0;i < 32;i++)
473 VectorMA(org, maxdist, org2, org2);
474 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
479 VectorCopy(v, bestorg);
480 VectorCopy(normal, bestnormal);
484 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
492 void CL_EntityParticles (entity_t *ent)
496 float sp, sy, cp, cy;
500 static vec3_t avelocities[NUMVERTEXNORMALS];
501 if (!cl_particles.integer) return;
506 if (!avelocities[0][0])
507 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
508 avelocities[0][i] = (rand()&255) * 0.01;
510 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
512 angle = cl.time * avelocities[i][0];
515 angle = cl.time * avelocities[i][1];
524 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);
526 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);
532 void CL_ReadPointFile_f (void)
536 char *pointfile = NULL, *pointfilepos, *t, tchar;
537 char name[MAX_OSPATH];
542 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
543 strlcat (name, ".pts", sizeof (name));
545 pointfile = COM_LoadTempFile (name);
547 pointfile = FS_LoadFile(name, tempmempool, true);
551 Con_Printf("Could not open %s\n", name);
555 Con_Printf("Reading %s...\n", name);
558 pointfilepos = pointfile;
559 while (*pointfilepos)
561 while (*pointfilepos == '\n' || *pointfilepos == '\r')
566 while (*t && *t != '\n' && *t != '\r')
570 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
576 VectorCopy(org, leakorg);
579 if (cl_numparticles < cl_maxparticles - 3)
582 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);
585 #ifndef WORKINGLQUAKE
588 VectorCopy(leakorg, org);
589 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
591 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);
592 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);
593 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);
598 CL_ParseParticleEffect
600 Parse an effect out of the server message
603 void CL_ParseParticleEffect (void)
606 int i, count, msgcount, color;
609 for (i=0 ; i<3 ; i++)
610 dir[i] = MSG_ReadChar () * (1.0/16);
611 msgcount = MSG_ReadByte ();
612 color = MSG_ReadByte ();
619 if (cl_particles_blood_bloodhack.integer)
624 CL_BloodPuff(org, dir, count / 2);
630 CL_BloodPuff(org, dir, count / 2);
634 CL_RunParticleEffect (org, dir, color, count);
643 void CL_ParticleExplosion (vec3_t org)
648 if (cl_stainmaps.integer)
649 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
650 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
652 i = CL_PointQ1Contents(org);
653 if ((i == CONTENTS_SLIME || i == CONTENTS_WATER) && cl_particles.integer && cl_particles_bubbles.integer)
655 for (i = 0;i < 128 * cl_particles_quality.value;i++)
656 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);
661 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
663 if (cl_particles.integer && cl_particles_smoke.integer)
665 for (i = 0;i < 64;i++)
668 v2[0] = lhrandom(-64, 64);
669 v2[1] = lhrandom(-64, 64);
670 v2[2] = lhrandom(-8, 24);
672 for (k = 0;k < 16;k++)
674 v[0] = org[0] + lhrandom(-64, 64);
675 v[1] = org[1] + lhrandom(-64, 64);
676 v[2] = org[2] + lhrandom(-8, 24);
677 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
680 VectorSubtract(v2, org, v2);
682 VectorScale(v2, 2.0f, v2);
683 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);
689 if (cl_particles.integer && cl_particles_sparks.integer)
690 for (i = 0;i < 128 * cl_particles_quality.value;i++)
691 particle(pt_static, PARTICLE_SPARK, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, 1.0f, 0.02f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 512, 9999, 1, 0, org[0], org[1], org[2], lhrandom(-256, 256), lhrandom(-256, 256), lhrandom(-256, 256) + 80, 0, 0, 0, 0, 0.2, 0);
694 //if (cl_explosions.integer)
695 // R_NewExplosion(org);
697 if (cl_particles.integer && cl_particles_sparks.integer)
698 for (i = 0;i < 64 * cl_particles_quality.value;i++)
699 particle(pt_ember, PARTICLE_SPARK, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, 1.0f, 0.01f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 256, 9999, 0.7, 0, org[0], org[1], org[2], lhrandom(-256, 256), lhrandom(-256, 256), lhrandom(-256, 256) + 80, cl.time, 0, 0, 0, 0, 0);
702 //if (cl_explosions.integer)
703 // R_NewExplosion(org);
705 if (cl_particles.integer && cl_particles_sparks.integer)
706 for (i = 0;i < 256 * cl_particles_quality.value;i++)
707 particle(pt_static, PARTICLE_SPARK, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, 1.5f, 0.05f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 512, 9999, 1, 0, org[0], org[1], org[2], lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192) + 160, 0, 0, 0, 0, 0.2, 0);
710 if (cl_explosions.integer)
717 CL_ParticleExplosion2
721 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
727 if (!cl_particles.integer) return;
729 for (i = 0;i < 512 * cl_particles_quality.value;i++)
731 VectorRandom (offset);
732 VectorScale (offset, 192, vel);
733 VectorScale (offset, 8, offset);
734 k = particlepalette[colorStart + (i % colorLength)];
735 pscale = lhrandom(0.5, 1.5);
736 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, pscale, pscale, (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*512, 9999, 0, 0, org[0] + offset[0], org[1] + offset[1], org[2] + offset[2], vel[0], vel[1], vel[2], 0, 0, 0, 0, lhrandom(1.5, 3), 0);
746 void CL_BlobExplosion (vec3_t org)
748 if (cl_stainmaps.integer)
749 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
750 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
752 if (cl_explosions.integer)
762 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
768 CL_ParticleExplosion(org);
771 if (!cl_particles.integer) return;
772 count *= cl_particles_quality.value;
775 k = particlepalette[color + (rand()&7)];
776 if (gamemode == GAME_GOODVSBAD2)
777 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);
779 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);
783 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
789 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
794 if (cl_stainmaps.integer)
795 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
796 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
798 if (!cl_particles.integer) return;
800 if (cl_particles_bulletimpacts.integer)
803 if (cl_particles_smoke.integer)
805 k = count * 0.25 * cl_particles_quality.value;
808 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
809 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
810 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
811 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
812 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.2, 0);
816 if (cl_particles_sparks.integer)
819 count *= cl_particles_quality.value;
822 k = particlepalette[0x68 + (rand() & 7)];
823 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.2, 0);
829 void CL_PlasmaBurn (vec3_t org)
831 if (cl_stainmaps.integer)
832 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
833 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
836 static float bloodcount = 0;
837 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
841 // bloodcount is used to accumulate counts too small to cause a blood particle
842 if (!cl_particles.integer) return;
843 if (!cl_particles_blood.integer) return;
850 while(bloodcount > 0)
852 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
853 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
854 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
855 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
856 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);
857 bloodcount -= 16 / cl_particles_quality.value;
861 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
863 vec3_t org, vel, diff, center, velscale;
864 if (!cl_particles.integer) return;
865 if (!cl_particles_bloodshowers.integer) return;
866 if (!cl_particles_blood.integer) return;
868 VectorSubtract(maxs, mins, diff);
869 center[0] = (mins[0] + maxs[0]) * 0.5;
870 center[1] = (mins[1] + maxs[1]) * 0.5;
871 center[2] = (mins[2] + maxs[2]) * 0.5;
872 velscale[0] = velspeed * 2.0 / diff[0];
873 velscale[1] = velspeed * 2.0 / diff[1];
874 velscale[2] = velspeed * 2.0 / diff[2];
876 bloodcount += count * 5.0f;
877 while (bloodcount > 0)
879 org[0] = lhrandom(mins[0], maxs[0]);
880 org[1] = lhrandom(mins[1], maxs[1]);
881 org[2] = lhrandom(mins[2], maxs[2]);
882 vel[0] = (org[0] - center[0]) * velscale[0];
883 vel[1] = (org[1] - center[1]) * velscale[1];
884 vel[2] = (org[2] - center[2]) * velscale[2];
885 bloodcount -= 16 / cl_particles_quality.value;
886 particle(pt_blood, PARTICLE_BILLBOARD, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], true, PBLEND_MOD, 8, 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 9999, 0, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 0, 0, 0, 0, 1, 0);
890 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
894 if (!cl_particles.integer) return;
895 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
896 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
897 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
899 count *= cl_particles_quality.value;
902 k = particlepalette[colorbase + (rand()&3)];
903 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ALPHA, 2, 2, 255 / cl_particles_quality.value, 0, lhrandom(1, 2), gravity ? 1 : 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0] + lhrandom(-randomvel, randomvel), dir[1] + lhrandom(-randomvel, randomvel), dir[2] + lhrandom(-randomvel, randomvel), 0, 0, 0, 0, 0, 0);
907 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
910 float t, z, minz, maxz;
911 if (!cl_particles.integer) return;
912 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
913 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
914 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
915 if (dir[2] < 0) // falling
917 t = (maxs[2] - mins[2]) / -dir[2];
922 t = (maxs[2] - mins[2]) / dir[2];
925 if (t < 0 || t > 2) // sanity check
928 minz = z - fabs(dir[2]) * 0.1;
929 maxz = z + fabs(dir[2]) * 0.1;
930 minz = bound(mins[2], minz, maxs[2]);
931 maxz = bound(mins[2], maxz, maxs[2]);
933 count *= cl_particles_quality.value;
938 count *= 4; // ick, this should be in the mod or maps?
942 k = particlepalette[colorbase + (rand()&3)];
943 if (gamemode == GAME_GOODVSBAD2)
945 particle(pt_rain, PARTICLE_SPARK, k, k, tex_particle, true, PBLEND_ADD, 20, 20, lhrandom(8, 16) / cl_particles_quality.value, 0, t, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], cl.time + 9999, dir[0], dir[1], dir[2], 0, 0);
949 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);
956 k = particlepalette[colorbase + (rand()&3)];
957 if (gamemode == GAME_GOODVSBAD2)
959 particle(pt_rain, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, 20, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, t, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, dir[0], dir[1], dir[2], 0, 0);
963 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);
968 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
972 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
977 if (!cl_particles.integer) return;
979 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
980 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
981 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
983 center[0] = (mins[0] + maxs[0]) * 0.5f;
984 center[1] = (mins[1] + maxs[1]) * 0.5f;
985 center[2] = (mins[2] + maxs[2]) * 0.5f;
987 count *= cl_particles_quality.value;
990 k = particlepalette[224 + (rand()&15)];
991 o[0] = lhrandom(mins[0], maxs[0]);
992 o[1] = lhrandom(mins[1], maxs[1]);
993 o[2] = lhrandom(mins[2], maxs[2]);
994 VectorSubtract(o, center, v);
995 VectorNormalizeFast(v);
996 VectorScale(v, 100, v);
997 v[2] += sv_gravity.value * 0.15f;
998 particle(pt_static, PARTICLE_BILLBOARD, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, 1.5, 1.5, lhrandom(64, 128) / cl_particles_quality.value, 128 / cl_particles_quality.value, 9999, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0, 0, 0, 0, 0.2, 0);
1002 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
1006 if (!cl_particles.integer) return;
1007 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1008 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1009 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1011 count *= cl_particles_quality.value;
1014 k = particlepalette[224 + (rand()&15)];
1015 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);
1017 particle(pt_static, PARTICLE_BILLBOARD, 0x303030, 0x606060, tex_smoke[rand()&7], false, PBLEND_ADD, 6, 6, lhrandom(48, 96) / cl_particles_quality.value, 64 / cl_particles_quality.value, 9999, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 32), 0, 0, 0, 0, 0, 0);
1021 void CL_Flames (vec3_t org, vec3_t vel, int count)
1024 if (!cl_particles.integer) return;
1026 count *= cl_particles_quality.value;
1029 k = particlepalette[224 + (rand()&15)];
1030 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);
1042 void CL_LavaSplash (vec3_t origin)
1044 float i, j, inc, vel;
1047 if (!cl_particles.integer) return;
1049 inc = 32 / cl_particles_quality.value;
1050 for (i = -128;i < 128;i += inc)
1052 for (j = -128;j < 128;j += inc)
1054 dir[0] = j + lhrandom(0, 8);
1055 dir[1] = i + lhrandom(0, 8);
1057 org[0] = origin[0] + dir[0];
1058 org[1] = origin[1] + dir[1];
1059 org[2] = origin[2] + lhrandom(0, 64);
1060 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1061 if (gamemode == GAME_GOODVSBAD2)
1063 k = particlepalette[0 + (rand()&255)];
1064 l = particlepalette[0 + (rand()&255)];
1065 particle(pt_static, PARTICLE_BILLBOARD, k, l, tex_particle, false, PBLEND_ADD, 12, 12, inc * 8, inc * 8, 9999, 0.05, 1, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, 0, 0);
1069 k = l = particlepalette[224 + (rand()&7)];
1070 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);
1083 void R_TeleportSplash (vec3_t org)
1086 if (!cl_particles.integer) return;
1088 inc = 8 / cl_particles_quality.value;
1089 for (i = -16;i < 16;i += inc)
1090 for (j = -16;j < 16;j += inc)
1091 for (k = -24;k < 32;k += inc)
1092 particle(pt_static, PARTICLE_BILLBOARD, 0xA0A0A0, 0xFFFFFF, tex_particle, false, PBLEND_ADD, 10, 10, inc * 32, inc * lhrandom(8, 16), inc * 32, 9999, 0, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-256, 256), 0, 0, 0, 0, 1, 0);
1096 #ifdef WORKINGLQUAKE
1097 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1099 void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
1102 vec3_t vec, dir, vel, pos;
1103 float len, dec, speed, qd;
1104 int contents, smoke, blood, bubbles;
1106 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1109 VectorSubtract(end, start, dir);
1110 VectorNormalize(dir);
1112 VectorSubtract (end, start, vec);
1113 #ifdef WORKINGLQUAKE
1114 len = VectorNormalize (vec);
1116 speed = 1.0f / cl.frametime;
1117 VectorSubtract(end, start, vel);
1119 len = VectorNormalizeLength (vec);
1120 dec = -ent->persistent.trail_time;
1121 ent->persistent.trail_time += len;
1122 if (ent->persistent.trail_time < 0.01f)
1125 // if we skip out, leave it reset
1126 ent->persistent.trail_time = 0.0f;
1128 speed = ent->state_current.time - ent->state_previous.time;
1130 speed = 1.0f / speed;
1131 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1133 VectorScale(vel, speed, vel);
1135 // advance into this frame to reach the first puff location
1136 VectorMA(start, dec, vec, pos);
1139 contents = CL_PointQ1Contents(pos);
1140 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1143 smoke = cl_particles.integer && cl_particles_smoke.integer;
1144 blood = cl_particles.integer && cl_particles_blood.integer;
1145 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1146 qd = 1.0f / cl_particles_quality.value;
1152 case 0: // rocket trail
1156 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);
1157 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);
1160 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);
1163 case 1: // grenade trail
1164 // FIXME: make it gradually stop smoking
1167 particle(pt_grow, PARTICLE_BILLBOARD, 0x303030, 0x606060, tex_smoke[rand()&7], false, PBLEND_ADD, 3, 3, qd*cl_particles_smoke_alpha.value*100, qd*cl_particles_smoke_alphafade.value*100, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 7, 0, 0, 0, 0, 0);
1172 case 4: // slight blood
1175 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);
1178 case 3: // green tracer
1182 if (gamemode == GAME_GOODVSBAD2)
1183 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);
1185 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);
1189 case 5: // flame tracer
1192 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);
1195 case 6: // voor trail
1199 if (gamemode == GAME_GOODVSBAD2)
1200 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);
1201 else if (gamemode == GAME_PRYDON)
1202 particle(pt_static, PARTICLE_BILLBOARD, 0x103040, 0x204050, tex_particle, false, PBLEND_ADD, 6, 6, qd*128, qd*384, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0, 0, 0, 0, 0, 0);
1204 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);
1208 case 7: // Nehahra smoke tracer
1211 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);
1213 case 8: // Nexuiz plasma trail
1216 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);
1220 // advance to next time and position
1222 VectorMA (pos, dec, vec, pos);
1224 #ifndef WORKINGLQUAKE
1225 ent->persistent.trail_time = len;
1229 void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1233 if (!cl_particles.integer) return;
1234 if (!cl_particles_smoke.integer) return;
1236 VectorCopy(start, pos);
1237 VectorSubtract(end, start, vec);
1238 #ifdef WORKINGLQUAKE
1239 len = VectorNormalize(vec);
1241 len = VectorNormalizeLength(vec);
1243 color = particlepalette[color];
1244 dec = 3.0f / cl_particles_quality.value;
1247 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);
1249 VectorMA(pos, dec, vec, pos);
1253 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1255 int tempcolor2, cr, cg, cb;
1259 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1260 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);
1263 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1266 if (!cl_particles.integer) return;
1269 if (cl_particles_smoke.integer)
1270 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1271 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);
1274 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1277 if (!cl_particles.integer) return;
1279 if (cl_stainmaps.integer)
1280 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1281 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1284 if (cl_particles_smoke.integer)
1285 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1286 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);
1289 if (cl_particles_sparks.integer)
1290 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1291 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);
1299 void CL_MoveParticles (void)
1302 int i, maxparticle, j, a, content;
1303 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1304 #ifdef WORKINGLQUAKE
1307 entity_render_t *hitent;
1310 // LordHavoc: early out condition
1311 if (!cl_numparticles)
1313 cl_freeparticle = 0;
1317 #ifdef WORKINGLQUAKE
1318 frametime = cl.frametime;
1320 frametime = cl.time - cl.oldtime;
1322 gravity = frametime * sv_gravity.value;
1323 dvel = 1+4*frametime;
1324 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1328 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1334 VectorCopy(p->org, p->oldorg);
1335 VectorMA(p->org, frametime, p->vel, p->org);
1336 VectorCopy(p->org, org);
1339 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1341 VectorCopy(v, p->org);
1344 // assume it's blood (lame, but...)
1345 #ifndef WORKINGLQUAKE
1346 if (cl_stainmaps.integer)
1347 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));
1349 if (!cl_decals.integer)
1356 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1357 // convert from a blood particle to a blood decal
1358 p->texnum = tex_blooddecal[rand()&7];
1359 #ifndef WORKINGLQUAKE
1361 p->ownermodel = hitent->model;
1362 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1363 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1364 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1366 p->time2 = cl.time + cl_decals_time.value;
1367 p->die = p->time2 + cl_decals_fadetime.value;
1369 VectorCopy(normal, p->vel2);
1370 VectorClear(p->vel);
1371 VectorAdd(p->org, normal, p->org);
1380 dist = DotProduct(p->vel, normal) * -p->bounce;
1381 VectorMA(p->vel, dist, normal, p->vel);
1382 if (DotProduct(p->vel, p->vel) < 0.03)
1383 VectorClear(p->vel);
1388 p->vel[2] -= p->gravity * gravity;
1390 p->alpha -= p->alphafade * frametime;
1392 if (p->alpha <= 0 || cl.time > p->die)
1400 f = p->friction * frametime;
1402 content = CL_PointQ1Contents(p->org);
1403 if (content != CONTENTS_EMPTY)
1406 VectorScale(p->vel, f, p->vel);
1409 if (p->type != pt_static)
1415 content = CL_PointQ1Contents(p->org);
1417 if (a != CONTENTS_EMPTY)
1419 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1421 p->scalex += frametime * 8;
1422 p->scaley += frametime * 8;
1423 //p->alpha -= bloodwaterfade;
1429 p->vel[2] -= gravity;
1433 content = CL_PointQ1Contents(p->org);
1434 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1441 if (cl.time > p->time2)
1444 p->time2 = cl.time + (rand() & 3) * 0.1;
1445 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1446 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1447 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1450 content = CL_PointQ1Contents(p->org);
1452 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1456 p->scalex += frametime * p->time2;
1457 p->scaley += frametime * p->time2;
1460 #ifndef WORKINGLQUAKE
1461 if (p->owner->model == p->ownermodel)
1463 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1464 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1465 if (cl.time > p->time2)
1467 p->alphafade = p->alpha / (p->die - cl.time);
1468 p->type = pt_decalfade;
1476 #ifndef WORKINGLQUAKE
1477 if (p->owner->model == p->ownermodel)
1479 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1480 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1487 while (cl.time > p->time2)
1490 particle(pt_static, PARTICLE_SPARK, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, p->scalex * 0.75, p->scaley * 0.75, p->alpha, p->alphafade, 9999, 0.5, 0, p->org[0], p->org[1], p->org[2], p->vel[0] * lhrandom(0.4, 0.6), p->vel[1] * lhrandom(0.4, 0.6), p->vel[2] * lhrandom(0.4, 0.6), 0, 0, 0, 0, 0, 0);
1494 Con_Printf("unknown particle type %i\n", p->type);
1500 cl_numparticles = maxparticle + 1;
1501 cl_freeparticle = 0;
1504 #define MAX_PARTICLETEXTURES 64
1505 // particletexture_t is a rectangle in the particlefonttexture
1508 rtexture_t *texture;
1509 float s1, t1, s2, t2;
1514 static int particlefonttexture;
1516 static rtexturepool_t *particletexturepool;
1517 static rtexture_t *particlefonttexture;
1519 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1521 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1523 static qbyte shadebubble(float dx, float dy, vec3_t light)
1527 dz = 1 - (dx*dx+dy*dy);
1528 if (dz > 0) // it does hit the sphere
1532 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1533 VectorNormalize(normal);
1534 dot = DotProduct(normal, light);
1535 if (dot > 0.5) // interior reflection
1536 f += ((dot * 2) - 1);
1537 else if (dot < -0.5) // exterior reflection
1538 f += ((dot * -2) - 1);
1540 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1541 VectorNormalize(normal);
1542 dot = DotProduct(normal, light);
1543 if (dot > 0.5) // interior reflection
1544 f += ((dot * 2) - 1);
1545 else if (dot < -0.5) // exterior reflection
1546 f += ((dot * -2) - 1);
1548 f += 16; // just to give it a haze so you can see the outline
1549 f = bound(0, f, 255);
1556 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1558 int basex, basey, y;
1559 basex = ((texnum >> 0) & 7) * 32;
1560 basey = ((texnum >> 3) & 7) * 32;
1561 particletexture[texnum].s1 = (basex + 1) / 256.0f;
1562 particletexture[texnum].t1 = (basey + 1) / 256.0f;
1563 particletexture[texnum].s2 = (basex + 31) / 256.0f;
1564 particletexture[texnum].t2 = (basey + 31) / 256.0f;
1565 for (y = 0;y < 32;y++)
1566 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 4);
1569 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1572 float cx, cy, dx, dy, f, iradius;
1574 cx = lhrandom(radius + 1, 30 - radius);
1575 cy = lhrandom(radius + 1, 30 - radius);
1576 iradius = 1.0f / radius;
1577 alpha *= (1.0f / 255.0f);
1578 for (y = 0;y < 32;y++)
1580 for (x = 0;x < 32;x++)
1584 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1587 d = data + (y * 32 + x) * 4;
1588 d[0] += f * (red - d[0]);
1589 d[1] += f * (green - d[1]);
1590 d[2] += f * (blue - d[2]);
1596 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1599 for (i = 0;i < 32*32;i++, data += 4)
1601 data[0] = bound(minr, data[0], maxr);
1602 data[1] = bound(ming, data[1], maxg);
1603 data[2] = bound(minb, data[2], maxb);
1607 void particletextureinvert(qbyte *data)
1610 for (i = 0;i < 32*32;i++, data += 4)
1612 data[0] = 255 - data[0];
1613 data[1] = 255 - data[1];
1614 data[2] = 255 - data[2];
1618 static void R_InitParticleTexture (void)
1620 int x, y, d, i, j, k, m;
1621 float dx, dy, radius, f, f2;
1622 qbyte data[32][32][4], noise1[64][64], noise2[64][64], data2[64][16][4];
1624 qbyte particletexturedata[256*256*4];
1626 // a note: decals need to modulate (multiply) the background color to
1627 // properly darken it (stain), and they need to be able to alpha fade,
1628 // this is a very difficult challenge because it means fading to white
1629 // (no change to background) rather than black (darkening everything
1630 // behind the whole decal polygon), and to accomplish this the texture is
1631 // inverted (dark red blood on white background becomes brilliant cyan
1632 // and white on black background) so we can alpha fade it to black, then
1633 // we invert it again during the blendfunc to make it work...
1635 memset(particletexturedata, 255, sizeof(particletexturedata));
1638 for (i = 0;i < 8;i++)
1640 memset(&data[0][0][0], 255, sizeof(data));
1643 fractalnoise(&noise1[0][0], 64, 4);
1644 fractalnoise(&noise2[0][0], 64, 8);
1646 for (y = 0;y < 32;y++)
1649 for (x = 0;x < 32;x++)
1652 d = (noise2[y][x] - 128) * 3 + 192;
1654 d = d * (256 - (int) (dx*dx+dy*dy)) / 256;
1655 d = (d * noise1[y][x]) >> 7;
1656 d = bound(0, d, 255);
1657 data[y][x][3] = (qbyte) d;
1664 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1668 for (i = 0;i < 16;i++)
1670 memset(&data[0][0][0], 255, sizeof(data));
1671 radius = i * 3.0f / 16.0f;
1672 f2 = 255.0f * ((15.0f - i) / 15.0f);
1673 for (y = 0;y < 32;y++)
1675 dy = (y - 16) * 0.25f;
1676 for (x = 0;x < 32;x++)
1678 dx = (x - 16) * 0.25f;
1679 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
1680 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1683 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1687 memset(&data[0][0][0], 255, sizeof(data));
1688 for (y = 0;y < 32;y++)
1691 for (x = 0;x < 32;x++)
1694 d = (256 - (dx*dx+dy*dy));
1695 d = bound(0, d, 255);
1696 data[y][x][3] = (qbyte) d;
1699 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1702 memset(&data[0][0][0], 255, sizeof(data));
1703 light[0] = 1;light[1] = 1;light[2] = 1;
1704 VectorNormalize(light);
1705 for (y = 0;y < 32;y++)
1706 for (x = 0;x < 32;x++)
1707 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);
1708 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1711 memset(&data[0][0][0], 255, sizeof(data));
1712 light[0] = 1;light[1] = 1;light[2] = 1;
1713 VectorNormalize(light);
1714 for (y = 0;y < 32;y++)
1715 for (x = 0;x < 32;x++)
1716 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
1717 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1720 for (i = 0;i < 8;i++)
1722 memset(&data[0][0][0], 255, sizeof(data));
1723 for (k = 0;k < 24;k++)
1724 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 160);
1725 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1726 particletextureinvert(&data[0][0][0]);
1727 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1731 for (i = 0;i < 8;i++)
1733 memset(&data[0][0][0], 255, sizeof(data));
1734 for (k = 0;k < 24;k++)
1735 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 96);
1736 for (j = 3;j < 7;j++)
1737 for (k = 0, m = rand() % 12;k < m;k++)
1738 particletextureblotch(&data[0][0][0], j, 96, 0, 0, 192);
1739 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1740 particletextureinvert(&data[0][0][0]);
1741 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1745 for (i = 0;i < 8;i++)
1747 memset(&data[0][0][0], 255, sizeof(data));
1748 for (k = 0;k < 12;k++)
1749 particletextureblotch(&data[0][0][0], 2, 0, 0, 0, 128);
1750 for (k = 0;k < 3;k++)
1751 particletextureblotch(&data[0][0][0], 14, 0, 0, 0, 160);
1752 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1753 particletextureinvert(&data[0][0][0]);
1754 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1758 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1759 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1760 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1762 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1763 if (!particlefonttexture)
1764 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1765 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1766 particletexture[i].texture = particlefonttexture;
1769 fractalnoise(&noise1[0][0], 64, 4);
1771 for (y = 0;y < 64;y++)
1773 for (x = 0;x < 16;x++)
1779 d = d * d * noise1[y][x] / (7 * 7);
1780 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1781 data2[y][x][3] = 255;
1785 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1786 if (!particletexture[tex_beam].texture)
1787 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1788 particletexture[tex_beam].s1 = 0;
1789 particletexture[tex_beam].t1 = 0;
1790 particletexture[tex_beam].s2 = 1;
1791 particletexture[tex_beam].t2 = 1;
1795 static void r_part_start(void)
1797 particletexturepool = R_AllocTexturePool();
1798 R_InitParticleTexture ();
1801 static void r_part_shutdown(void)
1803 R_FreeTexturePool(&particletexturepool);
1806 static void r_part_newmap(void)
1808 cl_numparticles = 0;
1809 cl_freeparticle = 0;
1812 void R_Particles_Init (void)
1814 Cvar_RegisterVariable(&r_drawparticles);
1815 #ifdef WORKINGLQUAKE
1818 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1822 #ifdef WORKINGLQUAKE
1823 void R_InitParticles(void)
1825 CL_Particles_Init();
1830 float particle_vertex3f[12], particle_texcoord2f[8];
1832 #ifdef WORKINGLQUAKE
1833 void R_DrawParticle(particle_t *p)
1836 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1838 const particle_t *p = calldata1;
1841 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1842 particletexture_t *tex;
1844 VectorCopy(p->org, org);
1846 tex = &particletexture[p->texnum];
1847 cr = p->color[0] * (1.0f / 255.0f);
1848 cg = p->color[1] * (1.0f / 255.0f);
1849 cb = p->color[2] * (1.0f / 255.0f);
1850 ca = p->alpha * (1.0f / 255.0f);
1851 if (p->blendmode == PBLEND_MOD)
1862 #ifndef WORKINGLQUAKE
1863 if (fogenabled && p->blendmode != PBLEND_MOD)
1865 VectorSubtract(org, r_vieworigin, fogvec);
1866 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1871 if (p->blendmode == 0)
1873 cr += fogcolor[0] * fog;
1874 cg += fogcolor[1] * fog;
1875 cb += fogcolor[2] * fog;
1879 R_Mesh_Matrix(&r_identitymatrix);
1881 memset(&m, 0, sizeof(m));
1882 m.tex[0] = R_GetTexture(tex->texture);
1883 m.pointer_texcoord[0] = particle_texcoord2f;
1884 m.pointer_vertex = particle_vertex3f;
1887 GL_Color(cr, cg, cb, ca);
1889 if (p->blendmode == 0)
1890 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1891 else if (p->blendmode == 1)
1892 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1894 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1895 GL_DepthMask(false);
1898 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1900 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1903 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1905 VectorNegate(p->vel2, v);
1906 VectorVectors(v, right, up);
1909 VectorVectors(p->vel2, right, up);
1910 VectorScale(right, p->scalex, right);
1911 VectorScale(up, p->scaley, up);
1915 VectorScale(r_viewleft, -p->scalex, right);
1916 VectorScale(r_viewup, p->scaley, up);
1918 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1919 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1920 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1921 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1922 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1923 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1924 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1925 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1926 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1927 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1928 particle_vertex3f[10] = org[1] + right[1] - up[1];
1929 particle_vertex3f[11] = org[2] + right[2] - up[2];
1930 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1931 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1932 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1933 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1935 else if (p->orientation == PARTICLE_SPARK)
1937 VectorMA(p->org, -p->scaley, p->vel, v);
1938 VectorMA(p->org, p->scaley, p->vel, up2);
1939 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1940 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1941 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1942 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1943 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1945 else if (p->orientation == PARTICLE_BEAM)
1947 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1948 VectorSubtract(p->vel2, p->org, up);
1949 VectorNormalizeFast(up);
1950 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1951 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1952 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1953 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1954 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1955 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1958 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1961 if (p->blendmode == 0)
1962 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1963 else if (p->blendmode == 1)
1964 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1966 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1967 glColor4f(cr, cg, cb, ca);
1969 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1970 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1971 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1972 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1975 R_Mesh_Draw(4, 2, polygonelements);
1979 void R_DrawParticles (void)
1982 float minparticledist;
1985 #ifdef WORKINGLQUAKE
1989 // LordHavoc: early out conditions
1990 if ((!cl_numparticles) || (!r_drawparticles.integer))
1993 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1995 #ifdef WORKINGLQUAKE
1996 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1998 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2000 // LordHavoc: only render if not too close
2001 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2002 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2005 glDisable(GL_BLEND);
2006 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2008 // LordHavoc: only render if not too close
2009 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2014 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2015 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);