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)
705 for (i = 0;i < 256 * cl_particles_quality.value;i++)
706 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);
709 if (cl_explosions.integer)
716 CL_ParticleExplosion2
720 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
726 if (!cl_particles.integer) return;
728 for (i = 0;i < 512 * cl_particles_quality.value;i++)
730 VectorRandom (offset);
731 VectorScale (offset, 192, vel);
732 VectorScale (offset, 8, offset);
733 k = particlepalette[colorStart + (i % colorLength)];
734 pscale = lhrandom(0.5, 1.5);
735 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);
745 void CL_BlobExplosion (vec3_t org)
747 if (cl_stainmaps.integer)
748 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
749 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
751 if (cl_explosions.integer)
761 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
767 CL_ParticleExplosion(org);
770 if (!cl_particles.integer) return;
771 count *= cl_particles_quality.value;
774 k = particlepalette[color + (rand()&7)];
775 if (gamemode == GAME_GOODVSBAD2)
776 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);
778 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);
782 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
788 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
793 if (cl_stainmaps.integer)
794 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
795 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
797 if (!cl_particles.integer) return;
799 if (cl_particles_bulletimpacts.integer)
802 if (cl_particles_smoke.integer)
804 k = count * 0.25 * cl_particles_quality.value;
807 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
808 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
809 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
810 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
811 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);
815 if (cl_particles_sparks.integer)
818 count *= cl_particles_quality.value;
821 k = particlepalette[0x68 + (rand() & 7)];
822 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);
828 void CL_PlasmaBurn (vec3_t org)
830 if (cl_stainmaps.integer)
831 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
832 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
835 static float bloodcount = 0;
836 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
840 // bloodcount is used to accumulate counts too small to cause a blood particle
841 if (!cl_particles.integer) return;
842 if (!cl_particles_blood.integer) return;
849 while(bloodcount > 0)
851 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
852 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
853 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
854 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
855 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);
856 bloodcount -= 16 / cl_particles_quality.value;
860 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
862 vec3_t org, vel, diff, center, velscale;
863 if (!cl_particles.integer) return;
864 if (!cl_particles_bloodshowers.integer) return;
865 if (!cl_particles_blood.integer) return;
867 VectorSubtract(maxs, mins, diff);
868 center[0] = (mins[0] + maxs[0]) * 0.5;
869 center[1] = (mins[1] + maxs[1]) * 0.5;
870 center[2] = (mins[2] + maxs[2]) * 0.5;
871 velscale[0] = velspeed * 2.0 / diff[0];
872 velscale[1] = velspeed * 2.0 / diff[1];
873 velscale[2] = velspeed * 2.0 / diff[2];
875 bloodcount += count * 5.0f;
876 while (bloodcount > 0)
878 org[0] = lhrandom(mins[0], maxs[0]);
879 org[1] = lhrandom(mins[1], maxs[1]);
880 org[2] = lhrandom(mins[2], maxs[2]);
881 vel[0] = (org[0] - center[0]) * velscale[0];
882 vel[1] = (org[1] - center[1]) * velscale[1];
883 vel[2] = (org[2] - center[2]) * velscale[2];
884 bloodcount -= 16 / cl_particles_quality.value;
885 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);
889 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
893 if (!cl_particles.integer) return;
894 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
895 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
896 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
898 count *= cl_particles_quality.value;
901 k = particlepalette[colorbase + (rand()&3)];
902 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);
906 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
909 float t, z, minz, maxz;
910 if (!cl_particles.integer) return;
911 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
912 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
913 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
914 if (dir[2] < 0) // falling
916 t = (maxs[2] - mins[2]) / -dir[2];
921 t = (maxs[2] - mins[2]) / dir[2];
924 if (t < 0 || t > 2) // sanity check
927 minz = z - fabs(dir[2]) * 0.1;
928 maxz = z + fabs(dir[2]) * 0.1;
929 minz = bound(mins[2], minz, maxs[2]);
930 maxz = bound(mins[2], maxz, maxs[2]);
932 count *= cl_particles_quality.value;
937 count *= 4; // ick, this should be in the mod or maps?
941 k = particlepalette[colorbase + (rand()&3)];
942 if (gamemode == GAME_GOODVSBAD2)
944 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);
948 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);
955 k = particlepalette[colorbase + (rand()&3)];
956 if (gamemode == GAME_GOODVSBAD2)
958 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);
962 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);
967 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
971 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
976 if (!cl_particles.integer) return;
978 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
979 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
980 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
982 center[0] = (mins[0] + maxs[0]) * 0.5f;
983 center[1] = (mins[1] + maxs[1]) * 0.5f;
984 center[2] = (mins[2] + maxs[2]) * 0.5f;
986 count *= cl_particles_quality.value;
989 k = particlepalette[224 + (rand()&15)];
990 o[0] = lhrandom(mins[0], maxs[0]);
991 o[1] = lhrandom(mins[1], maxs[1]);
992 o[2] = lhrandom(mins[2], maxs[2]);
993 VectorSubtract(o, center, v);
994 VectorNormalizeFast(v);
995 VectorScale(v, 100, v);
996 v[2] += sv_gravity.value * 0.15f;
997 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);
1001 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
1005 if (!cl_particles.integer) return;
1006 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1007 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1008 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1010 count *= cl_particles_quality.value;
1013 k = particlepalette[224 + (rand()&15)];
1014 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);
1016 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);
1020 void CL_Flames (vec3_t org, vec3_t vel, int count)
1023 if (!cl_particles.integer) return;
1025 count *= cl_particles_quality.value;
1028 k = particlepalette[224 + (rand()&15)];
1029 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);
1041 void CL_LavaSplash (vec3_t origin)
1043 float i, j, inc, vel;
1046 if (!cl_particles.integer) return;
1048 inc = 32 / cl_particles_quality.value;
1049 for (i = -128;i < 128;i += inc)
1051 for (j = -128;j < 128;j += inc)
1053 dir[0] = j + lhrandom(0, 8);
1054 dir[1] = i + lhrandom(0, 8);
1056 org[0] = origin[0] + dir[0];
1057 org[1] = origin[1] + dir[1];
1058 org[2] = origin[2] + lhrandom(0, 64);
1059 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1060 if (gamemode == GAME_GOODVSBAD2)
1062 k = particlepalette[0 + (rand()&255)];
1063 l = particlepalette[0 + (rand()&255)];
1064 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);
1068 k = l = particlepalette[224 + (rand()&7)];
1069 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);
1082 void R_TeleportSplash (vec3_t org)
1085 if (!cl_particles.integer) return;
1087 inc = 8 / cl_particles_quality.value;
1088 for (i = -16;i < 16;i += inc)
1089 for (j = -16;j < 16;j += inc)
1090 for (k = -24;k < 32;k += inc)
1091 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);
1095 #ifdef WORKINGLQUAKE
1096 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1098 void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
1101 vec3_t vec, dir, vel, pos;
1102 float len, dec, speed, qd;
1103 int contents, smoke, blood, bubbles;
1105 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1108 VectorSubtract(end, start, dir);
1109 VectorNormalize(dir);
1111 VectorSubtract (end, start, vec);
1112 #ifdef WORKINGLQUAKE
1113 len = VectorNormalize (vec);
1115 speed = 1.0f / cl.frametime;
1116 VectorSubtract(end, start, vel);
1118 len = VectorNormalizeLength (vec);
1119 dec = -ent->persistent.trail_time;
1120 ent->persistent.trail_time += len;
1121 if (ent->persistent.trail_time < 0.01f)
1124 // if we skip out, leave it reset
1125 ent->persistent.trail_time = 0.0f;
1127 speed = ent->state_current.time - ent->state_previous.time;
1129 speed = 1.0f / speed;
1130 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1132 VectorScale(vel, speed, vel);
1134 // advance into this frame to reach the first puff location
1135 VectorMA(start, dec, vec, pos);
1138 contents = CL_PointQ1Contents(pos);
1139 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1142 smoke = cl_particles.integer && cl_particles_smoke.integer;
1143 blood = cl_particles.integer && cl_particles_blood.integer;
1144 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1145 qd = 1.0f / cl_particles_quality.value;
1151 case 0: // rocket trail
1155 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);
1156 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);
1159 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);
1162 case 1: // grenade trail
1163 // FIXME: make it gradually stop smoking
1166 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);
1171 case 4: // slight blood
1174 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);
1177 case 3: // green tracer
1181 if (gamemode == GAME_GOODVSBAD2)
1182 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);
1184 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);
1188 case 5: // flame tracer
1191 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);
1194 case 6: // voor trail
1198 if (gamemode == GAME_GOODVSBAD2)
1199 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);
1200 else if (gamemode == GAME_PRYDON)
1201 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);
1203 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);
1207 case 7: // Nehahra smoke tracer
1210 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);
1212 case 8: // Nexuiz plasma trail
1215 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);
1219 // advance to next time and position
1221 VectorMA (pos, dec, vec, pos);
1223 #ifndef WORKINGLQUAKE
1224 ent->persistent.trail_time = len;
1228 void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1232 if (!cl_particles.integer) return;
1233 if (!cl_particles_smoke.integer) return;
1235 VectorCopy(start, pos);
1236 VectorSubtract(end, start, vec);
1237 #ifdef WORKINGLQUAKE
1238 len = VectorNormalize(vec);
1240 len = VectorNormalizeLength(vec);
1242 color = particlepalette[color];
1243 dec = 3.0f / cl_particles_quality.value;
1246 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);
1248 VectorMA(pos, dec, vec, pos);
1252 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1254 int tempcolor2, cr, cg, cb;
1258 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1259 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);
1262 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1265 if (!cl_particles.integer) return;
1268 if (cl_particles_smoke.integer)
1269 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1270 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);
1273 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1276 if (!cl_particles.integer) return;
1278 if (cl_stainmaps.integer)
1279 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1280 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1283 if (cl_particles_smoke.integer)
1284 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1285 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);
1288 if (cl_particles_sparks.integer)
1289 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1290 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);
1298 void CL_MoveParticles (void)
1301 int i, maxparticle, j, a, content;
1302 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1303 #ifdef WORKINGLQUAKE
1306 entity_render_t *hitent;
1309 // LordHavoc: early out condition
1310 if (!cl_numparticles)
1312 cl_freeparticle = 0;
1316 #ifdef WORKINGLQUAKE
1317 frametime = cl.frametime;
1319 frametime = cl.time - cl.oldtime;
1321 gravity = frametime * sv_gravity.value;
1322 dvel = 1+4*frametime;
1323 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1327 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1333 VectorCopy(p->org, p->oldorg);
1334 VectorMA(p->org, frametime, p->vel, p->org);
1335 VectorCopy(p->org, org);
1338 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1340 VectorCopy(v, p->org);
1343 // assume it's blood (lame, but...)
1344 #ifndef WORKINGLQUAKE
1345 if (cl_stainmaps.integer)
1346 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));
1348 if (!cl_decals.integer)
1355 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1356 // convert from a blood particle to a blood decal
1357 p->texnum = tex_blooddecal[rand()&7];
1358 #ifndef WORKINGLQUAKE
1360 p->ownermodel = hitent->model;
1361 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1362 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1363 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1365 p->time2 = cl.time + cl_decals_time.value;
1366 p->die = p->time2 + cl_decals_fadetime.value;
1368 VectorCopy(normal, p->vel2);
1369 VectorClear(p->vel);
1370 VectorAdd(p->org, normal, p->org);
1379 dist = DotProduct(p->vel, normal) * -p->bounce;
1380 VectorMA(p->vel, dist, normal, p->vel);
1381 if (DotProduct(p->vel, p->vel) < 0.03)
1382 VectorClear(p->vel);
1387 p->vel[2] -= p->gravity * gravity;
1389 p->alpha -= p->alphafade * frametime;
1391 if (p->alpha <= 0 || cl.time > p->die)
1399 f = p->friction * frametime;
1401 content = CL_PointQ1Contents(p->org);
1402 if (content != CONTENTS_EMPTY)
1405 VectorScale(p->vel, f, p->vel);
1408 if (p->type != pt_static)
1414 content = CL_PointQ1Contents(p->org);
1416 if (a != CONTENTS_EMPTY)
1418 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1420 p->scalex += frametime * 8;
1421 p->scaley += frametime * 8;
1422 //p->alpha -= bloodwaterfade;
1428 p->vel[2] -= gravity;
1432 content = CL_PointQ1Contents(p->org);
1433 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1440 if (cl.time > p->time2)
1443 p->time2 = cl.time + (rand() & 3) * 0.1;
1444 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1445 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1446 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1449 content = CL_PointQ1Contents(p->org);
1451 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1455 p->scalex += frametime * p->time2;
1456 p->scaley += frametime * p->time2;
1459 #ifndef WORKINGLQUAKE
1460 if (p->owner->model == p->ownermodel)
1462 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1463 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1464 if (cl.time > p->time2)
1466 p->alphafade = p->alpha / (p->die - cl.time);
1467 p->type = pt_decalfade;
1475 #ifndef WORKINGLQUAKE
1476 if (p->owner->model == p->ownermodel)
1478 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1479 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1486 while (cl.time > p->time2)
1489 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);
1493 Con_Printf("unknown particle type %i\n", p->type);
1499 cl_numparticles = maxparticle + 1;
1500 cl_freeparticle = 0;
1503 #define MAX_PARTICLETEXTURES 64
1504 // particletexture_t is a rectangle in the particlefonttexture
1507 rtexture_t *texture;
1508 float s1, t1, s2, t2;
1513 static int particlefonttexture;
1515 static rtexturepool_t *particletexturepool;
1516 static rtexture_t *particlefonttexture;
1518 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1520 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1522 static qbyte shadebubble(float dx, float dy, vec3_t light)
1526 dz = 1 - (dx*dx+dy*dy);
1527 if (dz > 0) // it does hit the sphere
1531 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1532 VectorNormalize(normal);
1533 dot = DotProduct(normal, light);
1534 if (dot > 0.5) // interior reflection
1535 f += ((dot * 2) - 1);
1536 else if (dot < -0.5) // exterior reflection
1537 f += ((dot * -2) - 1);
1539 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1540 VectorNormalize(normal);
1541 dot = DotProduct(normal, light);
1542 if (dot > 0.5) // interior reflection
1543 f += ((dot * 2) - 1);
1544 else if (dot < -0.5) // exterior reflection
1545 f += ((dot * -2) - 1);
1547 f += 16; // just to give it a haze so you can see the outline
1548 f = bound(0, f, 255);
1555 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1557 int basex, basey, y;
1558 basex = ((texnum >> 0) & 7) * 32;
1559 basey = ((texnum >> 3) & 7) * 32;
1560 particletexture[texnum].s1 = (basex + 1) / 256.0f;
1561 particletexture[texnum].t1 = (basey + 1) / 256.0f;
1562 particletexture[texnum].s2 = (basex + 31) / 256.0f;
1563 particletexture[texnum].t2 = (basey + 31) / 256.0f;
1564 for (y = 0;y < 32;y++)
1565 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 4);
1568 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1571 float cx, cy, dx, dy, f, iradius;
1573 cx = lhrandom(radius + 1, 30 - radius);
1574 cy = lhrandom(radius + 1, 30 - radius);
1575 iradius = 1.0f / radius;
1576 alpha *= (1.0f / 255.0f);
1577 for (y = 0;y < 32;y++)
1579 for (x = 0;x < 32;x++)
1583 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1586 d = data + (y * 32 + x) * 4;
1587 d[0] += f * (red - d[0]);
1588 d[1] += f * (green - d[1]);
1589 d[2] += f * (blue - d[2]);
1595 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1598 for (i = 0;i < 32*32;i++, data += 4)
1600 data[0] = bound(minr, data[0], maxr);
1601 data[1] = bound(ming, data[1], maxg);
1602 data[2] = bound(minb, data[2], maxb);
1606 void particletextureinvert(qbyte *data)
1609 for (i = 0;i < 32*32;i++, data += 4)
1611 data[0] = 255 - data[0];
1612 data[1] = 255 - data[1];
1613 data[2] = 255 - data[2];
1617 static void R_InitParticleTexture (void)
1619 int x, y, d, i, j, k, m;
1620 float dx, dy, radius, f, f2;
1621 qbyte data[32][32][4], noise1[64][64], noise2[64][64], data2[64][16][4];
1623 qbyte particletexturedata[256*256*4];
1625 // a note: decals need to modulate (multiply) the background color to
1626 // properly darken it (stain), and they need to be able to alpha fade,
1627 // this is a very difficult challenge because it means fading to white
1628 // (no change to background) rather than black (darkening everything
1629 // behind the whole decal polygon), and to accomplish this the texture is
1630 // inverted (dark red blood on white background becomes brilliant cyan
1631 // and white on black background) so we can alpha fade it to black, then
1632 // we invert it again during the blendfunc to make it work...
1634 memset(particletexturedata, 255, sizeof(particletexturedata));
1637 for (i = 0;i < 8;i++)
1639 memset(&data[0][0][0], 255, sizeof(data));
1642 fractalnoise(&noise1[0][0], 64, 4);
1643 fractalnoise(&noise2[0][0], 64, 8);
1645 for (y = 0;y < 32;y++)
1648 for (x = 0;x < 32;x++)
1651 d = (noise2[y][x] - 128) * 3 + 192;
1653 d = d * (256 - (int) (dx*dx+dy*dy)) / 256;
1654 d = (d * noise1[y][x]) >> 7;
1655 d = bound(0, d, 255);
1656 data[y][x][3] = (qbyte) d;
1663 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1667 for (i = 0;i < 16;i++)
1669 memset(&data[0][0][0], 255, sizeof(data));
1670 radius = i * 3.0f / 16.0f;
1671 f2 = 255.0f * ((15.0f - i) / 15.0f);
1672 for (y = 0;y < 32;y++)
1674 dy = (y - 16) * 0.25f;
1675 for (x = 0;x < 32;x++)
1677 dx = (x - 16) * 0.25f;
1678 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
1679 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1682 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1686 memset(&data[0][0][0], 255, sizeof(data));
1687 for (y = 0;y < 32;y++)
1690 for (x = 0;x < 32;x++)
1693 d = (256 - (dx*dx+dy*dy));
1694 d = bound(0, d, 255);
1695 data[y][x][3] = (qbyte) d;
1698 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1701 memset(&data[0][0][0], 255, sizeof(data));
1702 light[0] = 1;light[1] = 1;light[2] = 1;
1703 VectorNormalize(light);
1704 for (y = 0;y < 32;y++)
1705 for (x = 0;x < 32;x++)
1706 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);
1707 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1710 memset(&data[0][0][0], 255, sizeof(data));
1711 light[0] = 1;light[1] = 1;light[2] = 1;
1712 VectorNormalize(light);
1713 for (y = 0;y < 32;y++)
1714 for (x = 0;x < 32;x++)
1715 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
1716 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1719 for (i = 0;i < 8;i++)
1721 memset(&data[0][0][0], 255, sizeof(data));
1722 for (k = 0;k < 24;k++)
1723 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 160);
1724 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1725 particletextureinvert(&data[0][0][0]);
1726 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1730 for (i = 0;i < 8;i++)
1732 memset(&data[0][0][0], 255, sizeof(data));
1733 for (k = 0;k < 24;k++)
1734 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 96);
1735 for (j = 3;j < 7;j++)
1736 for (k = 0, m = rand() % 12;k < m;k++)
1737 particletextureblotch(&data[0][0][0], j, 96, 0, 0, 192);
1738 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1739 particletextureinvert(&data[0][0][0]);
1740 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1744 for (i = 0;i < 8;i++)
1746 memset(&data[0][0][0], 255, sizeof(data));
1747 for (k = 0;k < 12;k++)
1748 particletextureblotch(&data[0][0][0], 2, 0, 0, 0, 128);
1749 for (k = 0;k < 3;k++)
1750 particletextureblotch(&data[0][0][0], 14, 0, 0, 0, 160);
1751 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1752 particletextureinvert(&data[0][0][0]);
1753 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1757 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1758 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1759 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1761 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1762 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1763 particletexture[i].texture = particlefonttexture;
1766 fractalnoise(&noise1[0][0], 64, 4);
1768 for (y = 0;y < 64;y++)
1770 for (x = 0;x < 16;x++)
1776 d = d * d * noise1[y][x] / (7 * 7);
1777 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1778 data2[y][x][3] = 255;
1782 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "beam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1783 particletexture[tex_beam].s1 = 0;
1784 particletexture[tex_beam].t1 = 0;
1785 particletexture[tex_beam].s2 = 1;
1786 particletexture[tex_beam].t2 = 1;
1790 static void r_part_start(void)
1792 particletexturepool = R_AllocTexturePool();
1793 R_InitParticleTexture ();
1796 static void r_part_shutdown(void)
1798 R_FreeTexturePool(&particletexturepool);
1801 static void r_part_newmap(void)
1803 cl_numparticles = 0;
1804 cl_freeparticle = 0;
1807 void R_Particles_Init (void)
1809 Cvar_RegisterVariable(&r_drawparticles);
1810 #ifdef WORKINGLQUAKE
1813 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1817 #ifdef WORKINGLQUAKE
1818 void R_InitParticles(void)
1820 CL_Particles_Init();
1825 float particle_vertex3f[12], particle_texcoord2f[8];
1827 #ifdef WORKINGLQUAKE
1828 void R_DrawParticle(particle_t *p)
1831 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1833 const particle_t *p = calldata1;
1836 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1837 particletexture_t *tex;
1839 VectorCopy(p->org, org);
1841 tex = &particletexture[p->texnum];
1842 cr = p->color[0] * (1.0f / 255.0f);
1843 cg = p->color[1] * (1.0f / 255.0f);
1844 cb = p->color[2] * (1.0f / 255.0f);
1845 ca = p->alpha * (1.0f / 255.0f);
1846 if (p->blendmode == PBLEND_MOD)
1857 #ifndef WORKINGLQUAKE
1858 if (fogenabled && p->blendmode != PBLEND_MOD)
1860 VectorSubtract(org, r_vieworigin, fogvec);
1861 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1866 if (p->blendmode == 0)
1868 cr += fogcolor[0] * fog;
1869 cg += fogcolor[1] * fog;
1870 cb += fogcolor[2] * fog;
1874 R_Mesh_Matrix(&r_identitymatrix);
1876 memset(&m, 0, sizeof(m));
1877 m.tex[0] = R_GetTexture(tex->texture);
1878 m.pointer_texcoord[0] = particle_texcoord2f;
1879 m.pointer_vertex = particle_vertex3f;
1882 GL_Color(cr, cg, cb, ca);
1884 if (p->blendmode == 0)
1885 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1886 else if (p->blendmode == 1)
1887 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1889 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1890 GL_DepthMask(false);
1893 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1895 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1898 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1900 VectorNegate(p->vel2, v);
1901 VectorVectors(v, right, up);
1904 VectorVectors(p->vel2, right, up);
1905 VectorScale(right, p->scalex, right);
1906 VectorScale(up, p->scaley, up);
1910 VectorScale(r_viewleft, -p->scalex, right);
1911 VectorScale(r_viewup, p->scaley, up);
1913 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1914 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1915 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1916 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1917 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1918 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1919 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1920 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1921 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1922 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1923 particle_vertex3f[10] = org[1] + right[1] - up[1];
1924 particle_vertex3f[11] = org[2] + right[2] - up[2];
1925 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1926 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1927 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1928 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1930 else if (p->orientation == PARTICLE_SPARK)
1932 VectorMA(p->org, -p->scaley, p->vel, v);
1933 VectorMA(p->org, p->scaley, p->vel, up2);
1934 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1935 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1936 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1937 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1938 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1940 else if (p->orientation == PARTICLE_BEAM)
1942 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1943 VectorSubtract(p->vel2, p->org, up);
1944 VectorNormalizeFast(up);
1945 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1946 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1947 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1948 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1949 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1950 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1953 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1956 if (p->blendmode == 0)
1957 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1958 else if (p->blendmode == 1)
1959 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1961 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1962 glColor4f(cr, cg, cb, ca);
1964 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1965 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1966 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1967 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1970 R_Mesh_Draw(4, 2, polygonelements);
1974 void R_DrawParticles (void)
1977 float minparticledist;
1980 #ifdef WORKINGLQUAKE
1984 // LordHavoc: early out conditions
1985 if ((!cl_numparticles) || (!r_drawparticles.integer))
1988 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1990 #ifdef WORKINGLQUAKE
1991 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1993 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1995 // LordHavoc: only render if not too close
1996 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1997 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2000 glDisable(GL_BLEND);
2001 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2003 // LordHavoc: only render if not too close
2004 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2009 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2010 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);