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 = FS_LoadFile(name, tempmempool, true);
565 Con_Printf("Could not open %s\n", name);
569 Con_Printf("Reading %s...\n", name);
572 pointfilepos = pointfile;
573 while (*pointfilepos)
575 while (*pointfilepos == '\n' || *pointfilepos == '\r')
580 while (*t && *t != '\n' && *t != '\r')
584 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
590 VectorCopy(org, leakorg);
593 if (cl_numparticles < cl_maxparticles - 3)
596 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);
599 #ifndef WORKINGLQUAKE
602 VectorCopy(leakorg, org);
603 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
605 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);
606 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);
607 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);
612 CL_ParseParticleEffect
614 Parse an effect out of the server message
617 void CL_ParseParticleEffect (void)
620 int i, count, msgcount, color;
622 MSG_ReadVector(org, cl.protocol);
623 for (i=0 ; i<3 ; i++)
624 dir[i] = MSG_ReadChar () * (1.0/16);
625 msgcount = MSG_ReadByte ();
626 color = MSG_ReadByte ();
633 if (cl_particles_blood_bloodhack.integer)
638 CL_BloodPuff(org, dir, count / 2);
644 CL_BloodPuff(org, dir, count / 2);
648 CL_RunParticleEffect (org, dir, color, count);
657 void CL_ParticleExplosion (vec3_t org)
663 if (cl_stainmaps.integer)
664 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
665 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
667 i = CL_PointSuperContents(org);
668 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
670 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
671 for (i = 0;i < 128 * cl_particles_quality.value;i++)
672 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));
676 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
678 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
680 for (i = 0;i < 32;i++)
685 v2[0] = lhrandom(-48, 48);
686 v2[1] = lhrandom(-48, 48);
687 v2[2] = lhrandom(-48, 48);
689 for (k = 0;k < 16;k++)
691 v[0] = org[0] + lhrandom(-48, 48);
692 v[1] = org[1] + lhrandom(-48, 48);
693 v[2] = org[2] + lhrandom(-48, 48);
694 trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
695 if (trace.fraction >= 0.1)
698 VectorSubtract(trace.endpos, org, v2);
700 VectorScale(v2, 2.0f, v2);
701 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);
705 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
706 for (i = 0;i < 128 * cl_particles_quality.value;i++)
707 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);
710 if (cl_particles_explosions_shell.integer)
716 CL_ParticleExplosion2
720 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
726 if (!cl_particles.integer) return;
728 for (i = 0;i < 512 * cl_particles_quality.value;i++)
730 VectorRandom (offset);
731 VectorScale (offset, 192, vel);
732 VectorScale (offset, 8, offset);
733 k = particlepalette[colorStart + (i % colorLength)];
734 pscale = lhrandom(0.5, 1.5);
735 particle(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));
745 void CL_BlobExplosion (vec3_t org)
747 CL_ParticleExplosion(org);
756 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
762 CL_ParticleExplosion(org);
765 if (!cl_particles.integer) return;
766 count *= cl_particles_quality.value;
769 k = particlepalette[color + (rand()&7)];
770 if (gamemode == GAME_GOODVSBAD2)
771 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);
773 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);
777 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
783 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
787 if (!cl_particles.integer) return;
789 if (cl_particles_sparks.integer)
792 count *= cl_particles_quality.value;
795 k = particlepalette[0x68 + (rand() & 7)];
796 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);
801 void CL_Smoke (vec3_t org, vec3_t dir, int count)
807 if (!cl_particles.integer) return;
810 if (cl_particles_smoke.integer)
812 k = count * 0.25 * cl_particles_quality.value;
815 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
816 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
817 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
818 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
819 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);
824 void CL_BulletMark (vec3_t org)
826 if (cl_stainmaps.integer)
827 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
828 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
831 void CL_PlasmaBurn (vec3_t org)
833 if (cl_stainmaps.integer)
834 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
835 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
838 static float bloodcount = 0;
839 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
844 // bloodcount is used to accumulate counts too small to cause a blood particle
845 if (!cl_particles.integer) return;
846 if (!cl_particles_blood.integer) return;
853 while(bloodcount > 0)
855 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
856 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
857 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
858 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
859 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);
860 bloodcount -= 16 / cl_particles_quality.value;
864 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
866 vec3_t org, vel, diff, center, velscale;
867 if (!cl_particles.integer) return;
868 if (!cl_particles_bloodshowers.integer) return;
869 if (!cl_particles_blood.integer) return;
871 VectorSubtract(maxs, mins, diff);
872 center[0] = (mins[0] + maxs[0]) * 0.5;
873 center[1] = (mins[1] + maxs[1]) * 0.5;
874 center[2] = (mins[2] + maxs[2]) * 0.5;
875 velscale[0] = velspeed * 2.0 / diff[0];
876 velscale[1] = velspeed * 2.0 / diff[1];
877 velscale[2] = velspeed * 2.0 / diff[2];
879 bloodcount += count * 5.0f;
880 while (bloodcount > 0)
882 org[0] = lhrandom(mins[0], maxs[0]);
883 org[1] = lhrandom(mins[1], maxs[1]);
884 org[2] = lhrandom(mins[2], maxs[2]);
885 vel[0] = (org[0] - center[0]) * velscale[0];
886 vel[1] = (org[1] - center[1]) * velscale[1];
887 vel[2] = (org[2] - center[2]) * velscale[2];
888 bloodcount -= 16 / cl_particles_quality.value;
889 particle(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);
893 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
897 if (!cl_particles.integer) return;
898 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
899 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
900 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
902 count *= cl_particles_quality.value;
905 k = particlepalette[colorbase + (rand()&3)];
906 particle(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);
910 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
913 float t, z, minz, maxz;
915 if (!cl_particles.integer) return;
916 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
917 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
918 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
919 if (dir[2] < 0) // falling
924 minz = z - fabs(dir[2]) * 0.1;
925 maxz = z + fabs(dir[2]) * 0.1;
926 minz = bound(mins[2], minz, maxs[2]);
927 maxz = bound(mins[2], maxz, maxs[2]);
929 count *= cl_particles_quality.value;
934 count *= 4; // ick, this should be in the mod or maps?
938 k = particlepalette[colorbase + (rand()&3)];
939 if (gamemode == GAME_GOODVSBAD2)
940 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);
942 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);
948 k = particlepalette[colorbase + (rand()&3)];
949 if (gamemode == GAME_GOODVSBAD2)
950 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);
952 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);
954 VectorCopy(p->vel, p->relativedirection);
958 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
962 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
967 if (!cl_particles.integer) return;
969 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
970 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
971 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
973 center[0] = (mins[0] + maxs[0]) * 0.5f;
974 center[1] = (mins[1] + maxs[1]) * 0.5f;
975 center[2] = (mins[2] + maxs[2]) * 0.5f;
977 count *= cl_particles_quality.value;
980 k = particlepalette[224 + (rand()&15)];
981 o[0] = lhrandom(mins[0], maxs[0]);
982 o[1] = lhrandom(mins[1], maxs[1]);
983 o[2] = lhrandom(mins[2], maxs[2]);
984 VectorSubtract(o, center, v);
986 VectorScale(v, 100, v);
987 v[2] += sv_gravity.value * 0.15f;
988 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);
992 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
996 if (!cl_particles.integer) return;
997 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
998 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
999 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1001 count *= cl_particles_quality.value;
1004 k = particlepalette[224 + (rand()&15)];
1005 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);
1007 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);
1011 void CL_Flames (vec3_t org, vec3_t vel, int count)
1014 if (!cl_particles.integer) return;
1016 count *= cl_particles_quality.value;
1019 k = particlepalette[224 + (rand()&15)];
1020 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);
1032 void CL_LavaSplash (vec3_t origin)
1034 float i, j, inc, vel;
1037 if (!cl_particles.integer) return;
1039 inc = 32 / cl_particles_quality.value;
1040 for (i = -128;i < 128;i += inc)
1042 for (j = -128;j < 128;j += inc)
1044 dir[0] = j + lhrandom(0, 8);
1045 dir[1] = i + lhrandom(0, 8);
1047 org[0] = origin[0] + dir[0];
1048 org[1] = origin[1] + dir[1];
1049 org[2] = origin[2] + lhrandom(0, 64);
1050 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1051 if (gamemode == GAME_GOODVSBAD2)
1053 k = particlepalette[0 + (rand()&255)];
1054 l = particlepalette[0 + (rand()&255)];
1055 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);
1059 k = l = particlepalette[224 + (rand()&7)];
1060 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);
1072 void CL_TeleportSplash (vec3_t org)
1075 if (!cl_particles.integer) return;
1077 inc = 8 / cl_particles_quality.value;
1078 for (i = -16;i < 16;i += inc)
1079 for (j = -16;j < 16;j += inc)
1080 for (k = -24;k < 32;k += inc)
1081 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);
1084 #ifdef WORKINGLQUAKE
1085 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1087 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1090 vec3_t vec, dir, vel, pos;
1091 float len, dec, speed, qd;
1092 int smoke, blood, bubbles;
1093 #ifdef WORKINGLQUAKE
1097 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1100 VectorSubtract(end, start, dir);
1101 VectorNormalize(dir);
1103 VectorSubtract (end, start, vec);
1104 #ifdef WORKINGLQUAKE
1105 len = VectorNormalize (vec);
1107 speed = 1.0f / cl.frametime;
1108 VectorSubtract(end, start, vel);
1110 len = VectorNormalizeLength (vec);
1111 dec = -ent->persistent.trail_time;
1112 ent->persistent.trail_time += len;
1113 if (ent->persistent.trail_time < 0.01f)
1116 // if we skip out, leave it reset
1117 ent->persistent.trail_time = 0.0f;
1119 speed = ent->state_current.time - ent->state_previous.time;
1121 speed = 1.0f / speed;
1122 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1123 color = particlepalette[color];
1125 VectorScale(vel, speed, vel);
1127 // advance into this frame to reach the first puff location
1128 VectorMA(start, dec, vec, pos);
1131 smoke = cl_particles.integer && cl_particles_smoke.integer;
1132 blood = cl_particles.integer && cl_particles_blood.integer;
1133 #ifdef WORKINGLQUAKE
1134 contents = CL_PointQ1Contents(pos);
1135 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1137 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1139 qd = 1.0f / cl_particles_quality.value;
1145 case 0: // rocket trail
1149 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);
1150 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);
1153 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));
1156 case 1: // grenade trail
1157 // FIXME: make it gradually stop smoking
1160 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);
1165 case 4: // slight blood
1168 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);
1171 case 3: // green tracer
1175 if (gamemode == GAME_GOODVSBAD2)
1176 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);
1178 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);
1182 case 5: // flame tracer
1185 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);
1188 case 6: // voor trail
1192 if (gamemode == GAME_GOODVSBAD2)
1193 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);
1194 else if (gamemode == GAME_PRYDON)
1195 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);
1197 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);
1200 #ifndef WORKINGLQUAKE
1201 case 7: // Nehahra smoke tracer
1204 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);
1206 case 8: // Nexuiz plasma trail
1209 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);
1211 case 9: // glow trail
1214 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);
1218 Sys_Error("CL_RocketTrail: unknown trail type %i\n", type);
1221 // advance to next time and position
1223 VectorMA (pos, dec, vec, pos);
1225 #ifndef WORKINGLQUAKE
1226 ent->persistent.trail_time = len;
1230 void CL_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(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);
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(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);
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(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);
1266 if (cl_particles_sparks.integer)
1267 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1268 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);
1276 void CL_MoveParticles (void)
1279 int i, maxparticle, j, a, content;
1280 float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
1284 // LordHavoc: early out condition
1285 if (!cl_numparticles)
1287 cl_freeparticle = 0;
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;
1302 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1309 p->alpha -= p->alphafade * frametime;
1317 if (p->type->orientation != PARTICLE_BEAM)
1319 VectorCopy(p->org, oldorg);
1320 VectorMA(p->org, frametime, p->vel, p->org);
1321 VectorCopy(p->org, org);
1324 if (p->type == particletype + pt_rain)
1326 // raindrop - splash on solid/water/slime/lava
1327 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
1328 if (trace.fraction < 1)
1330 // convert from a raindrop particle to a rainsplash decal
1331 VectorCopy(trace.endpos, p->org);
1332 VectorCopy(trace.plane.normal, p->vel);
1333 VectorAdd(p->org, p->vel, p->org);
1334 p->type = particletype + pt_raindecal;
1335 p->texnum = tex_rainsplash[0];
1337 p->alphafade = p->alpha / 0.4;
1344 else if (p->type == particletype + pt_blood)
1346 // blood - splash on solid
1347 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
1348 if (trace.fraction < 1)
1350 // convert from a blood particle to a blood decal
1351 VectorCopy(trace.endpos, p->org);
1352 VectorCopy(trace.plane.normal, p->vel);
1353 VectorAdd(p->org, p->vel, p->org);
1354 #ifndef WORKINGLQUAKE
1355 if (cl_stainmaps.integer)
1356 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));
1358 if (!cl_decals.integer)
1364 p->type = particletype + pt_decal;
1365 p->texnum = tex_blooddecal[rand()&7];
1366 #ifndef WORKINGLQUAKE
1368 p->ownermodel = cl_entities[hitent].render.model;
1369 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1370 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1382 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
1383 if (trace.fraction < 1)
1385 VectorCopy(trace.endpos, p->org);
1393 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1394 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1395 if (DotProduct(p->vel, p->vel) < 0.03)
1396 VectorClear(p->vel);
1401 p->vel[2] -= p->gravity * gravity;
1405 f = p->friction * frametime;
1406 #ifdef WORKINGLQUAKE
1407 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1409 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1413 VectorScale(p->vel, f, p->vel);
1417 if (p->type != particletype + pt_static)
1419 switch (p->type - particletype)
1421 case pt_entityparticle:
1422 // particle that removes itself after one rendered frame
1429 #ifdef WORKINGLQUAKE
1430 a = CL_PointQ1Contents(p->org);
1431 if (a <= CONTENTS_WATER)
1433 a = CL_PointSuperContents(p->org);
1434 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1437 p->size += frametime * 8;
1438 //p->alpha -= bloodwaterfade;
1441 p->vel[2] -= gravity;
1442 #ifdef WORKINGLQUAKE
1443 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1445 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1450 #ifdef WORKINGLQUAKE
1451 a = CL_PointQ1Contents(p->org);
1452 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1454 a = CL_PointSuperContents(p->org);
1455 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1463 #ifdef WORKINGLQUAKE
1464 a = CL_PointQ1Contents(p->org);
1465 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1467 a = CL_PointSuperContents(p->org);
1468 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1473 if (cl.time > p->time2)
1476 p->time2 = cl.time + (rand() & 3) * 0.1;
1477 p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
1478 p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1479 //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
1481 #ifdef WORKINGLQUAKE
1482 a = CL_PointQ1Contents(p->org);
1483 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1485 a = CL_PointSuperContents(p->org);
1486 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1491 //p->size += frametime * 15;
1494 // FIXME: this has fairly wacky handling of alpha
1495 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1496 #ifndef WORKINGLQUAKE
1497 if (cl_entities[p->owner].render.model == p->ownermodel)
1499 Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1500 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1507 a = max(0, (cl.time - p->time2) * 40);
1509 p->texnum = tex_rainsplash[a];
1518 cl_numparticles = maxparticle + 1;
1519 cl_freeparticle = 0;
1522 #define MAX_PARTICLETEXTURES 64
1523 // particletexture_t is a rectangle in the particlefonttexture
1526 rtexture_t *texture;
1527 float s1, t1, s2, t2;
1532 static int particlefonttexture;
1534 static rtexturepool_t *particletexturepool;
1535 static rtexture_t *particlefonttexture;
1537 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1539 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1541 #define PARTICLETEXTURESIZE 64
1542 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1544 static qbyte shadebubble(float dx, float dy, vec3_t light)
1548 dz = 1 - (dx*dx+dy*dy);
1549 if (dz > 0) // it does hit the sphere
1553 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1554 VectorNormalize(normal);
1555 dot = DotProduct(normal, light);
1556 if (dot > 0.5) // interior reflection
1557 f += ((dot * 2) - 1);
1558 else if (dot < -0.5) // exterior reflection
1559 f += ((dot * -2) - 1);
1561 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1562 VectorNormalize(normal);
1563 dot = DotProduct(normal, light);
1564 if (dot > 0.5) // interior reflection
1565 f += ((dot * 2) - 1);
1566 else if (dot < -0.5) // exterior reflection
1567 f += ((dot * -2) - 1);
1569 f += 16; // just to give it a haze so you can see the outline
1570 f = bound(0, f, 255);
1577 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1579 int basex, basey, y;
1580 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1581 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1582 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1583 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1584 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1585 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1586 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1587 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1590 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1593 float cx, cy, dx, dy, f, iradius;
1595 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1596 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1597 iradius = 1.0f / radius;
1598 alpha *= (1.0f / 255.0f);
1599 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1601 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1605 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1608 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1609 d[0] += f * (red - d[0]);
1610 d[1] += f * (green - d[1]);
1611 d[2] += f * (blue - d[2]);
1617 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1620 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1622 data[0] = bound(minr, data[0], maxr);
1623 data[1] = bound(ming, data[1], maxg);
1624 data[2] = bound(minb, data[2], maxb);
1628 void particletextureinvert(qbyte *data)
1631 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1633 data[0] = 255 - data[0];
1634 data[1] = 255 - data[1];
1635 data[2] = 255 - data[2];
1639 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1640 static void R_InitBloodTextures (qbyte *particletexturedata)
1643 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1646 for (i = 0;i < 8;i++)
1648 memset(&data[0][0][0], 255, sizeof(data));
1649 for (k = 0;k < 24;k++)
1650 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1651 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1652 particletextureinvert(&data[0][0][0]);
1653 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1657 for (i = 0;i < 8;i++)
1659 memset(&data[0][0][0], 255, sizeof(data));
1661 for (j = 1;j < 10;j++)
1662 for (k = min(j, m - 1);k < m;k++)
1663 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1664 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1665 particletextureinvert(&data[0][0][0]);
1666 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1671 static void R_InitParticleTexture (void)
1673 int x, y, d, i, k, m;
1674 float dx, dy, radius, f, f2;
1675 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1677 qbyte *particletexturedata;
1679 // a note: decals need to modulate (multiply) the background color to
1680 // properly darken it (stain), and they need to be able to alpha fade,
1681 // this is a very difficult challenge because it means fading to white
1682 // (no change to background) rather than black (darkening everything
1683 // behind the whole decal polygon), and to accomplish this the texture is
1684 // inverted (dark red blood on white background becomes brilliant cyan
1685 // and white on black background) so we can alpha fade it to black, then
1686 // we invert it again during the blendfunc to make it work...
1688 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1689 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1692 for (i = 0;i < 8;i++)
1694 memset(&data[0][0][0], 255, sizeof(data));
1697 qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1699 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1700 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1702 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1704 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1705 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1707 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1708 d = (noise2[y][x] - 128) * 3 + 192;
1710 d = d * (1-(dx*dx+dy*dy));
1711 d = (d * noise1[y][x]) >> 7;
1712 d = bound(0, d, 255);
1713 data[y][x][3] = (qbyte) d;
1720 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1724 for (i = 0;i < 16;i++)
1726 memset(&data[0][0][0], 255, sizeof(data));
1727 radius = i * 3.0f / 4.0f / 16.0f;
1728 f2 = 255.0f * ((15.0f - i) / 15.0f);
1729 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1731 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1732 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1734 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1735 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1736 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1739 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1743 memset(&data[0][0][0], 255, sizeof(data));
1744 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1746 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1747 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1749 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1750 d = 256 * (1 - (dx*dx+dy*dy));
1751 d = bound(0, d, 255);
1752 data[y][x][3] = (qbyte) d;
1755 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1758 memset(&data[0][0][0], 255, sizeof(data));
1759 light[0] = 1;light[1] = 1;light[2] = 1;
1760 VectorNormalize(light);
1761 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1763 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1764 // stretch upper half of bubble by +50% and shrink lower half by -50%
1765 // (this gives an elongated teardrop shape)
1767 dy = (dy - 0.5f) * 2.0f;
1769 dy = (dy - 0.5f) / 1.5f;
1770 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1772 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1773 // shrink bubble width to half
1775 data[y][x][3] = shadebubble(dx, dy, light);
1778 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1781 memset(&data[0][0][0], 255, sizeof(data));
1782 light[0] = 1;light[1] = 1;light[2] = 1;
1783 VectorNormalize(light);
1784 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1786 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1787 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1789 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1790 data[y][x][3] = shadebubble(dx, dy, light);
1793 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1795 // Blood particles and blood decals
1796 R_InitBloodTextures (particletexturedata);
1799 for (i = 0;i < 8;i++)
1801 memset(&data[0][0][0], 255, sizeof(data));
1802 for (k = 0;k < 12;k++)
1803 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1804 for (k = 0;k < 3;k++)
1805 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1806 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1807 particletextureinvert(&data[0][0][0]);
1808 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1812 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1813 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1814 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1818 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1821 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1822 if (!particlefonttexture)
1823 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1824 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1825 particletexture[i].texture = particlefonttexture;
1828 fractalnoise(&noise3[0][0], 64, 4);
1830 for (y = 0;y < 64;y++)
1832 dy = (y - 0.5f*64) / (64*0.5f-1);
1833 for (x = 0;x < 16;x++)
1835 dx = (x - 0.5f*16) / (16*0.5f-2);
1836 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1837 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1838 data2[y][x][3] = 255;
1843 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1846 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1847 if (!particletexture[tex_beam].texture)
1848 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1849 particletexture[tex_beam].s1 = 0;
1850 particletexture[tex_beam].t1 = 0;
1851 particletexture[tex_beam].s2 = 1;
1852 particletexture[tex_beam].t2 = 1;
1854 Mem_Free(particletexturedata);
1857 static void r_part_start(void)
1859 particletexturepool = R_AllocTexturePool();
1860 R_InitParticleTexture ();
1863 static void r_part_shutdown(void)
1865 R_FreeTexturePool(&particletexturepool);
1868 static void r_part_newmap(void)
1870 cl_numparticles = 0;
1871 cl_freeparticle = 0;
1874 void R_Particles_Init (void)
1876 Cvar_RegisterVariable(&r_drawparticles);
1877 #ifdef WORKINGLQUAKE
1880 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1884 #ifdef WORKINGLQUAKE
1885 void R_InitParticles(void)
1887 CL_Particles_Init();
1892 float particle_vertex3f[12], particle_texcoord2f[8];
1894 #ifdef WORKINGLQUAKE
1895 void R_DrawParticle(particle_t *p)
1898 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1900 const particle_t *p = calldata1;
1904 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, size;
1905 particletexture_t *tex;
1907 VectorCopy(p->org, org);
1909 blendmode = p->type->blendmode;
1910 tex = &particletexture[p->texnum];
1911 cr = p->color[0] * (1.0f / 255.0f);
1912 cg = p->color[1] * (1.0f / 255.0f);
1913 cb = p->color[2] * (1.0f / 255.0f);
1914 ca = p->alpha * (1.0f / 255.0f);
1915 if (blendmode == PBLEND_MOD)
1925 #ifndef WORKINGLQUAKE
1926 if (p->type->lighting)
1928 float ambient[3], diffuse[3], diffusenormal[3];
1929 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
1930 cr *= (ambient[0] + 0.5 * diffuse[0]);
1931 cg *= (ambient[1] + 0.5 * diffuse[1]);
1932 cb *= (ambient[2] + 0.5 * diffuse[2]);
1936 VectorSubtract(org, r_vieworigin, fogvec);
1937 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1942 if (blendmode == PBLEND_ALPHA)
1944 cr += fogcolor[0] * fog;
1945 cg += fogcolor[1] * fog;
1946 cb += fogcolor[2] * fog;
1950 R_Mesh_Matrix(&r_identitymatrix);
1952 memset(&m, 0, sizeof(m));
1953 m.tex[0] = R_GetTexture(tex->texture);
1954 m.pointer_texcoord[0] = particle_texcoord2f;
1955 m.pointer_vertex = particle_vertex3f;
1958 GL_Color(cr, cg, cb, ca);
1960 if (blendmode == PBLEND_ALPHA)
1961 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1962 else if (blendmode == PBLEND_ADD)
1963 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1964 else //if (blendmode == PBLEND_MOD)
1965 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1966 GL_DepthMask(false);
1969 size = p->size * cl_particles_size.value;
1970 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1972 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1975 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1977 VectorNegate(p->vel, v);
1978 VectorVectors(v, right, up);
1981 VectorVectors(p->vel, right, up);
1982 VectorScale(right, size, right);
1983 VectorScale(up, size, up);
1987 VectorScale(r_viewleft, -size, right);
1988 VectorScale(r_viewup, size, up);
1990 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1991 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1992 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1993 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1994 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1995 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1996 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1997 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1998 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1999 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
2000 particle_vertex3f[10] = org[1] + right[1] - up[1];
2001 particle_vertex3f[11] = org[2] + right[2] - up[2];
2002 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2003 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2004 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2005 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2007 else if (p->type->orientation == PARTICLE_SPARK)
2009 VectorMA(p->org, -0.02, p->vel, v);
2010 VectorMA(p->org, 0.02, p->vel, up2);
2011 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2012 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2013 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2014 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2015 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2017 else if (p->type->orientation == PARTICLE_BEAM)
2019 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2020 VectorSubtract(p->vel, p->org, up);
2021 VectorNormalize(up);
2022 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2023 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2024 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2025 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2026 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2027 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2031 Con_Printf("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2036 if (blendmode == PBLEND_ALPHA)
2037 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2038 else if (blendmode == PBLEND_ADD)
2039 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2040 else //if (blendmode == PBLEND_MOD)
2041 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2042 glColor4f(cr, cg, cb, ca);
2044 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2045 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2046 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2047 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2050 R_Mesh_Draw(0, 4, 2, polygonelements);
2054 void R_DrawParticles (void)
2057 float minparticledist;
2060 #ifdef WORKINGLQUAKE
2064 // LordHavoc: early out conditions
2065 if ((!cl_numparticles) || (!r_drawparticles.integer))
2068 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2070 #ifdef WORKINGLQUAKE
2071 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2073 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2075 // LordHavoc: only render if not too close
2076 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2077 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2080 glDisable(GL_BLEND);
2081 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2083 // LordHavoc: only render if not too close
2084 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2089 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2091 if (p->type == particletype + pt_decal)
2092 R_DrawParticleCallback(p, 0);
2094 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);