2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
25 #define NUMVERTEXNORMALS 162
26 siextern float r_avertexnormals[NUMVERTEXNORMALS][3];
27 #define m_bytenormals r_avertexnormals
28 #define VectorNormalizeFast VectorNormalize
29 #define CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
30 typedef unsigned char qbyte;
31 #define cl_stainmaps.integer 0
32 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
35 #define CL_EntityParticles R_EntityParticles
36 #define CL_ReadPointFile_f R_ReadPointFile_f
37 #define CL_ParseParticleEffect R_ParseParticleEffect
38 #define CL_ParticleExplosion R_ParticleExplosion
39 #define CL_ParticleExplosion2 R_ParticleExplosion2
40 #define CL_BlobExplosion R_BlobExplosion
41 #define CL_RunParticleEffect R_RunParticleEffect
42 #define CL_LavaSplash R_LavaSplash
43 #define CL_RocketTrail2 R_RocketTrail2
44 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
46 vec3_t right1, right2, diff, normal;
48 VectorSubtract (org2, org1, normal);
49 VectorNormalizeFast (normal);
51 // calculate 'right' vector for start
52 VectorSubtract (r_vieworigin, org1, diff);
53 VectorNormalizeFast (diff);
54 CrossProduct (normal, diff, right1);
56 // calculate 'right' vector for end
57 VectorSubtract (r_vieworigin, org2, diff);
58 VectorNormalizeFast (diff);
59 CrossProduct (normal, diff, right2);
61 vert[ 0] = org1[0] + width * right1[0];
62 vert[ 1] = org1[1] + width * right1[1];
63 vert[ 2] = org1[2] + width * right1[2];
64 vert[ 3] = org1[0] - width * right1[0];
65 vert[ 4] = org1[1] - width * right1[1];
66 vert[ 5] = org1[2] - width * right1[2];
67 vert[ 6] = org2[0] - width * right2[0];
68 vert[ 7] = org2[1] - width * right2[1];
69 vert[ 8] = org2[2] - width * right2[2];
70 vert[ 9] = org2[0] + width * right2[0];
71 vert[10] = org2[1] + width * right2[1];
72 vert[11] = org2[2] + width * right2[2];
74 void fractalnoise(qbyte *noise, int size, int startgrid)
76 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
78 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
80 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
81 if (size != (1 << sizepower))
82 Sys_Error("fractalnoise: size must be power of 2\n");
84 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
85 if (startgrid != (1 << gridpower))
86 Sys_Error("fractalnoise: grid must be power of 2\n");
88 startgrid = bound(0, startgrid, size);
90 amplitude = 0xFFFF; // this gets halved before use
91 noisebuf = malloc(size*size*sizeof(int));
92 memset(noisebuf, 0, size*size*sizeof(int));
94 for (g2 = startgrid;g2;g2 >>= 1)
96 // brownian motion (at every smaller level there is random behavior)
98 for (y = 0;y < size;y += g2)
99 for (x = 0;x < size;x += g2)
100 n(x,y) += (rand()&litude);
105 // subdivide, diamond-square algorithm (really this has little to do with squares)
107 for (y = 0;y < size;y += g2)
108 for (x = 0;x < size;x += g2)
109 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
111 for (y = 0;y < size;y += g2)
112 for (x = 0;x < size;x += g2)
114 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
115 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
119 // find range of noise values
121 for (y = 0;y < size;y++)
122 for (x = 0;x < size;x++)
124 if (n(x,y) < min) min = n(x,y);
125 if (n(x,y) > max) max = n(x,y);
129 // normalize noise and copy to output
130 for (y = 0;y < size;y++)
131 for (x = 0;x < size;x++)
132 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
136 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
140 right[0] = forward[2];
141 right[1] = -forward[0];
142 right[2] = forward[1];
144 d = DotProduct(forward, right);
145 right[0] -= d * forward[0];
146 right[1] -= d * forward[1];
147 right[2] -= d * forward[2];
148 VectorNormalizeFast(right);
149 CrossProduct(right, forward, up);
153 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
155 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int hitbmodels, void **hitent, int hitsupercontentsmask)
162 memset (&trace, 0, sizeof(trace));
164 VectorCopy (end, trace.endpos);
166 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
168 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
170 VectorCopy(trace.endpos, impact);
171 VectorCopy(trace.plane.normal, normal);
172 return trace.fraction;
175 #include "cl_collision.h"
178 #define MAX_PARTICLES 32768 // default max # of particles at one time
179 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
183 pt_dead, pt_static, pt_rain, pt_bubble, pt_blood, pt_grow, pt_decal, pt_decalfade, pt_ember
189 PARTICLE_BILLBOARD = 0,
191 PARTICLE_ORIENTED_DOUBLESIDED = 2,
204 typedef struct particle_s
215 float alpha; // 0-255
216 float alphafade; // how much alpha reduces per second
217 float time2; // used for various things (snow fluttering, for example)
218 float bounce; // how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide, 2 = keep bouncing forever, 1.5 is typical)
219 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
221 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
222 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
223 float pressure; // if non-zero, apply pressure to other particles
225 #ifndef WORKINGLQUAKE
226 entity_render_t *owner; // decal stuck to this entity
227 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
228 vec3_t relativeorigin; // decal at this location in entity's coordinate space
229 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
234 static int particlepalette[256] =
236 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
237 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
238 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
239 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
240 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
241 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
242 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
243 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
244 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
245 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
246 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
247 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
248 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
249 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
250 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
251 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
252 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
253 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
254 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
255 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
256 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
257 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
258 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
259 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
260 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
261 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
262 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
263 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
264 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
265 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
266 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
267 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
270 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
272 // texture numbers in particle font
273 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
274 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
275 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
276 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
277 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
278 static const int tex_particle = 63;
279 static const int tex_bubble = 62;
280 static const int tex_raindrop = 61;
281 static const int tex_beam = 60;
283 static int cl_maxparticles;
284 static int cl_numparticles;
285 static int cl_freeparticle;
286 static particle_t *particles;
288 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
289 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
290 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
291 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
292 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
293 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
294 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
295 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
296 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
297 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
298 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
299 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
300 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
301 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
302 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
303 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
305 #ifndef WORKINGLQUAKE
306 static mempool_t *cl_part_mempool;
309 void CL_Particles_Clear(void)
320 void CL_ReadPointFile_f (void);
321 void CL_Particles_Init (void)
325 i = COM_CheckParm ("-particles");
327 if (i && i < com_argc - 1)
329 cl_maxparticles = (int)(atoi(com_argv[i+1]));
330 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
331 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
334 cl_maxparticles = MAX_PARTICLES;
336 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
338 Cvar_RegisterVariable (&cl_particles);
339 Cvar_RegisterVariable (&cl_particles_quality);
340 Cvar_RegisterVariable (&cl_particles_size);
341 Cvar_RegisterVariable (&cl_particles_bloodshowers);
342 Cvar_RegisterVariable (&cl_particles_blood);
343 Cvar_RegisterVariable (&cl_particles_blood_alpha);
344 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
345 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
346 Cvar_RegisterVariable (&cl_particles_smoke);
347 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
348 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
349 Cvar_RegisterVariable (&cl_particles_sparks);
350 Cvar_RegisterVariable (&cl_particles_bubbles);
351 Cvar_RegisterVariable (&cl_decals);
352 Cvar_RegisterVariable (&cl_decals_time);
353 Cvar_RegisterVariable (&cl_decals_fadetime);
356 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
358 cl_part_mempool = Mem_AllocPool("CL_Part");
359 particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
365 // list of all 26 parameters:
366 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
367 // porientation - PARTICLE_ enum values (PARTICLE_BILLBOARD, PARTICLE_SPARK, etc)
368 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
369 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
370 // plight - no longer used (this used to turn on particle lighting)
371 // pblendmode - PBLEND_ enum values (PBLEND_ALPHA, PBLEND_ADD, etc)
372 // pscalex,pscaley - width and height of particle (according to orientation), these are normally the same except when making sparks and beams
373 // palpha - opacity of particle as 0-255 (can be more than 255)
374 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
375 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
376 // pgravity - how much effect gravity has on the particle (0-1)
377 // 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
378 // px,py,pz - starting origin of particle
379 // pvx,pvy,pvz - starting velocity of particle
380 // ptime2 - extra time parameter for certain particle types (pt_decal delayed fades and pt_rain snowflutter use this)
381 // pvx2,pvy2,pvz2 - for PARTICLE_ORIENTED_DOUBLESIDED this is the surface normal of the orientation (forward vector), pt_rain uses this for snow fluttering
382 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
383 // 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
384 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)
387 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
388 ptempcolor = (pcolor1);
389 ptempcolor2 = (pcolor2);
390 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
391 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
392 pcb2 = (ptempcolor2) & 0xFF;
393 if (ptempcolor != ptempcolor2)
395 pcr1 = ((ptempcolor) >> 16) & 0xFF;
396 pcg1 = ((ptempcolor) >> 8) & 0xFF;
397 pcb1 = (ptempcolor) & 0xFF;
398 ptempcolor = rand() & 0xFF;
399 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
400 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
401 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
403 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
404 if (cl_freeparticle >= cl_maxparticles)
406 part = &particles[cl_freeparticle++];
407 if (cl_numparticles < cl_freeparticle)
408 cl_numparticles = cl_freeparticle;
409 memset(part, 0, sizeof(*part));
410 part->type = (ptype);
411 part->color[0] = pcr2;
412 part->color[1] = pcg2;
413 part->color[2] = pcb2;
414 part->color[3] = 0xFF;
415 part->orientation = porientation;
417 part->blendmode = pblendmode;
418 part->scalex = (pscalex);
419 part->scaley = (pscaley);
420 part->alpha = (palpha);
421 part->alphafade = (palphafade);
422 part->die = cl.time + (ptime);
423 part->gravity = (pgravity);
424 part->bounce = (pbounce);
428 part->vel[0] = (pvx);
429 part->vel[1] = (pvy);
430 part->vel[2] = (pvz);
431 part->time2 = (ptime2);
432 part->vel2[0] = (pvx2);
433 part->vel2[1] = (pvy2);
434 part->vel2[2] = (pvz2);
435 part->friction = (pfriction);
436 part->pressure = (ppressure);
440 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
443 if (!cl_decals.integer)
445 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);
446 #ifndef WORKINGLQUAKE
450 p->ownermodel = p->owner->model;
451 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
452 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
453 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
458 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
461 float bestfrac, bestorg[3], bestnormal[3];
462 float frac, v[3], normal[3], org2[3];
464 void *besthitent = NULL, *hitent;
466 entity_render_t *besthitent = NULL, *hitent;
469 for (i = 0;i < 32;i++)
472 VectorMA(org, maxdist, org2, org2);
473 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
478 VectorCopy(v, bestorg);
479 VectorCopy(normal, bestnormal);
483 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
491 void CL_EntityParticles (entity_t *ent)
495 float sp, sy, cp, cy;
499 static vec3_t avelocities[NUMVERTEXNORMALS];
500 if (!cl_particles.integer) return;
505 if (!avelocities[0][0])
506 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
507 avelocities[0][i] = (rand()&255) * 0.01;
509 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
511 angle = cl.time * avelocities[i][0];
514 angle = cl.time * avelocities[i][1];
523 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);
525 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);
531 void CL_ReadPointFile_f (void)
535 char *pointfile = NULL, *pointfilepos, *t, tchar;
536 char name[MAX_OSPATH];
541 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
542 strlcat (name, ".pts", sizeof (name));
544 pointfile = COM_LoadTempFile (name);
546 pointfile = FS_LoadFile(name, tempmempool, true);
550 Con_Printf("Could not open %s\n", name);
554 Con_Printf("Reading %s...\n", name);
557 pointfilepos = pointfile;
558 while (*pointfilepos)
560 while (*pointfilepos == '\n' || *pointfilepos == '\r')
565 while (*t && *t != '\n' && *t != '\r')
569 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
575 VectorCopy(org, leakorg);
578 if (cl_numparticles < cl_maxparticles - 3)
581 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);
584 #ifndef WORKINGLQUAKE
587 VectorCopy(leakorg, org);
588 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
590 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);
591 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);
592 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);
597 CL_ParseParticleEffect
599 Parse an effect out of the server message
602 void CL_ParseParticleEffect (void)
605 int i, count, msgcount, color;
608 for (i=0 ; i<3 ; i++)
609 dir[i] = MSG_ReadChar () * (1.0/16);
610 msgcount = MSG_ReadByte ();
611 color = MSG_ReadByte ();
618 if (cl_particles_blood_bloodhack.integer)
623 CL_BloodPuff(org, dir, count / 2);
629 CL_BloodPuff(org, dir, count / 2);
633 CL_RunParticleEffect (org, dir, color, count);
642 void CL_ParticleExplosion (vec3_t org)
647 if (cl_stainmaps.integer)
648 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
649 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
651 i = CL_PointQ1Contents(org);
652 if ((i == CONTENTS_SLIME || i == CONTENTS_WATER) && cl_particles.integer && cl_particles_bubbles.integer)
654 for (i = 0;i < 128 * cl_particles_quality.value;i++)
655 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);
660 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
662 if (cl_particles.integer && cl_particles_smoke.integer)
664 for (i = 0;i < 64;i++)
667 v2[0] = lhrandom(-64, 64);
668 v2[1] = lhrandom(-64, 64);
669 v2[2] = lhrandom(-8, 24);
671 for (k = 0;k < 16;k++)
673 v[0] = org[0] + lhrandom(-64, 64);
674 v[1] = org[1] + lhrandom(-64, 64);
675 v[2] = org[2] + lhrandom(-8, 24);
676 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
679 VectorSubtract(v2, org, v2);
681 VectorScale(v2, 2.0f, v2);
682 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);
688 if (cl_particles.integer && cl_particles_sparks.integer)
689 for (i = 0;i < 128 * cl_particles_quality.value;i++)
690 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);
693 //if (cl_explosions.integer)
694 // R_NewExplosion(org);
696 if (cl_particles.integer && cl_particles_sparks.integer)
697 for (i = 0;i < 64 * cl_particles_quality.value;i++)
698 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);
701 //if (cl_explosions.integer)
702 // R_NewExplosion(org);
704 if (cl_particles.integer && cl_particles_sparks.integer)
707 for (i = 0;i < 256 * cl_particles_quality.value;i++)
709 k = particlepalette[0x68 + (rand() & 7)];
710 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.2, 0);
715 if (cl_explosions.integer)
722 CL_ParticleExplosion2
726 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
731 if (!cl_particles.integer) return;
733 for (i = 0;i < 512 * cl_particles_quality.value;i++)
735 VectorRandom (offset);
736 VectorScale (offset, 192, vel);
737 VectorScale (offset, 8, offset);
738 k = particlepalette[colorStart + (i % colorLength)];
739 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] + offset[0], org[1] + offset[1], org[2] + offset[2], vel[0], vel[1], vel[2], 0, 0, 0, 0, 1, 0);
749 void CL_BlobExplosion (vec3_t org)
751 if (cl_stainmaps.integer)
752 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
753 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
755 if (cl_explosions.integer)
765 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
771 CL_ParticleExplosion(org);
774 if (!cl_particles.integer) return;
775 count *= cl_particles_quality.value;
778 k = particlepalette[color + (rand()&7)];
779 if (gamemode == GAME_GOODVSBAD2)
780 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);
782 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);
786 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
792 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
797 if (cl_stainmaps.integer)
798 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
799 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
801 if (!cl_particles.integer) return;
803 if (cl_particles_bulletimpacts.integer)
806 if (cl_particles_smoke.integer)
808 k = count * 0.25 * cl_particles_quality.value;
811 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
812 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
813 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
814 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
815 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);
819 if (cl_particles_sparks.integer)
822 count *= cl_particles_quality.value;
825 k = particlepalette[0x68 + (rand() & 7)];
826 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);
832 void CL_PlasmaBurn (vec3_t org)
834 if (cl_stainmaps.integer)
835 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
836 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
839 static float bloodcount = 0;
840 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
844 // bloodcount is used to accumulate counts too small to cause a blood particle
845 if (!cl_particles.integer) return;
846 if (!cl_particles_blood.integer) return;
853 while(bloodcount > 0)
855 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
856 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
857 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
858 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
859 particle(pt_blood, PARTICLE_BILLBOARD, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], true, PBLEND_MOD, 8, 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 9999, 0, -1, org3[0], org3[1], org3[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 0, 0, 0, 0, 1, 0);
860 bloodcount -= 16 / cl_particles_quality.value;
864 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
866 vec3_t org, vel, diff, center, velscale;
867 if (!cl_particles.integer) return;
868 if (!cl_particles_bloodshowers.integer) return;
869 if (!cl_particles_blood.integer) return;
871 VectorSubtract(maxs, mins, diff);
872 center[0] = (mins[0] + maxs[0]) * 0.5;
873 center[1] = (mins[1] + maxs[1]) * 0.5;
874 center[2] = (mins[2] + maxs[2]) * 0.5;
875 velscale[0] = velspeed * 2.0 / diff[0];
876 velscale[1] = velspeed * 2.0 / diff[1];
877 velscale[2] = velspeed * 2.0 / diff[2];
879 bloodcount += count * 5.0f;
880 while (bloodcount > 0)
882 org[0] = lhrandom(mins[0], maxs[0]);
883 org[1] = lhrandom(mins[1], maxs[1]);
884 org[2] = lhrandom(mins[2], maxs[2]);
885 vel[0] = (org[0] - center[0]) * velscale[0];
886 vel[1] = (org[1] - center[1]) * velscale[1];
887 vel[2] = (org[2] - center[2]) * velscale[2];
888 bloodcount -= 16 / cl_particles_quality.value;
889 particle(pt_blood, PARTICLE_BILLBOARD, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], true, PBLEND_MOD, 8, 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 9999, 0, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 0, 0, 0, 0, 1, 0);
893 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
897 if (!cl_particles.integer) return;
898 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
899 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
900 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
902 count *= cl_particles_quality.value;
905 k = particlepalette[colorbase + (rand()&3)];
906 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ALPHA, 2, 2, 255 / cl_particles_quality.value, 0, lhrandom(1, 2), gravity ? 1 : 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0] + lhrandom(-randomvel, randomvel), dir[1] + lhrandom(-randomvel, randomvel), dir[2] + lhrandom(-randomvel, randomvel), 0, 0, 0, 0, 0, 0);
910 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
913 float t, z, minz, maxz;
914 if (!cl_particles.integer) return;
915 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
916 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
917 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
918 if (dir[2] < 0) // falling
920 t = (maxs[2] - mins[2]) / -dir[2];
925 t = (maxs[2] - mins[2]) / dir[2];
928 if (t < 0 || t > 2) // sanity check
931 minz = z - fabs(dir[2]) * 0.1;
932 maxz = z + fabs(dir[2]) * 0.1;
933 minz = bound(mins[2], minz, maxs[2]);
934 maxz = bound(mins[2], maxz, maxs[2]);
936 count *= cl_particles_quality.value;
941 count *= 4; // ick, this should be in the mod or maps?
945 k = particlepalette[colorbase + (rand()&3)];
946 if (gamemode == GAME_GOODVSBAD2)
948 particle(pt_rain, PARTICLE_SPARK, k, k, tex_particle, true, PBLEND_ADD, 20, 20, lhrandom(8, 16) / cl_particles_quality.value, 0, t, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], cl.time + 9999, dir[0], dir[1], dir[2], 0, 0);
952 particle(pt_rain, PARTICLE_SPARK, k, k, tex_particle, true, PBLEND_ADD, 0.5, 0.02, lhrandom(8, 16) / cl_particles_quality.value, 0, t, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], cl.time + 9999, dir[0], dir[1], dir[2], 0, 0);
959 k = particlepalette[colorbase + (rand()&3)];
960 if (gamemode == GAME_GOODVSBAD2)
962 particle(pt_rain, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, 20, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, t, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, dir[0], dir[1], dir[2], 0, 0);
966 particle(pt_rain, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, 1, 1, lhrandom(64, 128) / cl_particles_quality.value, 0, t, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, dir[0], dir[1], dir[2], 0, 0);
971 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
975 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
980 if (!cl_particles.integer) return;
982 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
983 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
984 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
986 center[0] = (mins[0] + maxs[0]) * 0.5f;
987 center[1] = (mins[1] + maxs[1]) * 0.5f;
988 center[2] = (mins[2] + maxs[2]) * 0.5f;
990 count *= cl_particles_quality.value;
993 k = particlepalette[224 + (rand()&15)];
994 o[0] = lhrandom(mins[0], maxs[0]);
995 o[1] = lhrandom(mins[1], maxs[1]);
996 o[2] = lhrandom(mins[2], maxs[2]);
997 VectorSubtract(o, center, v);
998 VectorNormalizeFast(v);
999 VectorScale(v, 100, v);
1000 v[2] += sv_gravity.value * 0.15f;
1001 particle(pt_static, PARTICLE_BILLBOARD, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, 1.5, 1.5, lhrandom(64, 128) / cl_particles_quality.value, 128 / cl_particles_quality.value, 9999, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0, 0, 0, 0, 0.2, 0);
1005 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
1009 if (!cl_particles.integer) return;
1010 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1011 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1012 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1014 count *= cl_particles_quality.value;
1017 k = particlepalette[224 + (rand()&15)];
1018 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, 4, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, 9999, -1, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(0, 64), 0, 0, 0, 0, 1, 0);
1020 particle(pt_static, PARTICLE_BILLBOARD, 0x303030, 0x606060, tex_smoke[rand()&7], false, PBLEND_ADD, 6, 6, lhrandom(48, 96) / cl_particles_quality.value, 64 / cl_particles_quality.value, 9999, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 32), 0, 0, 0, 0, 0, 0);
1024 void CL_Flames (vec3_t org, vec3_t vel, int count)
1027 if (!cl_particles.integer) return;
1029 count *= cl_particles_quality.value;
1032 k = particlepalette[224 + (rand()&15)];
1033 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, 4, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, 9999, -1, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 0, 0, 0, 0, 1, 0);
1045 void CL_LavaSplash (vec3_t origin)
1047 float i, j, inc, vel;
1050 if (!cl_particles.integer) return;
1052 inc = 32 / cl_particles_quality.value;
1053 for (i = -128;i < 128;i += inc)
1055 for (j = -128;j < 128;j += inc)
1057 dir[0] = j + lhrandom(0, 8);
1058 dir[1] = i + lhrandom(0, 8);
1060 org[0] = origin[0] + dir[0];
1061 org[1] = origin[1] + dir[1];
1062 org[2] = origin[2] + lhrandom(0, 64);
1063 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1064 if (gamemode == GAME_GOODVSBAD2)
1066 k = particlepalette[0 + (rand()&255)];
1067 l = particlepalette[0 + (rand()&255)];
1068 particle(pt_static, PARTICLE_BILLBOARD, k, l, tex_particle, false, PBLEND_ADD, 12, 12, inc * 8, inc * 8, 9999, 0.05, 1, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, 0, 0);
1072 k = l = particlepalette[224 + (rand()&7)];
1073 particle(pt_static, PARTICLE_BILLBOARD, k, l, tex_particle, false, PBLEND_ADD, 12, 12, inc * 8, inc * 8, 9999, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, 0, 0);
1086 void R_TeleportSplash (vec3_t org)
1089 if (!cl_particles.integer) return;
1091 inc = 8 / cl_particles_quality.value;
1092 for (i = -16;i < 16;i += inc)
1093 for (j = -16;j < 16;j += inc)
1094 for (k = -24;k < 32;k += inc)
1095 particle(pt_static, PARTICLE_BILLBOARD, 0xA0A0A0, 0xFFFFFF, tex_particle, false, PBLEND_ADD, 10, 10, inc * 32, inc * lhrandom(8, 16), inc * 32, 9999, 0, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-256, 256), 0, 0, 0, 0, 1, 0);
1099 #ifdef WORKINGLQUAKE
1100 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1102 void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
1105 vec3_t vec, dir, vel, pos;
1106 float len, dec, speed, qd;
1107 int contents, smoke, blood, bubbles;
1109 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1112 VectorSubtract(end, start, dir);
1113 VectorNormalize(dir);
1115 VectorSubtract (end, start, vec);
1116 #ifdef WORKINGLQUAKE
1117 len = VectorNormalize (vec);
1119 speed = 1.0f / cl.frametime;
1120 VectorSubtract(end, start, vel);
1122 len = VectorNormalizeLength (vec);
1123 dec = -ent->persistent.trail_time;
1124 ent->persistent.trail_time += len;
1125 if (ent->persistent.trail_time < 0.01f)
1128 // if we skip out, leave it reset
1129 ent->persistent.trail_time = 0.0f;
1131 speed = ent->state_current.time - ent->state_previous.time;
1133 speed = 1.0f / speed;
1134 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1136 VectorScale(vel, speed, vel);
1138 // advance into this frame to reach the first puff location
1139 VectorMA(start, dec, vec, pos);
1142 contents = CL_PointQ1Contents(pos);
1143 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1146 smoke = cl_particles.integer && cl_particles_smoke.integer;
1147 blood = cl_particles.integer && cl_particles_blood.integer;
1148 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1149 qd = 1.0f / cl_particles_quality.value;
1155 case 0: // rocket trail
1159 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);
1160 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);
1163 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);
1166 case 1: // grenade trail
1167 // FIXME: make it gradually stop smoking
1170 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);
1175 case 4: // slight blood
1178 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);
1181 case 3: // green tracer
1185 if (gamemode == GAME_GOODVSBAD2)
1186 particle(pt_static, PARTICLE_BILLBOARD, 0x00002E, 0x000030, tex_particle, false, PBLEND_ADD, 6, 6, qd*128, qd*384, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0, 0, 0, 0, 0, 0);
1188 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);
1192 case 5: // flame tracer
1195 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);
1198 case 6: // voor trail
1202 if (gamemode == GAME_GOODVSBAD2)
1203 particle(pt_static, PARTICLE_BILLBOARD, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, false, PBLEND_ALPHA, 6, 6, qd*255, qd*384, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0, 0, 0, 0, 0, 0);
1205 particle(pt_static, PARTICLE_BILLBOARD, 0x502030, 0x502030, tex_particle, false, PBLEND_ADD, 6, 6, qd*128, qd*384, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0, 0, 0, 0, 0, 0);
1209 case 7: // Nehahra smoke tracer
1212 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);
1214 case 8: // Nexuiz plasma trail
1217 particle(pt_static, PARTICLE_BILLBOARD, 0x283880, 0x283880, tex_particle, false, PBLEND_ADD, 4, 4, qd*255, qd*1024, 9999, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1221 // advance to next time and position
1223 VectorMA (pos, dec, vec, pos);
1225 #ifndef WORKINGLQUAKE
1226 ent->persistent.trail_time = len;
1230 void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1234 if (!cl_particles.integer) return;
1235 if (!cl_particles_smoke.integer) return;
1237 VectorCopy(start, pos);
1238 VectorSubtract(end, start, vec);
1239 #ifdef WORKINGLQUAKE
1240 len = VectorNormalize(vec);
1242 len = VectorNormalizeLength(vec);
1244 color = particlepalette[color];
1245 dec = 3.0f / cl_particles_quality.value;
1248 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);
1250 VectorMA(pos, dec, vec, pos);
1254 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1256 int tempcolor2, cr, cg, cb;
1260 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1261 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);
1264 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1267 if (!cl_particles.integer) return;
1270 if (cl_particles_smoke.integer)
1271 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1272 particle(pt_grow, PARTICLE_BILLBOARD, 0x202020, 0x404040, tex_smoke[rand()&7], true, PBLEND_ADD, 5, 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 9999, 0, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count) * 0.5f, dir[1] + lhrandom(-count, count) * 0.5f, dir[2] + lhrandom(-count, count) * 0.5f, 15, 0, 0, 0, 0, 0);
1275 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1278 if (!cl_particles.integer) return;
1280 if (cl_stainmaps.integer)
1281 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1282 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1285 if (cl_particles_smoke.integer)
1286 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1287 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);
1290 if (cl_particles_sparks.integer)
1291 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1292 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);
1300 void CL_MoveParticles (void)
1303 int i, maxparticle, j, a, content;
1304 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1305 #ifdef WORKINGLQUAKE
1308 entity_render_t *hitent;
1311 // LordHavoc: early out condition
1312 if (!cl_numparticles)
1314 cl_freeparticle = 0;
1318 #ifdef WORKINGLQUAKE
1319 frametime = cl.frametime;
1321 frametime = cl.time - cl.oldtime;
1323 gravity = frametime * sv_gravity.value;
1324 dvel = 1+4*frametime;
1325 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1329 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1335 VectorCopy(p->org, p->oldorg);
1336 VectorMA(p->org, frametime, p->vel, p->org);
1337 VectorCopy(p->org, org);
1340 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1342 VectorCopy(v, p->org);
1345 // assume it's blood (lame, but...)
1346 #ifndef WORKINGLQUAKE
1347 if (cl_stainmaps.integer)
1348 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));
1350 if (!cl_decals.integer)
1357 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1358 // convert from a blood particle to a blood decal
1359 p->texnum = tex_blooddecal[rand()&7];
1360 #ifndef WORKINGLQUAKE
1362 p->ownermodel = hitent->model;
1363 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1364 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1365 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1367 p->time2 = cl.time + cl_decals_time.value;
1368 p->die = p->time2 + cl_decals_fadetime.value;
1370 VectorCopy(normal, p->vel2);
1371 VectorClear(p->vel);
1372 VectorAdd(p->org, normal, p->org);
1381 dist = DotProduct(p->vel, normal) * -p->bounce;
1382 VectorMA(p->vel, dist, normal, p->vel);
1383 if (DotProduct(p->vel, p->vel) < 0.03)
1384 VectorClear(p->vel);
1389 p->vel[2] -= p->gravity * gravity;
1391 p->alpha -= p->alphafade * frametime;
1393 if (p->alpha <= 0 || cl.time > p->die)
1401 f = p->friction * frametime;
1403 content = CL_PointQ1Contents(p->org);
1404 if (content != CONTENTS_EMPTY)
1407 VectorScale(p->vel, f, p->vel);
1410 if (p->type != pt_static)
1416 content = CL_PointQ1Contents(p->org);
1418 if (a != CONTENTS_EMPTY)
1420 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1422 p->scalex += frametime * 8;
1423 p->scaley += frametime * 8;
1424 //p->alpha -= bloodwaterfade;
1430 p->vel[2] -= gravity;
1434 content = CL_PointQ1Contents(p->org);
1435 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1442 if (cl.time > p->time2)
1445 p->time2 = cl.time + (rand() & 3) * 0.1;
1446 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1447 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1448 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1451 content = CL_PointQ1Contents(p->org);
1453 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1457 p->scalex += frametime * p->time2;
1458 p->scaley += frametime * p->time2;
1461 #ifndef WORKINGLQUAKE
1462 if (p->owner->model == p->ownermodel)
1464 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1465 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1466 if (cl.time > p->time2)
1468 p->alphafade = p->alpha / (p->die - cl.time);
1469 p->type = pt_decalfade;
1477 #ifndef WORKINGLQUAKE
1478 if (p->owner->model == p->ownermodel)
1480 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1481 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1488 while (cl.time > p->time2)
1491 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);
1495 Con_Printf("unknown particle type %i\n", p->type);
1501 cl_numparticles = maxparticle + 1;
1502 cl_freeparticle = 0;
1505 #define MAX_PARTICLETEXTURES 64
1506 // particletexture_t is a rectangle in the particlefonttexture
1509 rtexture_t *texture;
1510 float s1, t1, s2, t2;
1515 static int particlefonttexture;
1517 static rtexturepool_t *particletexturepool;
1518 static rtexture_t *particlefonttexture;
1520 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1522 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1524 static qbyte shadebubble(float dx, float dy, vec3_t light)
1528 dz = 1 - (dx*dx+dy*dy);
1529 if (dz > 0) // it does hit the sphere
1533 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1534 VectorNormalize(normal);
1535 dot = DotProduct(normal, light);
1536 if (dot > 0.5) // interior reflection
1537 f += ((dot * 2) - 1);
1538 else if (dot < -0.5) // exterior reflection
1539 f += ((dot * -2) - 1);
1541 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1542 VectorNormalize(normal);
1543 dot = DotProduct(normal, light);
1544 if (dot > 0.5) // interior reflection
1545 f += ((dot * 2) - 1);
1546 else if (dot < -0.5) // exterior reflection
1547 f += ((dot * -2) - 1);
1549 f += 16; // just to give it a haze so you can see the outline
1550 f = bound(0, f, 255);
1557 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1559 int basex, basey, y;
1560 basex = ((texnum >> 0) & 7) * 32;
1561 basey = ((texnum >> 3) & 7) * 32;
1562 particletexture[texnum].s1 = (basex + 1) / 256.0f;
1563 particletexture[texnum].t1 = (basey + 1) / 256.0f;
1564 particletexture[texnum].s2 = (basex + 31) / 256.0f;
1565 particletexture[texnum].t2 = (basey + 31) / 256.0f;
1566 for (y = 0;y < 32;y++)
1567 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 4);
1570 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1573 float cx, cy, dx, dy, f, iradius;
1575 cx = lhrandom(radius + 1, 30 - radius);
1576 cy = lhrandom(radius + 1, 30 - radius);
1577 iradius = 1.0f / radius;
1578 alpha *= (1.0f / 255.0f);
1579 for (y = 0;y < 32;y++)
1581 for (x = 0;x < 32;x++)
1585 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1588 d = data + (y * 32 + x) * 4;
1589 d[0] += f * (red - d[0]);
1590 d[1] += f * (green - d[1]);
1591 d[2] += f * (blue - d[2]);
1597 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1600 for (i = 0;i < 32*32;i++, data += 4)
1602 data[0] = bound(minr, data[0], maxr);
1603 data[1] = bound(ming, data[1], maxg);
1604 data[2] = bound(minb, data[2], maxb);
1608 void particletextureinvert(qbyte *data)
1611 for (i = 0;i < 32*32;i++, data += 4)
1613 data[0] = 255 - data[0];
1614 data[1] = 255 - data[1];
1615 data[2] = 255 - data[2];
1619 static void R_InitParticleTexture (void)
1621 int x, y, d, i, j, k, m;
1622 float dx, dy, radius, f, f2;
1623 qbyte data[32][32][4], noise1[64][64], noise2[64][64], data2[64][16][4];
1625 qbyte particletexturedata[256*256*4];
1627 // a note: decals need to modulate (multiply) the background color to
1628 // properly darken it (stain), and they need to be able to alpha fade,
1629 // this is a very difficult challenge because it means fading to white
1630 // (no change to background) rather than black (darkening everything
1631 // behind the whole decal polygon), and to accomplish this the texture is
1632 // inverted (dark red blood on white background becomes brilliant cyan
1633 // and white on black background) so we can alpha fade it to black, then
1634 // we invert it again during the blendfunc to make it work...
1636 memset(particletexturedata, 255, sizeof(particletexturedata));
1639 for (i = 0;i < 8;i++)
1641 memset(&data[0][0][0], 255, sizeof(data));
1644 fractalnoise(&noise1[0][0], 64, 4);
1645 fractalnoise(&noise2[0][0], 64, 8);
1647 for (y = 0;y < 32;y++)
1650 for (x = 0;x < 32;x++)
1653 d = (noise2[y][x] - 128) * 3 + 192;
1655 d = d * (256 - (int) (dx*dx+dy*dy)) / 256;
1656 d = (d * noise1[y][x]) >> 7;
1657 d = bound(0, d, 255);
1658 data[y][x][3] = (qbyte) d;
1665 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1669 for (i = 0;i < 16;i++)
1671 memset(&data[0][0][0], 255, sizeof(data));
1672 radius = i * 3.0f / 16.0f;
1673 f2 = 255.0f * ((15.0f - i) / 15.0f);
1674 for (y = 0;y < 32;y++)
1676 dy = (y - 16) * 0.25f;
1677 for (x = 0;x < 32;x++)
1679 dx = (x - 16) * 0.25f;
1680 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
1681 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1684 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1688 memset(&data[0][0][0], 255, sizeof(data));
1689 for (y = 0;y < 32;y++)
1692 for (x = 0;x < 32;x++)
1695 d = (256 - (dx*dx+dy*dy));
1696 d = bound(0, d, 255);
1697 data[y][x][3] = (qbyte) d;
1700 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1703 memset(&data[0][0][0], 255, sizeof(data));
1704 light[0] = 1;light[1] = 1;light[2] = 1;
1705 VectorNormalize(light);
1706 for (y = 0;y < 32;y++)
1707 for (x = 0;x < 32;x++)
1708 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);
1709 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1712 memset(&data[0][0][0], 255, sizeof(data));
1713 light[0] = 1;light[1] = 1;light[2] = 1;
1714 VectorNormalize(light);
1715 for (y = 0;y < 32;y++)
1716 for (x = 0;x < 32;x++)
1717 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
1718 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1721 for (i = 0;i < 8;i++)
1723 memset(&data[0][0][0], 255, sizeof(data));
1724 for (k = 0;k < 24;k++)
1725 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 160);
1726 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1727 particletextureinvert(&data[0][0][0]);
1728 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1732 for (i = 0;i < 8;i++)
1734 memset(&data[0][0][0], 255, sizeof(data));
1735 for (k = 0;k < 24;k++)
1736 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 96);
1737 for (j = 3;j < 7;j++)
1738 for (k = 0, m = rand() % 12;k < m;k++)
1739 particletextureblotch(&data[0][0][0], j, 96, 0, 0, 192);
1740 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1741 particletextureinvert(&data[0][0][0]);
1742 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1746 for (i = 0;i < 8;i++)
1748 memset(&data[0][0][0], 255, sizeof(data));
1749 for (k = 0;k < 12;k++)
1750 particletextureblotch(&data[0][0][0], 2, 0, 0, 0, 128);
1751 for (k = 0;k < 3;k++)
1752 particletextureblotch(&data[0][0][0], 14, 0, 0, 0, 160);
1753 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1754 particletextureinvert(&data[0][0][0]);
1755 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1759 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1760 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1761 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1763 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1764 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1765 particletexture[i].texture = particlefonttexture;
1768 fractalnoise(&noise1[0][0], 64, 4);
1770 for (y = 0;y < 64;y++)
1772 for (x = 0;x < 16;x++)
1778 d = d * d * noise1[y][x] / (7 * 7);
1779 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1780 data2[y][x][3] = 255;
1784 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "beam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1785 particletexture[tex_beam].s1 = 0;
1786 particletexture[tex_beam].t1 = 0;
1787 particletexture[tex_beam].s2 = 1;
1788 particletexture[tex_beam].t2 = 1;
1792 static void r_part_start(void)
1794 particletexturepool = R_AllocTexturePool();
1795 R_InitParticleTexture ();
1798 static void r_part_shutdown(void)
1800 R_FreeTexturePool(&particletexturepool);
1803 static void r_part_newmap(void)
1805 cl_numparticles = 0;
1806 cl_freeparticle = 0;
1809 void R_Particles_Init (void)
1811 Cvar_RegisterVariable(&r_drawparticles);
1812 #ifdef WORKINGLQUAKE
1815 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1819 #ifdef WORKINGLQUAKE
1820 void R_InitParticles(void)
1822 CL_Particles_Init();
1827 float particle_vertex3f[12], particle_texcoord2f[8];
1829 #ifdef WORKINGLQUAKE
1830 void R_DrawParticle(particle_t *p)
1833 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1835 const particle_t *p = calldata1;
1838 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1839 particletexture_t *tex;
1841 VectorCopy(p->org, org);
1843 tex = &particletexture[p->texnum];
1844 cr = p->color[0] * (1.0f / 255.0f);
1845 cg = p->color[1] * (1.0f / 255.0f);
1846 cb = p->color[2] * (1.0f / 255.0f);
1847 ca = p->alpha * (1.0f / 255.0f);
1848 if (p->blendmode == PBLEND_MOD)
1859 #ifndef WORKINGLQUAKE
1860 if (fogenabled && p->blendmode != PBLEND_MOD)
1862 VectorSubtract(org, r_vieworigin, fogvec);
1863 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1868 if (p->blendmode == 0)
1870 cr += fogcolor[0] * fog;
1871 cg += fogcolor[1] * fog;
1872 cb += fogcolor[2] * fog;
1876 R_Mesh_Matrix(&r_identitymatrix);
1878 memset(&m, 0, sizeof(m));
1879 m.tex[0] = R_GetTexture(tex->texture);
1880 m.pointer_texcoord[0] = particle_texcoord2f;
1881 m.pointer_vertex = particle_vertex3f;
1884 GL_Color(cr, cg, cb, ca);
1886 if (p->blendmode == 0)
1887 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1888 else if (p->blendmode == 1)
1889 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1891 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1892 GL_DepthMask(false);
1895 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1897 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1900 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1902 VectorNegate(p->vel2, v);
1903 VectorVectors(v, right, up);
1906 VectorVectors(p->vel2, right, up);
1907 VectorScale(right, p->scalex, right);
1908 VectorScale(up, p->scaley, up);
1912 VectorScale(r_viewleft, -p->scalex, right);
1913 VectorScale(r_viewup, p->scaley, up);
1915 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1916 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1917 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1918 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1919 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1920 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1921 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1922 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1923 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1924 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1925 particle_vertex3f[10] = org[1] + right[1] - up[1];
1926 particle_vertex3f[11] = org[2] + right[2] - up[2];
1927 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1928 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1929 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1930 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1932 else if (p->orientation == PARTICLE_SPARK)
1934 VectorMA(p->org, -p->scaley, p->vel, v);
1935 VectorMA(p->org, p->scaley, p->vel, up2);
1936 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1937 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1938 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1939 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1940 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1942 else if (p->orientation == PARTICLE_BEAM)
1944 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1945 VectorSubtract(p->vel2, p->org, up);
1946 VectorNormalizeFast(up);
1947 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1948 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1949 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1950 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1951 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1952 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1955 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1958 if (p->blendmode == 0)
1959 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1960 else if (p->blendmode == 1)
1961 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1963 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1964 glColor4f(cr, cg, cb, ca);
1966 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1967 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1968 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1969 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1972 R_Mesh_Draw(4, 2, polygonelements);
1976 void R_DrawParticles (void)
1979 float minparticledist;
1982 #ifdef WORKINGLQUAKE
1986 // LordHavoc: early out conditions
1987 if ((!cl_numparticles) || (!r_drawparticles.integer))
1990 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1992 #ifdef WORKINGLQUAKE
1993 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1995 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1997 // LordHavoc: only render if not too close
1998 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1999 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2002 glDisable(GL_BLEND);
2003 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2005 // LordHavoc: only render if not too close
2006 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2011 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2012 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);