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_static, pt_rain, pt_bubble, pt_blood, pt_grow, pt_decal, pt_decalfade
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 particle_t *particles;
286 static particle_t **freeparticles; // list used only in compacting particles array
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)
319 void CL_ReadPointFile_f (void);
320 void CL_Particles_Init (void)
324 i = COM_CheckParm ("-particles");
326 if (i && i < com_argc - 1)
328 cl_maxparticles = (int)(atoi(com_argv[i+1]));
329 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
330 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
333 cl_maxparticles = MAX_PARTICLES;
335 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
337 Cvar_RegisterVariable (&cl_particles);
338 Cvar_RegisterVariable (&cl_particles_quality);
339 Cvar_RegisterVariable (&cl_particles_size);
340 Cvar_RegisterVariable (&cl_particles_bloodshowers);
341 Cvar_RegisterVariable (&cl_particles_blood);
342 Cvar_RegisterVariable (&cl_particles_blood_alpha);
343 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
344 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
345 Cvar_RegisterVariable (&cl_particles_smoke);
346 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
347 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
348 Cvar_RegisterVariable (&cl_particles_sparks);
349 Cvar_RegisterVariable (&cl_particles_bubbles);
350 Cvar_RegisterVariable (&cl_decals);
351 Cvar_RegisterVariable (&cl_decals_time);
352 Cvar_RegisterVariable (&cl_decals_fadetime);
355 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
356 freeparticles = (void *) 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));
360 freeparticles = (void *) 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)
386 if (cl_numparticles < cl_maxparticles)
389 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
390 ptempcolor = (pcolor1);
391 ptempcolor2 = (pcolor2);
392 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
393 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
394 pcb2 = (ptempcolor2) & 0xFF;
395 if (ptempcolor != ptempcolor2)
397 pcr1 = ((ptempcolor) >> 16) & 0xFF;
398 pcg1 = ((ptempcolor) >> 8) & 0xFF;
399 pcb1 = (ptempcolor) & 0xFF;
400 ptempcolor = rand() & 0xFF;
401 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
402 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
403 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
405 part = &particles[cl_numparticles++];
406 memset(part, 0, sizeof(*part));
407 part->type = (ptype);
408 part->color[0] = pcr2;
409 part->color[1] = pcg2;
410 part->color[2] = pcb2;
411 part->color[3] = 0xFF;
412 part->orientation = porientation;
414 part->blendmode = pblendmode;
415 part->scalex = (pscalex);
416 part->scaley = (pscaley);
417 part->alpha = (palpha);
418 part->alphafade = (palphafade);
419 part->die = cl.time + (ptime);
420 part->gravity = (pgravity);
421 part->bounce = (pbounce);
425 part->vel[0] = (pvx);
426 part->vel[1] = (pvy);
427 part->vel[2] = (pvz);
428 part->time2 = (ptime2);
429 part->vel2[0] = (pvx2);
430 part->vel2[1] = (pvy2);
431 part->vel2[2] = (pvz2);
432 part->friction = (pfriction);
433 part->pressure = (ppressure);
439 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
442 if (!cl_decals.integer)
444 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);
445 #ifndef WORKINGLQUAKE
449 p->ownermodel = p->owner->model;
450 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
451 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
452 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
457 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
460 float bestfrac, bestorg[3], bestnormal[3];
461 float frac, v[3], normal[3], org2[3];
463 void *besthitent = NULL, *hitent;
465 entity_render_t *besthitent = NULL, *hitent;
468 for (i = 0;i < 32;i++)
471 VectorMA(org, maxdist, org2, org2);
472 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
477 VectorCopy(v, bestorg);
478 VectorCopy(normal, bestnormal);
482 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
490 void CL_EntityParticles (entity_t *ent)
494 float sp, sy, cp, cy;
498 static vec3_t avelocities[NUMVERTEXNORMALS];
499 if (!cl_particles.integer) return;
504 if (!avelocities[0][0])
505 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
506 avelocities[0][i] = (rand()&255) * 0.01;
508 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
510 angle = cl.time * avelocities[i][0];
513 angle = cl.time * avelocities[i][1];
522 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);
524 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);
530 void CL_ReadPointFile_f (void)
534 char *pointfile = NULL, *pointfilepos, *t, tchar;
535 char name[MAX_OSPATH];
540 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
541 strlcat (name, ".pts", sizeof (name));
543 pointfile = COM_LoadTempFile (name);
545 pointfile = FS_LoadFile(name, true);
549 Con_Printf("Could not open %s\n", name);
553 Con_Printf("Reading %s...\n", name);
556 pointfilepos = pointfile;
557 while (*pointfilepos)
559 while (*pointfilepos == '\n' || *pointfilepos == '\r')
564 while (*t && *t != '\n' && *t != '\r')
568 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
574 VectorCopy(org, leakorg);
577 if (cl_numparticles < cl_maxparticles - 3)
580 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);
583 #ifndef WORKINGLQUAKE
586 VectorCopy(leakorg, org);
587 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
589 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);
590 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);
591 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);
596 CL_ParseParticleEffect
598 Parse an effect out of the server message
601 void CL_ParseParticleEffect (void)
604 int i, count, msgcount, color;
607 for (i=0 ; i<3 ; i++)
608 dir[i] = MSG_ReadChar () * (1.0/16);
609 msgcount = MSG_ReadByte ();
610 color = MSG_ReadByte ();
617 if (cl_particles_blood_bloodhack.integer)
622 CL_BloodPuff(org, dir, count / 2);
628 CL_BloodPuff(org, dir, count / 2);
632 CL_RunParticleEffect (org, dir, color, count);
641 void CL_ParticleExplosion (vec3_t org)
646 if (cl_stainmaps.integer)
647 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
648 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
650 i = CL_PointQ1Contents(org);
651 if ((i == CONTENTS_SLIME || i == CONTENTS_WATER) && cl_particles.integer && cl_particles_bubbles.integer)
653 for (i = 0;i < 128 * cl_particles_quality.value;i++)
654 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);
659 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
661 if (cl_particles.integer && cl_particles_smoke.integer)
663 for (i = 0;i < 64;i++)
666 v2[0] = lhrandom(-64, 64);
667 v2[1] = lhrandom(-64, 64);
668 v2[2] = lhrandom(-8, 24);
670 for (k = 0;k < 16;k++)
672 v[0] = org[0] + lhrandom(-64, 64);
673 v[1] = org[1] + lhrandom(-64, 64);
674 v[2] = org[2] + lhrandom(-8, 24);
675 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
678 VectorSubtract(v2, org, v2);
680 VectorScale(v2, 2.0f, v2);
681 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);
686 if (cl_particles.integer && cl_particles_sparks.integer)
689 for (i = 0;i < 256 * cl_particles_quality.value;i++)
691 k = particlepalette[0x68 + (rand() & 7)];
692 particle(pt_static, PARTICLE_SPARK, k, k, tex_particle, false, PBLEND_ADD, 1.5f, 0.05f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 512, 9999, 1, 0, org[0], org[1], org[2], lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192) + 160, 0, 0, 0, 0, 0, 0);
697 if (cl_explosions.integer)
703 CL_ParticleExplosion2
707 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
710 if (!cl_particles.integer) return;
712 for (i = 0;i < 512 * cl_particles_quality.value;i++)
714 k = particlepalette[colorStart + (i % colorLength)];
715 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ALPHA, 1.5, 1.5, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 384, 0.3, 0, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192), 0, 0, 0, 0, 1, 0);
725 void CL_BlobExplosion (vec3_t org)
727 if (cl_stainmaps.integer)
728 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
729 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
731 if (cl_explosions.integer)
741 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
747 CL_ParticleExplosion(org);
750 if (!cl_particles.integer) return;
751 count *= cl_particles_quality.value;
754 k = particlepalette[color + (rand()&7)];
755 if (gamemode == GAME_GOODVSBAD2)
756 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);
758 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);
762 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
768 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
773 if (cl_stainmaps.integer)
774 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
775 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
777 if (!cl_particles.integer) return;
779 if (cl_particles_bulletimpacts.integer)
782 if (cl_particles_smoke.integer)
784 k = count * 0.25 * cl_particles_quality.value;
787 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
788 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
789 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
790 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
791 particle(pt_grow, PARTICLE_BILLBOARD, 0x101010, 0x202020, tex_smoke[rand()&7], true, PBLEND_ADD, 3, 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 9999, -0.2, 0, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 16), 15, 0, 0, 0, 0, 0);
795 if (cl_particles_sparks.integer)
798 count *= cl_particles_quality.value;
801 k = particlepalette[0x68 + (rand() & 7)];
802 particle(pt_static, PARTICLE_SPARK, k, k, tex_particle, false, PBLEND_ADD, 0.4f, 0.015f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, 9999, 1, 0, org[0], org[1], org[2], lhrandom(-64, 64) + dir[0], lhrandom(-64, 64) + dir[1], lhrandom(0, 128) + dir[2], 0, 0, 0, 0, 0, 0);
808 void CL_PlasmaBurn (vec3_t org)
810 if (cl_stainmaps.integer)
811 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
812 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
815 static float bloodcount = 0;
816 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
820 // bloodcount is used to accumulate counts too small to cause a blood particle
821 if (!cl_particles.integer) return;
822 if (!cl_particles_blood.integer) return;
829 while(bloodcount > 0)
831 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
832 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
833 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
834 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
835 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);
836 bloodcount -= 16 / cl_particles_quality.value;
840 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
842 vec3_t org, vel, diff, center, velscale;
843 if (!cl_particles.integer) return;
844 if (!cl_particles_bloodshowers.integer) return;
845 if (!cl_particles_blood.integer) return;
847 VectorSubtract(maxs, mins, diff);
848 center[0] = (mins[0] + maxs[0]) * 0.5;
849 center[1] = (mins[1] + maxs[1]) * 0.5;
850 center[2] = (mins[2] + maxs[2]) * 0.5;
851 velscale[0] = velspeed * 2.0 / diff[0];
852 velscale[1] = velspeed * 2.0 / diff[1];
853 velscale[2] = velspeed * 2.0 / diff[2];
855 bloodcount += count * 5.0f;
856 while (bloodcount > 0)
858 org[0] = lhrandom(mins[0], maxs[0]);
859 org[1] = lhrandom(mins[1], maxs[1]);
860 org[2] = lhrandom(mins[2], maxs[2]);
861 vel[0] = (org[0] - center[0]) * velscale[0];
862 vel[1] = (org[1] - center[1]) * velscale[1];
863 vel[2] = (org[2] - center[2]) * velscale[2];
864 bloodcount -= 16 / cl_particles_quality.value;
865 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);
869 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
873 if (!cl_particles.integer) return;
874 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
875 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
876 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
878 count *= cl_particles_quality.value;
881 k = particlepalette[colorbase + (rand()&3)];
882 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);
886 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
889 float t, z, minz, maxz;
890 if (!cl_particles.integer) return;
891 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
892 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
893 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
894 if (dir[2] < 0) // falling
896 t = (maxs[2] - mins[2]) / -dir[2];
901 t = (maxs[2] - mins[2]) / dir[2];
904 if (t < 0 || t > 2) // sanity check
907 minz = z - fabs(dir[2]) * 0.1;
908 maxz = z + fabs(dir[2]) * 0.1;
909 minz = bound(mins[2], minz, maxs[2]);
910 maxz = bound(mins[2], maxz, maxs[2]);
912 count *= cl_particles_quality.value;
917 count *= 4; // ick, this should be in the mod or maps?
921 k = particlepalette[colorbase + (rand()&3)];
922 if (gamemode == GAME_GOODVSBAD2)
924 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);
928 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);
935 k = particlepalette[colorbase + (rand()&3)];
936 if (gamemode == GAME_GOODVSBAD2)
938 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);
942 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);
947 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
951 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
956 if (!cl_particles.integer) return;
958 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
959 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
960 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
962 center[0] = (mins[0] + maxs[0]) * 0.5f;
963 center[1] = (mins[1] + maxs[1]) * 0.5f;
964 center[2] = (mins[2] + maxs[2]) * 0.5f;
966 count *= cl_particles_quality.value;
969 k = particlepalette[224 + (rand()&15)];
970 o[0] = lhrandom(mins[0], maxs[0]);
971 o[1] = lhrandom(mins[1], maxs[1]);
972 o[2] = lhrandom(mins[2], maxs[2]);
973 VectorSubtract(o, center, v);
974 VectorNormalizeFast(v);
975 VectorScale(v, 100, v);
976 v[2] += sv_gravity.value * 0.15f;
977 particle(pt_static, PARTICLE_BILLBOARD, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, 1.5, 1.5, lhrandom(64, 128) / cl_particles_quality.value, 128 / cl_particles_quality.value, 9999, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0, 0, 0, 0, 0, 0);
981 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
985 if (!cl_particles.integer) return;
986 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
987 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
988 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
990 count *= cl_particles_quality.value;
993 k = particlepalette[224 + (rand()&15)];
994 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);
996 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);
1000 void CL_Flames (vec3_t org, vec3_t vel, int count)
1003 if (!cl_particles.integer) return;
1005 count *= cl_particles_quality.value;
1008 k = particlepalette[224 + (rand()&15)];
1009 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);
1021 void CL_LavaSplash (vec3_t origin)
1023 float i, j, inc, vel;
1026 if (!cl_particles.integer) return;
1028 inc = 32 / cl_particles_quality.value;
1029 for (i = -128;i < 128;i += inc)
1031 for (j = -128;j < 128;j += inc)
1033 dir[0] = j + lhrandom(0, 8);
1034 dir[1] = i + lhrandom(0, 8);
1036 org[0] = origin[0] + dir[0];
1037 org[1] = origin[1] + dir[1];
1038 org[2] = origin[2] + lhrandom(0, 64);
1039 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1040 if (gamemode == GAME_GOODVSBAD2)
1042 k = particlepalette[0 + (rand()&255)];
1043 l = particlepalette[0 + (rand()&255)];
1044 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);
1048 k = l = particlepalette[224 + (rand()&7)];
1049 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);
1062 void R_TeleportSplash (vec3_t org)
1065 if (!cl_particles.integer) return;
1067 inc = 8 / cl_particles_quality.value;
1068 for (i = -16;i < 16;i += inc)
1069 for (j = -16;j < 16;j += inc)
1070 for (k = -24;k < 32;k += inc)
1071 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);
1075 #ifdef WORKINGLQUAKE
1076 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1078 void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
1081 vec3_t vec, dir, vel, pos;
1082 float len, dec, speed, qd;
1083 int contents, smoke, blood, bubbles;
1085 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1088 VectorSubtract(end, start, dir);
1089 VectorNormalize(dir);
1091 VectorSubtract (end, start, vec);
1092 #ifdef WORKINGLQUAKE
1093 len = VectorNormalize (vec);
1095 speed = 1.0f / cl.frametime;
1096 VectorSubtract(end, start, vel);
1098 len = VectorNormalizeLength (vec);
1099 dec = -ent->persistent.trail_time;
1100 ent->persistent.trail_time += len;
1101 if (ent->persistent.trail_time < 0.01f)
1104 // if we skip out, leave it reset
1105 ent->persistent.trail_time = 0.0f;
1107 speed = ent->state_current.time - ent->state_previous.time;
1109 speed = 1.0f / speed;
1110 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1112 VectorScale(vel, speed, vel);
1114 // advance into this frame to reach the first puff location
1115 VectorMA(start, dec, vec, pos);
1118 contents = CL_PointQ1Contents(pos);
1119 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1122 smoke = cl_particles.integer && cl_particles_smoke.integer;
1123 blood = cl_particles.integer && cl_particles_blood.integer;
1124 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1125 qd = 1.0f / cl_particles_quality.value;
1131 case 0: // rocket trail
1135 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);
1136 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);
1139 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);
1142 case 1: // grenade trail
1143 // FIXME: make it gradually stop smoking
1146 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);
1151 case 4: // slight blood
1154 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);
1157 case 3: // green tracer
1161 if (gamemode == GAME_GOODVSBAD2)
1162 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);
1164 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);
1168 case 5: // flame tracer
1171 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);
1174 case 6: // voor trail
1178 if (gamemode == GAME_GOODVSBAD2)
1179 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);
1181 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);
1185 case 7: // Nehahra smoke tracer
1188 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);
1190 case 8: // Nexuiz plasma trail
1193 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);
1197 // advance to next time and position
1199 VectorMA (pos, dec, vec, pos);
1201 #ifndef WORKINGLQUAKE
1202 ent->persistent.trail_time = len;
1206 void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1210 if (!cl_particles.integer) return;
1211 if (!cl_particles_smoke.integer) return;
1213 VectorCopy(start, pos);
1214 VectorSubtract(end, start, vec);
1215 #ifdef WORKINGLQUAKE
1216 len = VectorNormalize(vec);
1218 len = VectorNormalizeLength(vec);
1220 color = particlepalette[color];
1221 dec = 3.0f / cl_particles_quality.value;
1224 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);
1226 VectorMA(pos, dec, vec, pos);
1230 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1232 int tempcolor2, cr, cg, cb;
1236 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1237 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);
1240 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1243 if (!cl_particles.integer) return;
1246 if (cl_particles_smoke.integer)
1247 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1248 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);
1251 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1254 if (!cl_particles.integer) return;
1256 if (cl_stainmaps.integer)
1257 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1258 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1261 if (cl_particles_smoke.integer)
1262 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1263 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);
1266 if (cl_particles_sparks.integer)
1267 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1268 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);
1276 void CL_MoveParticles (void)
1279 int i, activeparticles, maxparticle, j, a, pressureused = false, content;
1280 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1281 #ifdef WORKINGLQUAKE
1284 entity_render_t *hitent;
1287 // LordHavoc: early out condition
1288 if (!cl_numparticles)
1291 #ifdef WORKINGLQUAKE
1292 frametime = cl.frametime;
1294 frametime = cl.time - cl.oldtime;
1296 gravity = frametime * sv_gravity.value;
1297 dvel = 1+4*frametime;
1298 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1300 activeparticles = 0;
1303 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1306 VectorCopy(p->org, p->oldorg);
1307 VectorMA(p->org, frametime, p->vel, p->org);
1308 VectorCopy(p->org, org);
1311 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1313 VectorCopy(v, p->org);
1316 // assume it's blood (lame, but...)
1317 #ifndef WORKINGLQUAKE
1318 if (cl_stainmaps.integer)
1319 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));
1321 if (cl_decals.integer)
1324 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1325 // convert from a blood particle to a blood decal
1326 p->texnum = tex_blooddecal[rand()&7];
1327 #ifndef WORKINGLQUAKE
1329 p->ownermodel = hitent->model;
1330 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1331 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1332 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1334 p->time2 = cl.time + cl_decals_time.value;
1335 p->die = p->time2 + cl_decals_fadetime.value;
1337 VectorCopy(normal, p->vel2);
1338 VectorClear(p->vel);
1339 VectorAdd(p->org, normal, p->org);
1349 freeparticles[j++] = p;
1355 dist = DotProduct(p->vel, normal) * -p->bounce;
1356 VectorMA(p->vel, dist, normal, p->vel);
1357 if (DotProduct(p->vel, p->vel) < 0.03)
1358 VectorClear(p->vel);
1362 p->vel[2] -= p->gravity * gravity;
1363 p->alpha -= p->alphafade * frametime;
1366 f = p->friction * frametime;
1368 content = CL_PointQ1Contents(p->org);
1369 if (content != CONTENTS_EMPTY)
1372 VectorScale(p->vel, f, p->vel);
1375 if (p->type != pt_static)
1381 content = CL_PointQ1Contents(p->org);
1383 if (a != CONTENTS_EMPTY)
1385 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1387 p->scalex += frametime * 8;
1388 p->scaley += frametime * 8;
1389 //p->alpha -= bloodwaterfade;
1395 p->vel[2] -= gravity;
1399 content = CL_PointQ1Contents(p->org);
1400 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1407 if (cl.time > p->time2)
1410 p->time2 = cl.time + (rand() & 3) * 0.1;
1411 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1412 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1413 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1416 content = CL_PointQ1Contents(p->org);
1418 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1422 p->scalex += frametime * p->time2;
1423 p->scaley += frametime * p->time2;
1426 #ifndef WORKINGLQUAKE
1427 if (p->owner->model == p->ownermodel)
1429 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1430 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1435 if (cl.time > p->time2)
1437 p->alphafade = p->alpha / (p->die - cl.time);
1438 p->type = pt_decalfade;
1442 #ifndef WORKINGLQUAKE
1443 if (p->owner->model == p->ownermodel)
1445 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1446 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1453 Con_Printf("unknown particle type %i\n", p->type);
1459 // remove dead particles
1460 if (p->alpha < 1 || p->die < cl.time)
1461 freeparticles[j++] = p;
1467 pressureused = true;
1470 // fill in gaps to compact the array
1472 while (maxparticle >= activeparticles)
1474 *freeparticles[i++] = particles[maxparticle--];
1475 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1478 cl_numparticles = activeparticles;
1482 activeparticles = 0;
1483 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1485 freeparticles[activeparticles++] = p;
1487 if (activeparticles)
1489 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1491 for (j = 0;j < activeparticles;j++)
1493 if (freeparticles[j] != p)
1495 float dist, diff[3];
1496 VectorSubtract(p->org, freeparticles[j]->org, diff);
1497 dist = DotProduct(diff, diff);
1498 if (dist < 4096 && dist >= 1)
1500 dist = freeparticles[j]->scalex * 4.0f * frametime / sqrt(dist);
1501 VectorMA(p->vel, dist, diff, p->vel);
1510 #define MAX_PARTICLETEXTURES 64
1511 // particletexture_t is a rectangle in the particlefonttexture
1514 rtexture_t *texture;
1515 float s1, t1, s2, t2;
1520 static int particlefonttexture;
1522 static rtexturepool_t *particletexturepool;
1523 static rtexture_t *particlefonttexture;
1525 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1527 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1529 static qbyte shadebubble(float dx, float dy, vec3_t light)
1533 dz = 1 - (dx*dx+dy*dy);
1534 if (dz > 0) // it does hit the sphere
1538 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1539 VectorNormalize(normal);
1540 dot = DotProduct(normal, light);
1541 if (dot > 0.5) // interior reflection
1542 f += ((dot * 2) - 1);
1543 else if (dot < -0.5) // exterior reflection
1544 f += ((dot * -2) - 1);
1546 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1547 VectorNormalize(normal);
1548 dot = DotProduct(normal, light);
1549 if (dot > 0.5) // interior reflection
1550 f += ((dot * 2) - 1);
1551 else if (dot < -0.5) // exterior reflection
1552 f += ((dot * -2) - 1);
1554 f += 16; // just to give it a haze so you can see the outline
1555 f = bound(0, f, 255);
1562 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1564 int basex, basey, y;
1565 basex = ((texnum >> 0) & 7) * 32;
1566 basey = ((texnum >> 3) & 7) * 32;
1567 particletexture[texnum].s1 = (basex + 1) / 256.0f;
1568 particletexture[texnum].t1 = (basey + 1) / 256.0f;
1569 particletexture[texnum].s2 = (basex + 31) / 256.0f;
1570 particletexture[texnum].t2 = (basey + 31) / 256.0f;
1571 for (y = 0;y < 32;y++)
1572 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 4);
1575 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1578 float cx, cy, dx, dy, f, iradius;
1580 cx = lhrandom(radius + 1, 30 - radius);
1581 cy = lhrandom(radius + 1, 30 - radius);
1582 iradius = 1.0f / radius;
1583 alpha *= (1.0f / 255.0f);
1584 for (y = 0;y < 32;y++)
1586 for (x = 0;x < 32;x++)
1590 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1593 d = data + (y * 32 + x) * 4;
1594 d[0] += f * (red - d[0]);
1595 d[1] += f * (green - d[1]);
1596 d[2] += f * (blue - d[2]);
1602 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1605 for (i = 0;i < 32*32;i++, data += 4)
1607 data[0] = bound(minr, data[0], maxr);
1608 data[1] = bound(ming, data[1], maxg);
1609 data[2] = bound(minb, data[2], maxb);
1613 void particletextureinvert(qbyte *data)
1616 for (i = 0;i < 32*32;i++, data += 4)
1618 data[0] = 255 - data[0];
1619 data[1] = 255 - data[1];
1620 data[2] = 255 - data[2];
1624 static void R_InitParticleTexture (void)
1626 int x, y, d, i, j, k, m;
1627 float dx, dy, radius, f, f2;
1628 qbyte data[32][32][4], noise1[64][64], noise2[64][64], data2[64][16][4];
1630 qbyte particletexturedata[256*256*4];
1632 // a note: decals need to modulate (multiply) the background color to
1633 // properly darken it (stain), and they need to be able to alpha fade,
1634 // this is a very difficult challenge because it means fading to white
1635 // (no change to background) rather than black (darkening everything
1636 // behind the whole decal polygon), and to accomplish this the texture is
1637 // inverted (dark red blood on white background becomes brilliant cyan
1638 // and white on black background) so we can alpha fade it to black, then
1639 // we invert it again during the blendfunc to make it work...
1641 memset(particletexturedata, 255, sizeof(particletexturedata));
1644 for (i = 0;i < 8;i++)
1646 memset(&data[0][0][0], 255, sizeof(data));
1649 fractalnoise(&noise1[0][0], 64, 4);
1650 fractalnoise(&noise2[0][0], 64, 8);
1652 for (y = 0;y < 32;y++)
1655 for (x = 0;x < 32;x++)
1658 d = (noise2[y][x] - 128) * 3 + 192;
1660 d = d * (256 - (int) (dx*dx+dy*dy)) / 256;
1661 d = (d * noise1[y][x]) >> 7;
1662 d = bound(0, d, 255);
1663 data[y][x][3] = (qbyte) d;
1670 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1674 for (i = 0;i < 16;i++)
1676 memset(&data[0][0][0], 255, sizeof(data));
1677 radius = i * 3.0f / 16.0f;
1678 f2 = 255.0f * ((15.0f - i) / 15.0f);
1679 for (y = 0;y < 32;y++)
1681 dy = (y - 16) * 0.25f;
1682 for (x = 0;x < 32;x++)
1684 dx = (x - 16) * 0.25f;
1685 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
1686 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1689 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1693 memset(&data[0][0][0], 255, sizeof(data));
1694 for (y = 0;y < 32;y++)
1697 for (x = 0;x < 32;x++)
1700 d = (256 - (dx*dx+dy*dy));
1701 d = bound(0, d, 255);
1702 data[y][x][3] = (qbyte) d;
1705 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1708 memset(&data[0][0][0], 255, sizeof(data));
1709 light[0] = 1;light[1] = 1;light[2] = 1;
1710 VectorNormalize(light);
1711 for (y = 0;y < 32;y++)
1712 for (x = 0;x < 32;x++)
1713 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);
1714 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1717 memset(&data[0][0][0], 255, sizeof(data));
1718 light[0] = 1;light[1] = 1;light[2] = 1;
1719 VectorNormalize(light);
1720 for (y = 0;y < 32;y++)
1721 for (x = 0;x < 32;x++)
1722 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
1723 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1726 for (i = 0;i < 8;i++)
1728 memset(&data[0][0][0], 255, sizeof(data));
1729 for (k = 0;k < 24;k++)
1730 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 160);
1731 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1732 particletextureinvert(&data[0][0][0]);
1733 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1737 for (i = 0;i < 8;i++)
1739 memset(&data[0][0][0], 255, sizeof(data));
1740 for (k = 0;k < 24;k++)
1741 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 96);
1742 for (j = 3;j < 7;j++)
1743 for (k = 0, m = rand() % 12;k < m;k++)
1744 particletextureblotch(&data[0][0][0], j, 96, 0, 0, 192);
1745 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1746 particletextureinvert(&data[0][0][0]);
1747 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1751 for (i = 0;i < 8;i++)
1753 memset(&data[0][0][0], 255, sizeof(data));
1754 for (k = 0;k < 12;k++)
1755 particletextureblotch(&data[0][0][0], 2, 0, 0, 0, 128);
1756 for (k = 0;k < 3;k++)
1757 particletextureblotch(&data[0][0][0], 14, 0, 0, 0, 160);
1758 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1759 particletextureinvert(&data[0][0][0]);
1760 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1764 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1765 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1766 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1768 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1769 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1770 particletexture[i].texture = particlefonttexture;
1773 fractalnoise(&noise1[0][0], 64, 4);
1775 for (y = 0;y < 64;y++)
1777 for (x = 0;x < 16;x++)
1783 d = d * d * noise1[y][x] / (7 * 7);
1784 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1785 data2[y][x][3] = 255;
1789 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "beam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1790 particletexture[tex_beam].s1 = 0;
1791 particletexture[tex_beam].t1 = 0;
1792 particletexture[tex_beam].s2 = 1;
1793 particletexture[tex_beam].t2 = 1;
1797 static void r_part_start(void)
1799 particletexturepool = R_AllocTexturePool();
1800 R_InitParticleTexture ();
1803 static void r_part_shutdown(void)
1805 R_FreeTexturePool(&particletexturepool);
1808 static void r_part_newmap(void)
1810 cl_numparticles = 0;
1813 void R_Particles_Init (void)
1815 Cvar_RegisterVariable(&r_drawparticles);
1816 #ifdef WORKINGLQUAKE
1819 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1823 #ifdef WORKINGLQUAKE
1824 void R_InitParticles(void)
1826 CL_Particles_Init();
1831 float particle_vertex3f[12], particle_texcoord2f[8];
1833 #ifdef WORKINGLQUAKE
1834 void R_DrawParticle(particle_t *p)
1837 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1839 const particle_t *p = calldata1;
1842 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1843 particletexture_t *tex;
1845 VectorCopy(p->org, org);
1847 tex = &particletexture[p->texnum];
1848 cr = p->color[0] * (1.0f / 255.0f);
1849 cg = p->color[1] * (1.0f / 255.0f);
1850 cb = p->color[2] * (1.0f / 255.0f);
1851 ca = p->alpha * (1.0f / 255.0f);
1852 if (p->blendmode == PBLEND_MOD)
1863 #ifndef WORKINGLQUAKE
1864 if (fogenabled && p->blendmode != PBLEND_MOD)
1866 VectorSubtract(org, r_vieworigin, fogvec);
1867 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1872 if (p->blendmode == 0)
1874 cr += fogcolor[0] * fog;
1875 cg += fogcolor[1] * fog;
1876 cb += fogcolor[2] * fog;
1880 R_Mesh_Matrix(&r_identitymatrix);
1882 memset(&m, 0, sizeof(m));
1883 m.tex[0] = R_GetTexture(tex->texture);
1884 m.pointer_texcoord[0] = particle_texcoord2f;
1885 m.pointer_vertex = particle_vertex3f;
1888 GL_Color(cr, cg, cb, ca);
1890 if (p->blendmode == 0)
1891 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1892 else if (p->blendmode == 1)
1893 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1895 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1896 GL_DepthMask(false);
1899 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1901 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1904 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1906 VectorNegate(p->vel2, v);
1907 VectorVectors(v, right, up);
1910 VectorVectors(p->vel2, right, up);
1911 VectorScale(right, p->scalex, right);
1912 VectorScale(up, p->scaley, up);
1916 VectorScale(r_viewleft, -p->scalex, right);
1917 VectorScale(r_viewup, p->scaley, up);
1919 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1920 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1921 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1922 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1923 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1924 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1925 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1926 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1927 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1928 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1929 particle_vertex3f[10] = org[1] + right[1] - up[1];
1930 particle_vertex3f[11] = org[2] + right[2] - up[2];
1931 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1932 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1933 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1934 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1936 else if (p->orientation == PARTICLE_SPARK)
1938 VectorMA(p->org, -p->scaley, p->vel, v);
1939 VectorMA(p->org, p->scaley, p->vel, up2);
1940 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1941 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1942 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1943 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1944 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1946 else if (p->orientation == PARTICLE_BEAM)
1948 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1949 VectorSubtract(p->vel2, p->org, up);
1950 VectorNormalizeFast(up);
1951 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1952 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1953 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1954 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1955 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1956 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1959 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1962 if (p->blendmode == 0)
1963 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1964 else if (p->blendmode == 1)
1965 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1967 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1968 glColor4f(cr, cg, cb, ca);
1970 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1971 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1972 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1973 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1976 R_Mesh_Draw(4, 2, polygonelements);
1980 void R_DrawParticles (void)
1983 float minparticledist;
1986 #ifdef WORKINGLQUAKE
1990 // LordHavoc: early out conditions
1991 if ((!cl_numparticles) || (!r_drawparticles.integer))
1994 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1996 #ifdef WORKINGLQUAKE
1997 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1999 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2001 // LordHavoc: only render if not too close
2002 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2003 if (DotProduct(p->org, r_viewforward) >= minparticledist)
2006 glDisable(GL_BLEND);
2007 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2009 // LordHavoc: only render if not too close
2010 c_particles += cl_numparticles;
2011 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2012 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2013 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);