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 CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
29 typedef unsigned char qbyte;
30 #define cl_stainmaps.integer 0
31 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
34 #define CL_EntityParticles R_EntityParticles
35 #define CL_ReadPointFile_f R_ReadPointFile_f
36 #define CL_ParseParticleEffect R_ParseParticleEffect
37 #define CL_ParticleExplosion R_ParticleExplosion
38 #define CL_ParticleExplosion2 R_ParticleExplosion2
39 #define CL_TeleportSplash R_TeleportSplash
40 #define CL_BlobExplosion R_BlobExplosion
41 #define CL_RunParticleEffect R_RunParticleEffect
42 #define CL_LavaSplash R_LavaSplash
43 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
45 vec3_t right1, right2, diff, normal;
47 VectorSubtract (org2, org1, normal);
48 VectorNormalize (normal);
50 // calculate 'right' vector for start
51 VectorSubtract (r_vieworigin, org1, diff);
52 VectorNormalize (diff);
53 CrossProduct (normal, diff, right1);
55 // calculate 'right' vector for end
56 VectorSubtract (r_vieworigin, org2, diff);
57 VectorNormalize (diff);
58 CrossProduct (normal, diff, right2);
60 vert[ 0] = org1[0] + width * right1[0];
61 vert[ 1] = org1[1] + width * right1[1];
62 vert[ 2] = org1[2] + width * right1[2];
63 vert[ 3] = org1[0] - width * right1[0];
64 vert[ 4] = org1[1] - width * right1[1];
65 vert[ 5] = org1[2] - width * right1[2];
66 vert[ 6] = org2[0] - width * right2[0];
67 vert[ 7] = org2[1] - width * right2[1];
68 vert[ 8] = org2[2] - width * right2[2];
69 vert[ 9] = org2[0] + width * right2[0];
70 vert[10] = org2[1] + width * right2[1];
71 vert[11] = org2[2] + width * right2[2];
73 void fractalnoise(qbyte *noise, int size, int startgrid)
75 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
77 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
79 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
80 if (size != (1 << sizepower))
82 Con_Printf("fractalnoise: size must be power of 2\n");
86 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
87 if (startgrid != (1 << gridpower))
89 Con_Printf("fractalnoise: grid must be power of 2\n");
93 startgrid = bound(0, startgrid, size);
95 amplitude = 0xFFFF; // this gets halved before use
96 noisebuf = malloc(size*size*sizeof(int));
97 memset(noisebuf, 0, size*size*sizeof(int));
99 for (g2 = startgrid;g2;g2 >>= 1)
101 // brownian motion (at every smaller level there is random behavior)
103 for (y = 0;y < size;y += g2)
104 for (x = 0;x < size;x += g2)
105 n(x,y) += (rand()&litude);
110 // subdivide, diamond-square algorithm (really this has little to do with squares)
112 for (y = 0;y < size;y += g2)
113 for (x = 0;x < size;x += g2)
114 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
116 for (y = 0;y < size;y += g2)
117 for (x = 0;x < size;x += g2)
119 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
120 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
124 // find range of noise values
126 for (y = 0;y < size;y++)
127 for (x = 0;x < size;x++)
129 if (n(x,y) < min) min = n(x,y);
130 if (n(x,y) > max) max = n(x,y);
134 // normalize noise and copy to output
135 for (y = 0;y < size;y++)
136 for (x = 0;x < size;x++)
137 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
141 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
145 right[0] = forward[2];
146 right[1] = -forward[0];
147 right[2] = forward[1];
149 d = DotProduct(forward, right);
150 right[0] -= d * forward[0];
151 right[1] -= d * forward[1];
152 right[2] -= d * forward[2];
153 VectorNormalize(right);
154 CrossProduct(right, forward, up);
158 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
160 trace_t CL_TraceBox (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hitbmodels, int *hitent, int hitsupercontentsmask, qboolean hitplayers)
167 memset (&trace, 0, sizeof(trace));
169 VectorCopy (end, trace.endpos);
171 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
173 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
178 #include "cl_collision.h"
182 #define MAX_PARTICLES 32768 // default max # of particles at one time
183 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
187 PARTICLE_BILLBOARD = 0,
189 PARTICLE_ORIENTED_DOUBLESIDED = 2,
202 typedef struct particletype_s
205 porientation_t orientation;
212 pt_alphastatic, pt_static, pt_spark, pt_beam, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_smoke, pt_decal, pt_entityparticle, pt_total
216 // must match ptype_t values
217 particletype_t particletype[pt_total] =
219 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
220 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
221 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
222 {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
223 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
224 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
225 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
226 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
227 {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
228 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
229 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
230 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
233 typedef struct particle_s
235 particletype_t *type;
238 vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam
240 float alpha; // 0-255
241 float alphafade; // how much alpha reduces per second
242 float time2; // used for snow fluttering and decal fade
243 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)
244 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
245 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
247 unsigned short owner; // decal stuck to this entity
248 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
249 vec3_t relativeorigin; // decal at this location in entity's coordinate space
250 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
254 static int particlepalette[256] =
256 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
257 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
258 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
259 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
260 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
261 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
262 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
263 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
264 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
265 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
266 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
267 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
268 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
269 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
270 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
271 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
272 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
273 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
274 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
275 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
276 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
277 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
278 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
279 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
280 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
281 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
282 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
283 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
284 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
285 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
286 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
287 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
290 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
292 // texture numbers in particle font
293 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
294 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
295 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
296 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
297 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
298 static const int tex_particle = 63;
299 static const int tex_bubble = 62;
300 static const int tex_raindrop = 61;
301 static const int tex_beam = 60;
303 static int cl_maxparticles;
304 static int cl_numparticles;
305 static int cl_freeparticle;
306 static particle_t *particles;
308 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
309 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
310 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
311 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
312 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
313 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
314 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
315 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
316 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
317 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
318 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
319 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
320 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
321 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
322 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
323 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
324 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
325 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
326 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
327 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
329 void CL_Particles_Clear(void)
333 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
341 void CL_ReadPointFile_f (void);
342 void CL_Particles_Init (void)
346 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
347 i = COM_CheckParm ("-particles");
349 if (i && i < com_argc - 1)
351 cl_maxparticles = (int)(atoi(com_argv[i+1]));
352 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
353 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
356 cl_maxparticles = MAX_PARTICLES;
358 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
360 Cvar_RegisterVariable (&cl_particles);
361 Cvar_RegisterVariable (&cl_particles_quality);
362 Cvar_RegisterVariable (&cl_particles_size);
363 Cvar_RegisterVariable (&cl_particles_bloodshowers);
364 Cvar_RegisterVariable (&cl_particles_blood);
365 Cvar_RegisterVariable (&cl_particles_blood_alpha);
366 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
367 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
368 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
369 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
370 Cvar_RegisterVariable (&cl_particles_explosions_shell);
371 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
372 Cvar_RegisterVariable (&cl_particles_smoke);
373 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
374 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
375 Cvar_RegisterVariable (&cl_particles_sparks);
376 Cvar_RegisterVariable (&cl_particles_bubbles);
377 Cvar_RegisterVariable (&cl_decals);
378 Cvar_RegisterVariable (&cl_decals_time);
379 Cvar_RegisterVariable (&cl_decals_fadetime);
382 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
384 particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
386 CL_Particles_Clear();
389 void CL_Particles_Shutdown (void)
392 // No clue what to do here...
396 // list of all 26 parameters:
397 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
398 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
399 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
400 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
401 // palpha - opacity of particle as 0-255 (can be more than 255)
402 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
403 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
404 // pgravity - how much effect gravity has on the particle (0-1)
405 // 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
406 // px,py,pz - starting origin of particle
407 // pvx,pvy,pvz - starting velocity of particle
408 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
409 particle_t *particle(particletype_t *ptype, int pcolor1, int pcolor2, int ptex, float psize, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pfriction)
412 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
413 ptempcolor = (pcolor1);
414 ptempcolor2 = (pcolor2);
415 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
416 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
417 pcb2 = (ptempcolor2) & 0xFF;
418 if (ptempcolor != ptempcolor2)
420 pcr1 = ((ptempcolor) >> 16) & 0xFF;
421 pcg1 = ((ptempcolor) >> 8) & 0xFF;
422 pcb1 = (ptempcolor) & 0xFF;
423 ptempcolor = rand() & 0xFF;
424 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
425 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
426 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
428 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
429 if (cl_freeparticle >= cl_maxparticles)
431 part = &particles[cl_freeparticle++];
432 if (cl_numparticles < cl_freeparticle)
433 cl_numparticles = cl_freeparticle;
434 memset(part, 0, sizeof(*part));
435 part->type = (ptype);
436 part->color[0] = pcr2;
437 part->color[1] = pcg2;
438 part->color[2] = pcb2;
439 part->color[3] = 0xFF;
441 part->size = (psize);
442 part->alpha = (palpha);
443 part->alphafade = (palphafade);
444 part->gravity = (pgravity);
445 part->bounce = (pbounce);
449 part->vel[0] = (pvx);
450 part->vel[1] = (pvy);
451 part->vel[2] = (pvz);
453 part->friction = (pfriction);
457 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
460 if (!cl_decals.integer)
462 p = particle(particletype + pt_decal, color1, color2, texnum, size, alpha, 0, 0, 0, org[0] + normal[0], org[1] + normal[1], org[2] + normal[2], normal[0], normal[1], normal[2], 0);
466 #ifndef WORKINGLQUAKE
468 p->ownermodel = cl_entities[p->owner].render.model;
469 Matrix4x4_Transform(&cl_entities[p->owner].render.inversematrix, org, p->relativeorigin);
470 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.inversematrix, normal, p->relativedirection);
471 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
476 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
479 float bestfrac, bestorg[3], bestnormal[3];
481 int besthitent = 0, hitent;
484 for (i = 0;i < 32;i++)
487 VectorMA(org, maxdist, org2, org2);
488 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, &hitent, SUPERCONTENTS_SOLID, false);
489 if (bestfrac > trace.fraction)
491 bestfrac = trace.fraction;
493 VectorCopy(trace.endpos, bestorg);
494 VectorCopy(trace.plane.normal, bestnormal);
498 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
506 void CL_EntityParticles (entity_t *ent)
510 float sp, sy, cp, cy;
514 static vec3_t avelocities[NUMVERTEXNORMALS];
515 if (!cl_particles.integer) return;
520 if (!avelocities[0][0])
521 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
522 avelocities[0][i] = (rand()&255) * 0.01;
524 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
526 angle = cl.time * avelocities[i][0];
529 angle = cl.time * avelocities[i][1];
538 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 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);
540 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 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);
546 void CL_ReadPointFile_f (void)
550 char *pointfile = NULL, *pointfilepos, *t, tchar;
551 char name[MAX_OSPATH];
556 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
557 strlcat (name, ".pts", sizeof (name));
559 pointfile = COM_LoadTempFile (name);
561 pointfile = (char *)FS_LoadFile(name, tempmempool, true);
565 Con_Printf("Could not open %s\n", name);
569 Con_Printf("Reading %s...\n", name);
570 VectorClear(leakorg);
573 pointfilepos = pointfile;
574 while (*pointfilepos)
576 while (*pointfilepos == '\n' || *pointfilepos == '\r')
581 while (*t && *t != '\n' && *t != '\r')
585 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
591 VectorCopy(org, leakorg);
594 if (cl_numparticles < cl_maxparticles - 3)
597 particle(particletype + pt_static, particlepalette[(-c)&15], particlepalette[(-c)&15], tex_particle, 2, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0);
600 #ifndef WORKINGLQUAKE
603 VectorCopy(leakorg, org);
604 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
606 particle(particletype + pt_beam, 0xFF0000, 0xFF0000, tex_beam, 64, 255, 0, 0, 0, org[0] - 4096, org[1], org[2], org[0] + 4096, org[1], org[2], 0);
607 particle(particletype + pt_beam, 0x00FF00, 0x00FF00, tex_beam, 64, 255, 0, 0, 0, org[0], org[1] - 4096, org[2], org[0], org[1] + 4096, org[2], 0);
608 particle(particletype + pt_beam, 0x0000FF, 0x0000FF, tex_beam, 64, 255, 0, 0, 0, org[0], org[1], org[2] - 4096, org[0], org[1], org[2] + 4096, 0);
613 CL_ParseParticleEffect
615 Parse an effect out of the server message
618 void CL_ParseParticleEffect (void)
621 int i, count, msgcount, color;
623 MSG_ReadVector(org, cl.protocol);
624 for (i=0 ; i<3 ; i++)
625 dir[i] = MSG_ReadChar () * (1.0/16);
626 msgcount = MSG_ReadByte ();
627 color = MSG_ReadByte ();
634 if (cl_particles_blood_bloodhack.integer)
639 CL_BloodPuff(org, dir, count / 2);
645 CL_BloodPuff(org, dir, count / 2);
649 CL_RunParticleEffect (org, dir, color, count);
658 void CL_ParticleExplosion (vec3_t org)
664 if (cl_stainmaps.integer)
665 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
666 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
668 i = CL_PointSuperContents(org);
669 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
671 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
672 for (i = 0;i < 128 * cl_particles_quality.value;i++)
673 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, (1.0f / cl_particles_quality.value) * lhrandom(128, 255), (1.0f / cl_particles_quality.value) * 128, -0.125, 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), (1.0 / 16.0));
677 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
679 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
681 for (i = 0;i < 32;i++)
686 v2[0] = lhrandom(-48, 48);
687 v2[1] = lhrandom(-48, 48);
688 v2[2] = lhrandom(-48, 48);
690 for (k = 0;k < 16;k++)
692 v[0] = org[0] + lhrandom(-48, 48);
693 v[1] = org[1] + lhrandom(-48, 48);
694 v[2] = org[2] + lhrandom(-48, 48);
695 trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
696 if (trace.fraction >= 0.1)
699 VectorSubtract(trace.endpos, org, v2);
701 VectorScale(v2, 2.0f, v2);
702 particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 12, 32, 64, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0);
706 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
707 for (i = 0;i < 128 * cl_particles_quality.value;i++)
708 particle(particletype + pt_spark, 0x903010, 0xFFD030, tex_particle, 1.0f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 512, 1, 0, org[0], org[1], org[2], lhrandom(-256, 256), lhrandom(-256, 256), lhrandom(-256, 256) + 80, 0.2);
711 if (cl_particles_explosions_shell.integer)
717 CL_ParticleExplosion2
721 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
727 if (!cl_particles.integer) return;
729 for (i = 0;i < 512 * cl_particles_quality.value;i++)
731 VectorRandom (offset);
732 VectorScale (offset, 192, vel);
733 VectorScale (offset, 8, offset);
734 k = particlepalette[colorStart + (i % colorLength)];
735 pscale = lhrandom(0.5, 1.5);
736 particle(particletype + pt_static, k, k, tex_particle, pscale, (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*512, 0, 0, org[0] + offset[0], org[1] + offset[1], org[2] + offset[2], vel[0], vel[1], vel[2], lhrandom(1.5, 3));
746 void CL_BlobExplosion (vec3_t org)
748 CL_ParticleExplosion(org);
757 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
763 CL_ParticleExplosion(org);
766 if (!cl_particles.integer) return;
767 count *= cl_particles_quality.value;
770 k = particlepalette[color + (rand()&7)];
771 if (gamemode == GAME_GOODVSBAD2)
772 particle(particletype + pt_alphastatic, k, k, tex_particle, 5, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 300, 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);
774 particle(particletype + pt_alphastatic, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 512, 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);
778 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
784 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
788 if (!cl_particles.integer) return;
790 if (cl_particles_sparks.integer)
793 count *= cl_particles_quality.value;
796 k = particlepalette[0x68 + (rand() & 7)];
797 particle(particletype + pt_spark, k, k, tex_particle, 0.4f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, gravityscale, 0, org[0], org[1], org[2], lhrandom(-64, 64) + dir[0], lhrandom(-64, 64) + dir[1], lhrandom(0, 128) + dir[2], 0);
802 void CL_Smoke (vec3_t org, vec3_t dir, int count)
808 if (!cl_particles.integer) return;
811 if (cl_particles_smoke.integer)
813 k = count * 0.25 * cl_particles_quality.value;
816 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
817 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
818 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
819 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
820 particle(particletype + pt_smoke, 0x101010, 0x202020, tex_smoke[rand()&7], 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 0, 0, trace.endpos[0], trace.endpos[1], trace.endpos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
825 void CL_BulletMark (vec3_t org)
827 if (cl_stainmaps.integer)
828 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
829 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
832 void CL_PlasmaBurn (vec3_t org)
834 if (cl_stainmaps.integer)
835 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
836 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
839 static float bloodcount = 0;
840 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
845 // bloodcount is used to accumulate counts too small to cause a blood particle
846 if (!cl_particles.integer) return;
847 if (!cl_particles_blood.integer) return;
854 while(bloodcount > 0)
856 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
857 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
858 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
859 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
860 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, trace.endpos[0], trace.endpos[1], trace.endpos[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 1);
861 bloodcount -= 16 / cl_particles_quality.value;
865 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
867 vec3_t org, vel, diff, center, velscale;
868 if (!cl_particles.integer) return;
869 if (!cl_particles_bloodshowers.integer) return;
870 if (!cl_particles_blood.integer) return;
872 VectorSubtract(maxs, mins, diff);
873 center[0] = (mins[0] + maxs[0]) * 0.5;
874 center[1] = (mins[1] + maxs[1]) * 0.5;
875 center[2] = (mins[2] + maxs[2]) * 0.5;
876 velscale[0] = velspeed * 2.0 / diff[0];
877 velscale[1] = velspeed * 2.0 / diff[1];
878 velscale[2] = velspeed * 2.0 / diff[2];
880 bloodcount += count * 5.0f;
881 while (bloodcount > 0)
883 org[0] = lhrandom(mins[0], maxs[0]);
884 org[1] = lhrandom(mins[1], maxs[1]);
885 org[2] = lhrandom(mins[2], maxs[2]);
886 vel[0] = (org[0] - center[0]) * velscale[0];
887 vel[1] = (org[1] - center[1]) * velscale[1];
888 vel[2] = (org[2] - center[2]) * velscale[2];
889 bloodcount -= 16 / cl_particles_quality.value;
890 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 1);
894 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
898 if (!cl_particles.integer) return;
899 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
900 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
901 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
903 count *= cl_particles_quality.value;
906 k = particlepalette[colorbase + (rand()&3)];
907 particle(particletype + pt_alphastatic, k, k, tex_particle, 2, 255 / cl_particles_quality.value, (255 / cl_particles_quality.value) / 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);
911 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
914 float t, z, minz, maxz;
916 if (!cl_particles.integer) return;
917 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
918 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
919 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
920 if (dir[2] < 0) // falling
925 minz = z - fabs(dir[2]) * 0.1;
926 maxz = z + fabs(dir[2]) * 0.1;
927 minz = bound(mins[2], minz, maxs[2]);
928 maxz = bound(mins[2], maxz, maxs[2]);
930 count *= cl_particles_quality.value;
935 count *= 4; // ick, this should be in the mod or maps?
939 k = particlepalette[colorbase + (rand()&3)];
940 if (gamemode == GAME_GOODVSBAD2)
941 particle(particletype + pt_rain, k, k, tex_particle, 20, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
943 particle(particletype + pt_rain, k, k, tex_particle, 0.5, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
949 k = particlepalette[colorbase + (rand()&3)];
950 if (gamemode == GAME_GOODVSBAD2)
951 p = particle(particletype + pt_snow, k, k, tex_particle, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
953 p = particle(particletype + pt_snow, k, k, tex_particle, 1, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
955 VectorCopy(p->vel, p->relativedirection);
959 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
963 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
968 if (!cl_particles.integer) return;
970 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
971 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
972 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
974 center[0] = (mins[0] + maxs[0]) * 0.5f;
975 center[1] = (mins[1] + maxs[1]) * 0.5f;
976 center[2] = (mins[2] + maxs[2]) * 0.5f;
978 count *= cl_particles_quality.value;
981 k = particlepalette[224 + (rand()&15)];
982 o[0] = lhrandom(mins[0], maxs[0]);
983 o[1] = lhrandom(mins[1], maxs[1]);
984 o[2] = lhrandom(mins[2], maxs[2]);
985 VectorSubtract(o, center, v);
987 VectorScale(v, 100, v);
988 v[2] += sv_gravity.value * 0.15f;
989 particle(particletype + pt_static, 0x903010, 0xFFD030, tex_particle, 1.5, lhrandom(64, 128) / cl_particles_quality.value, 128 / cl_particles_quality.value, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0.2);
993 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
997 if (!cl_particles.integer) return;
998 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
999 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1000 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1002 count *= cl_particles_quality.value;
1005 k = particlepalette[224 + (rand()&15)];
1006 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -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), 1);
1008 particle(particletype + pt_static, 0x303030, 0x606060, tex_smoke[rand()&7], 6, lhrandom(48, 96) / cl_particles_quality.value, 64 / cl_particles_quality.value, 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);
1012 void CL_Flames (vec3_t org, vec3_t vel, int count)
1015 if (!cl_particles.integer) return;
1017 count *= cl_particles_quality.value;
1020 k = particlepalette[224 + (rand()&15)];
1021 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 1);
1033 void CL_LavaSplash (vec3_t origin)
1035 float i, j, inc, vel;
1038 if (!cl_particles.integer) return;
1040 inc = 32 / cl_particles_quality.value;
1041 for (i = -128;i < 128;i += inc)
1043 for (j = -128;j < 128;j += inc)
1045 dir[0] = j + lhrandom(0, 8);
1046 dir[1] = i + lhrandom(0, 8);
1048 org[0] = origin[0] + dir[0];
1049 org[1] = origin[1] + dir[1];
1050 org[2] = origin[2] + lhrandom(0, 64);
1051 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1052 if (gamemode == GAME_GOODVSBAD2)
1054 k = particlepalette[0 + (rand()&255)];
1055 l = particlepalette[0 + (rand()&255)];
1056 particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 1, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0);
1060 k = l = particlepalette[224 + (rand()&7)];
1061 particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0);
1073 void CL_TeleportSplash (vec3_t org)
1076 if (!cl_particles.integer) return;
1078 inc = 8 / cl_particles_quality.value;
1079 for (i = -16;i < 16;i += inc)
1080 for (j = -16;j < 16;j += inc)
1081 for (k = -24;k < 32;k += inc)
1082 particle(particletype + pt_static, 0xA0A0A0, 0xFFFFFF, tex_particle, 10, inc * lhrandom(8, 16), inc * 32, 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), 1);
1085 #ifdef WORKINGLQUAKE
1086 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1088 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1091 vec3_t vec, dir, vel, pos;
1092 float len, dec, speed, qd;
1093 int smoke, blood, bubbles;
1094 #ifdef WORKINGLQUAKE
1098 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1101 VectorSubtract(end, start, dir);
1102 VectorNormalize(dir);
1104 VectorSubtract (end, start, vec);
1105 #ifdef WORKINGLQUAKE
1106 len = VectorNormalize (vec);
1108 speed = 1.0f / cl.frametime;
1109 VectorSubtract(end, start, vel);
1111 len = VectorNormalizeLength (vec);
1112 dec = -ent->persistent.trail_time;
1113 ent->persistent.trail_time += len;
1114 if (ent->persistent.trail_time < 0.01f)
1117 // if we skip out, leave it reset
1118 ent->persistent.trail_time = 0.0f;
1120 speed = ent->state_current.time - ent->state_previous.time;
1122 speed = 1.0f / speed;
1123 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1124 color = particlepalette[color];
1126 VectorScale(vel, speed, vel);
1128 // advance into this frame to reach the first puff location
1129 VectorMA(start, dec, vec, pos);
1132 smoke = cl_particles.integer && cl_particles_smoke.integer;
1133 blood = cl_particles.integer && cl_particles_blood.integer;
1134 #ifdef WORKINGLQUAKE
1135 contents = CL_PointQ1Contents(pos);
1136 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1138 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1140 qd = 1.0f / cl_particles_quality.value;
1146 case 0: // rocket trail
1150 particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*125, qd*cl_particles_smoke_alphafade.value*125, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1151 particle(particletype + pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*288, qd*cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], lhrandom(-20, 20), lhrandom(-20, 20), lhrandom(-20, 20), 0);
1154 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, qd*lhrandom(64, 255), qd*256, -0.25, 1.5, pos[0], pos[1], pos[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), (1.0 / 16.0));
1157 case 1: // grenade trail
1158 // FIXME: make it gradually stop smoking
1161 particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*100, qd*cl_particles_smoke_alphafade.value*100, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1166 case 4: // slight blood
1169 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 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), 1);
1172 case 3: // green tracer
1176 if (gamemode == GAME_GOODVSBAD2)
1177 particle(particletype + pt_static, 0x00002E, 0x000030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1179 particle(particletype + pt_static, 0x002000, 0x003000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1183 case 5: // flame tracer
1186 particle(particletype + pt_static, 0x301000, 0x502000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1189 case 6: // voor trail
1193 if (gamemode == GAME_GOODVSBAD2)
1194 particle(particletype + pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, qd*255, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1195 else if (gamemode == GAME_PRYDON)
1196 particle(particletype + pt_static, 0x103040, 0x204050, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1198 particle(particletype + pt_static, 0x502030, 0x502030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1201 #ifndef WORKINGLQUAKE
1202 case 7: // Nehahra smoke tracer
1205 particle(particletype + pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, qd*64, qd*320, 0, 0, pos[0], pos[1], pos[2], lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(0, 16), 0);
1207 case 8: // Nexuiz plasma trail
1210 particle(particletype + pt_static, 0x283880, 0x283880, tex_particle, 4, qd*255, qd*1024, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0);
1212 case 9: // glow trail
1215 particle(particletype + pt_alphastatic, color, color, tex_particle, 5, qd*128, qd*320, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0);
1219 Sys_Error("CL_RocketTrail: unknown trail type %i\n", type);
1222 // advance to next time and position
1224 VectorMA (pos, dec, vec, pos);
1226 #ifndef WORKINGLQUAKE
1227 ent->persistent.trail_time = len;
1231 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1233 int tempcolor2, cr, cg, cb;
1237 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1238 particle(particletype + pt_beam, tempcolor2, tempcolor2, tex_beam, radius, alpha * 255, alpha * 255 / lifetime, 0, 0, start[0], start[1], start[2], end[0], end[1], end[2], 0);
1241 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1244 if (!cl_particles.integer) return;
1247 if (cl_particles_smoke.integer)
1248 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1249 particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 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, 0);
1252 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1255 if (!cl_particles.integer) return;
1257 if (cl_stainmaps.integer)
1258 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1259 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1262 if (cl_particles_smoke.integer)
1263 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1264 particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 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), 0);
1267 if (cl_particles_sparks.integer)
1268 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1269 particle(particletype + pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, lhrandom(64, 255) / cl_particles_quality.value, 512 / cl_particles_quality.value, 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);
1277 void CL_MoveParticles (void)
1280 int i, maxparticle, j, a, content;
1281 float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
1285 // LordHavoc: early out condition
1286 if (!cl_numparticles)
1288 cl_freeparticle = 0;
1292 #ifdef WORKINGLQUAKE
1293 frametime = cl.frametime;
1295 frametime = cl.time - cl.oldtime;
1297 gravity = frametime * sv_gravity.value;
1298 dvel = 1+4*frametime;
1299 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1303 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1310 p->alpha -= p->alphafade * frametime;
1318 if (p->type->orientation != PARTICLE_BEAM)
1320 VectorCopy(p->org, oldorg);
1321 VectorMA(p->org, frametime, p->vel, p->org);
1322 VectorCopy(p->org, org);
1325 if (p->type == particletype + pt_rain)
1327 // raindrop - splash on solid/water/slime/lava
1328 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
1329 if (trace.fraction < 1)
1331 // convert from a raindrop particle to a rainsplash decal
1332 VectorCopy(trace.endpos, p->org);
1333 VectorCopy(trace.plane.normal, p->vel);
1334 VectorAdd(p->org, p->vel, p->org);
1335 p->type = particletype + pt_raindecal;
1336 p->texnum = tex_rainsplash[0];
1338 p->alphafade = p->alpha / 0.4;
1345 else if (p->type == particletype + pt_blood)
1347 // blood - splash on solid
1348 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
1349 if (trace.fraction < 1)
1351 // convert from a blood particle to a blood decal
1352 VectorCopy(trace.endpos, p->org);
1353 VectorCopy(trace.plane.normal, p->vel);
1354 VectorAdd(p->org, p->vel, p->org);
1355 #ifndef WORKINGLQUAKE
1356 if (cl_stainmaps.integer)
1357 R_Stain(p->org, 32, 32, 16, 16, p->alpha * p->size * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->size * (1.0f / 40.0f));
1359 if (!cl_decals.integer)
1365 p->type = particletype + pt_decal;
1366 p->texnum = tex_blooddecal[rand()&7];
1367 #ifndef WORKINGLQUAKE
1369 p->ownermodel = cl_entities[hitent].render.model;
1370 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1371 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1383 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
1384 if (trace.fraction < 1)
1386 VectorCopy(trace.endpos, p->org);
1394 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1395 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1396 if (DotProduct(p->vel, p->vel) < 0.03)
1397 VectorClear(p->vel);
1402 p->vel[2] -= p->gravity * gravity;
1406 f = p->friction * frametime;
1407 #ifdef WORKINGLQUAKE
1408 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1410 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1414 VectorScale(p->vel, f, p->vel);
1418 if (p->type != particletype + pt_static)
1420 switch (p->type - particletype)
1422 case pt_entityparticle:
1423 // particle that removes itself after one rendered frame
1430 #ifdef WORKINGLQUAKE
1431 a = CL_PointQ1Contents(p->org);
1432 if (a <= CONTENTS_WATER)
1434 a = CL_PointSuperContents(p->org);
1435 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1438 p->size += frametime * 8;
1439 //p->alpha -= bloodwaterfade;
1442 p->vel[2] -= gravity;
1443 #ifdef WORKINGLQUAKE
1444 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1446 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1451 #ifdef WORKINGLQUAKE
1452 a = CL_PointQ1Contents(p->org);
1453 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1455 a = CL_PointSuperContents(p->org);
1456 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1464 #ifdef WORKINGLQUAKE
1465 a = CL_PointQ1Contents(p->org);
1466 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1468 a = CL_PointSuperContents(p->org);
1469 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1474 if (cl.time > p->time2)
1477 p->time2 = cl.time + (rand() & 3) * 0.1;
1478 p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
1479 p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1480 //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
1482 #ifdef WORKINGLQUAKE
1483 a = CL_PointQ1Contents(p->org);
1484 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1486 a = CL_PointSuperContents(p->org);
1487 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1492 //p->size += frametime * 15;
1495 // FIXME: this has fairly wacky handling of alpha
1496 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1497 #ifndef WORKINGLQUAKE
1498 if (cl_entities[p->owner].render.model == p->ownermodel)
1500 Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1501 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1508 a = max(0, (cl.time - p->time2) * 40);
1510 p->texnum = tex_rainsplash[a];
1519 cl_numparticles = maxparticle + 1;
1520 cl_freeparticle = 0;
1523 #define MAX_PARTICLETEXTURES 64
1524 // particletexture_t is a rectangle in the particlefonttexture
1525 typedef struct particletexture_s
1527 rtexture_t *texture;
1528 float s1, t1, s2, t2;
1533 static int particlefonttexture;
1535 static rtexturepool_t *particletexturepool;
1536 static rtexture_t *particlefonttexture;
1538 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1540 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1542 #define PARTICLETEXTURESIZE 64
1543 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1545 static qbyte shadebubble(float dx, float dy, vec3_t light)
1549 dz = 1 - (dx*dx+dy*dy);
1550 if (dz > 0) // it does hit the sphere
1554 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1555 VectorNormalize(normal);
1556 dot = DotProduct(normal, light);
1557 if (dot > 0.5) // interior reflection
1558 f += ((dot * 2) - 1);
1559 else if (dot < -0.5) // exterior reflection
1560 f += ((dot * -2) - 1);
1562 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1563 VectorNormalize(normal);
1564 dot = DotProduct(normal, light);
1565 if (dot > 0.5) // interior reflection
1566 f += ((dot * 2) - 1);
1567 else if (dot < -0.5) // exterior reflection
1568 f += ((dot * -2) - 1);
1570 f += 16; // just to give it a haze so you can see the outline
1571 f = bound(0, f, 255);
1578 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1580 int basex, basey, y;
1581 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1582 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1583 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1584 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1585 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1586 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1587 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1588 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1591 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1594 float cx, cy, dx, dy, f, iradius;
1596 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1597 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1598 iradius = 1.0f / radius;
1599 alpha *= (1.0f / 255.0f);
1600 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1602 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1606 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1609 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1610 d[0] += f * (red - d[0]);
1611 d[1] += f * (green - d[1]);
1612 d[2] += f * (blue - d[2]);
1618 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1621 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1623 data[0] = bound(minr, data[0], maxr);
1624 data[1] = bound(ming, data[1], maxg);
1625 data[2] = bound(minb, data[2], maxb);
1629 void particletextureinvert(qbyte *data)
1632 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1634 data[0] = 255 - data[0];
1635 data[1] = 255 - data[1];
1636 data[2] = 255 - data[2];
1640 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1641 static void R_InitBloodTextures (qbyte *particletexturedata)
1644 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1647 for (i = 0;i < 8;i++)
1649 memset(&data[0][0][0], 255, sizeof(data));
1650 for (k = 0;k < 24;k++)
1651 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1652 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1653 particletextureinvert(&data[0][0][0]);
1654 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1658 for (i = 0;i < 8;i++)
1660 memset(&data[0][0][0], 255, sizeof(data));
1662 for (j = 1;j < 10;j++)
1663 for (k = min(j, m - 1);k < m;k++)
1664 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1665 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1666 particletextureinvert(&data[0][0][0]);
1667 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1672 static void R_InitParticleTexture (void)
1674 int x, y, d, i, k, m;
1675 float dx, dy, radius, f, f2;
1676 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1678 qbyte *particletexturedata;
1680 // a note: decals need to modulate (multiply) the background color to
1681 // properly darken it (stain), and they need to be able to alpha fade,
1682 // this is a very difficult challenge because it means fading to white
1683 // (no change to background) rather than black (darkening everything
1684 // behind the whole decal polygon), and to accomplish this the texture is
1685 // inverted (dark red blood on white background becomes brilliant cyan
1686 // and white on black background) so we can alpha fade it to black, then
1687 // we invert it again during the blendfunc to make it work...
1689 particletexturedata = (qbyte *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1690 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1693 for (i = 0;i < 8;i++)
1695 memset(&data[0][0][0], 255, sizeof(data));
1698 qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1700 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1701 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1703 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1705 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1706 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1708 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1709 d = (noise2[y][x] - 128) * 3 + 192;
1711 d = d * (1-(dx*dx+dy*dy));
1712 d = (d * noise1[y][x]) >> 7;
1713 d = bound(0, d, 255);
1714 data[y][x][3] = (qbyte) d;
1721 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1725 for (i = 0;i < 16;i++)
1727 memset(&data[0][0][0], 255, sizeof(data));
1728 radius = i * 3.0f / 4.0f / 16.0f;
1729 f2 = 255.0f * ((15.0f - i) / 15.0f);
1730 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1732 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1733 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1735 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1736 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1737 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1740 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1744 memset(&data[0][0][0], 255, sizeof(data));
1745 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1747 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1748 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1750 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1751 d = 256 * (1 - (dx*dx+dy*dy));
1752 d = bound(0, d, 255);
1753 data[y][x][3] = (qbyte) d;
1756 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1759 memset(&data[0][0][0], 255, sizeof(data));
1760 light[0] = 1;light[1] = 1;light[2] = 1;
1761 VectorNormalize(light);
1762 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1764 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1765 // stretch upper half of bubble by +50% and shrink lower half by -50%
1766 // (this gives an elongated teardrop shape)
1768 dy = (dy - 0.5f) * 2.0f;
1770 dy = (dy - 0.5f) / 1.5f;
1771 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1773 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1774 // shrink bubble width to half
1776 data[y][x][3] = shadebubble(dx, dy, light);
1779 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1782 memset(&data[0][0][0], 255, sizeof(data));
1783 light[0] = 1;light[1] = 1;light[2] = 1;
1784 VectorNormalize(light);
1785 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1787 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1788 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1790 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1791 data[y][x][3] = shadebubble(dx, dy, light);
1794 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1796 // Blood particles and blood decals
1797 R_InitBloodTextures (particletexturedata);
1800 for (i = 0;i < 8;i++)
1802 memset(&data[0][0][0], 255, sizeof(data));
1803 for (k = 0;k < 12;k++)
1804 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1805 for (k = 0;k < 3;k++)
1806 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1807 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1808 particletextureinvert(&data[0][0][0]);
1809 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1813 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1814 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1815 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1819 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1822 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1823 if (!particlefonttexture)
1824 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1825 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1826 particletexture[i].texture = particlefonttexture;
1829 fractalnoise(&noise3[0][0], 64, 4);
1831 for (y = 0;y < 64;y++)
1833 dy = (y - 0.5f*64) / (64*0.5f-1);
1834 for (x = 0;x < 16;x++)
1836 dx = (x - 0.5f*16) / (16*0.5f-2);
1837 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1838 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1839 data2[y][x][3] = 255;
1844 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1847 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1848 if (!particletexture[tex_beam].texture)
1849 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1850 particletexture[tex_beam].s1 = 0;
1851 particletexture[tex_beam].t1 = 0;
1852 particletexture[tex_beam].s2 = 1;
1853 particletexture[tex_beam].t2 = 1;
1855 Mem_Free(particletexturedata);
1858 static void r_part_start(void)
1860 particletexturepool = R_AllocTexturePool();
1861 R_InitParticleTexture ();
1864 static void r_part_shutdown(void)
1866 R_FreeTexturePool(&particletexturepool);
1869 static void r_part_newmap(void)
1871 cl_numparticles = 0;
1872 cl_freeparticle = 0;
1875 void R_Particles_Init (void)
1877 Cvar_RegisterVariable(&r_drawparticles);
1878 #ifdef WORKINGLQUAKE
1881 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1885 #ifdef WORKINGLQUAKE
1886 void R_InitParticles(void)
1888 CL_Particles_Init();
1893 float particle_vertex3f[12], particle_texcoord2f[8];
1895 #ifdef WORKINGLQUAKE
1896 void R_DrawParticle(particle_t *p)
1899 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1901 const particle_t *p = (particle_t *)calldata1;
1905 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, size;
1906 particletexture_t *tex;
1908 VectorCopy(p->org, org);
1910 blendmode = p->type->blendmode;
1911 tex = &particletexture[p->texnum];
1912 cr = p->color[0] * (1.0f / 255.0f);
1913 cg = p->color[1] * (1.0f / 255.0f);
1914 cb = p->color[2] * (1.0f / 255.0f);
1915 ca = p->alpha * (1.0f / 255.0f);
1916 if (blendmode == PBLEND_MOD)
1926 #ifndef WORKINGLQUAKE
1927 if (p->type->lighting)
1929 float ambient[3], diffuse[3], diffusenormal[3];
1930 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
1931 cr *= (ambient[0] + 0.5 * diffuse[0]);
1932 cg *= (ambient[1] + 0.5 * diffuse[1]);
1933 cb *= (ambient[2] + 0.5 * diffuse[2]);
1937 VectorSubtract(org, r_vieworigin, fogvec);
1938 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1943 if (blendmode == PBLEND_ALPHA)
1945 cr += fogcolor[0] * fog;
1946 cg += fogcolor[1] * fog;
1947 cb += fogcolor[2] * fog;
1951 R_Mesh_Matrix(&r_identitymatrix);
1953 memset(&m, 0, sizeof(m));
1954 m.tex[0] = R_GetTexture(tex->texture);
1955 m.pointer_texcoord[0] = particle_texcoord2f;
1956 m.pointer_vertex = particle_vertex3f;
1959 GL_Color(cr, cg, cb, ca);
1961 if (blendmode == PBLEND_ALPHA)
1962 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1963 else if (blendmode == PBLEND_ADD)
1964 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1965 else //if (blendmode == PBLEND_MOD)
1966 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1967 GL_DepthMask(false);
1970 size = p->size * cl_particles_size.value;
1971 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1973 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1976 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1978 VectorNegate(p->vel, v);
1979 VectorVectors(v, right, up);
1982 VectorVectors(p->vel, right, up);
1983 VectorScale(right, size, right);
1984 VectorScale(up, size, up);
1988 VectorScale(r_viewleft, -size, right);
1989 VectorScale(r_viewup, size, up);
1991 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1992 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1993 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1994 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1995 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1996 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1997 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1998 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1999 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
2000 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
2001 particle_vertex3f[10] = org[1] + right[1] - up[1];
2002 particle_vertex3f[11] = org[2] + right[2] - up[2];
2003 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2004 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2005 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2006 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2008 else if (p->type->orientation == PARTICLE_SPARK)
2010 VectorMA(p->org, -0.02, p->vel, v);
2011 VectorMA(p->org, 0.02, p->vel, up2);
2012 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2013 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2014 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2015 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2016 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2018 else if (p->type->orientation == PARTICLE_BEAM)
2020 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2021 VectorSubtract(p->vel, p->org, up);
2022 VectorNormalize(up);
2023 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2024 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2025 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2026 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2027 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2028 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2032 Con_Printf("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2037 if (blendmode == PBLEND_ALPHA)
2038 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2039 else if (blendmode == PBLEND_ADD)
2040 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2041 else //if (blendmode == PBLEND_MOD)
2042 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2043 glColor4f(cr, cg, cb, ca);
2045 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2046 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2047 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2048 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2051 R_Mesh_Draw(0, 4, 2, polygonelements);
2055 void R_DrawParticles (void)
2058 float minparticledist;
2061 #ifdef WORKINGLQUAKE
2065 // LordHavoc: early out conditions
2066 if ((!cl_numparticles) || (!r_drawparticles.integer))
2069 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2071 #ifdef WORKINGLQUAKE
2072 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2074 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2076 // LordHavoc: only render if not too close
2077 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2078 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2081 glDisable(GL_BLEND);
2082 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2084 // LordHavoc: only render if not too close
2085 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2089 renderstats.particles++;
2090 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2092 if (p->type == particletype + pt_decal)
2093 R_DrawParticleCallback(p, 0);
2095 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);