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 Host_Error("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);
1219 // advance to next time and position
1221 VectorMA (pos, dec, vec, pos);
1223 #ifndef WORKINGLQUAKE
1224 ent->persistent.trail_time = len;
1228 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1230 int tempcolor2, cr, cg, cb;
1234 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1235 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);
1238 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1241 if (!cl_particles.integer) return;
1244 if (cl_particles_smoke.integer)
1245 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1246 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);
1249 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1252 if (!cl_particles.integer) return;
1254 if (cl_stainmaps.integer)
1255 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1256 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1259 if (cl_particles_smoke.integer)
1260 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1261 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);
1264 if (cl_particles_sparks.integer)
1265 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1266 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);
1274 void CL_MoveParticles (void)
1277 int i, maxparticle, j, a, content;
1278 float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
1282 // LordHavoc: early out condition
1283 if (!cl_numparticles)
1285 cl_freeparticle = 0;
1289 #ifdef WORKINGLQUAKE
1290 frametime = cl.frametime;
1292 frametime = cl.time - cl.oldtime;
1294 gravity = frametime * sv_gravity.value;
1295 dvel = 1+4*frametime;
1296 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1300 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1307 p->alpha -= p->alphafade * frametime;
1315 if (p->type->orientation != PARTICLE_BEAM)
1317 VectorCopy(p->org, oldorg);
1318 VectorMA(p->org, frametime, p->vel, p->org);
1319 VectorCopy(p->org, org);
1322 if (p->type == particletype + pt_rain)
1324 // raindrop - splash on solid/water/slime/lava
1325 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
1326 if (trace.fraction < 1)
1328 // convert from a raindrop particle to a rainsplash decal
1329 VectorCopy(trace.endpos, p->org);
1330 VectorCopy(trace.plane.normal, p->vel);
1331 VectorAdd(p->org, p->vel, p->org);
1332 p->type = particletype + pt_raindecal;
1333 p->texnum = tex_rainsplash[0];
1335 p->alphafade = p->alpha / 0.4;
1342 else if (p->type == particletype + pt_blood)
1344 // blood - splash on solid
1345 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
1346 if (trace.fraction < 1)
1348 // convert from a blood particle to a blood decal
1349 VectorCopy(trace.endpos, p->org);
1350 VectorCopy(trace.plane.normal, p->vel);
1351 VectorAdd(p->org, p->vel, p->org);
1352 #ifndef WORKINGLQUAKE
1353 if (cl_stainmaps.integer)
1354 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));
1356 if (!cl_decals.integer)
1362 p->type = particletype + pt_decal;
1363 p->texnum = tex_blooddecal[rand()&7];
1364 #ifndef WORKINGLQUAKE
1366 p->ownermodel = cl_entities[hitent].render.model;
1367 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1368 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1380 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
1381 if (trace.fraction < 1)
1383 VectorCopy(trace.endpos, p->org);
1391 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1392 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1393 if (DotProduct(p->vel, p->vel) < 0.03)
1394 VectorClear(p->vel);
1399 p->vel[2] -= p->gravity * gravity;
1403 f = p->friction * frametime;
1404 #ifdef WORKINGLQUAKE
1405 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1407 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1411 VectorScale(p->vel, f, p->vel);
1415 if (p->type != particletype + pt_static)
1417 switch (p->type - particletype)
1419 case pt_entityparticle:
1420 // particle that removes itself after one rendered frame
1427 #ifdef WORKINGLQUAKE
1428 a = CL_PointQ1Contents(p->org);
1429 if (a <= CONTENTS_WATER)
1431 a = CL_PointSuperContents(p->org);
1432 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1435 p->size += frametime * 8;
1436 //p->alpha -= bloodwaterfade;
1439 p->vel[2] -= gravity;
1440 #ifdef WORKINGLQUAKE
1441 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1443 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1448 #ifdef WORKINGLQUAKE
1449 a = CL_PointQ1Contents(p->org);
1450 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1452 a = CL_PointSuperContents(p->org);
1453 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1461 #ifdef WORKINGLQUAKE
1462 a = CL_PointQ1Contents(p->org);
1463 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1465 a = CL_PointSuperContents(p->org);
1466 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1471 if (cl.time > p->time2)
1474 p->time2 = cl.time + (rand() & 3) * 0.1;
1475 p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
1476 p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1477 //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
1479 #ifdef WORKINGLQUAKE
1480 a = CL_PointQ1Contents(p->org);
1481 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1483 a = CL_PointSuperContents(p->org);
1484 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1489 //p->size += frametime * 15;
1492 // FIXME: this has fairly wacky handling of alpha
1493 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1494 #ifndef WORKINGLQUAKE
1495 if (cl_entities[p->owner].render.model == p->ownermodel)
1497 Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1498 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1505 a = max(0, (cl.time - p->time2) * 40);
1507 p->texnum = tex_rainsplash[a];
1516 cl_numparticles = maxparticle + 1;
1517 cl_freeparticle = 0;
1520 #define MAX_PARTICLETEXTURES 64
1521 // particletexture_t is a rectangle in the particlefonttexture
1524 rtexture_t *texture;
1525 float s1, t1, s2, t2;
1530 static int particlefonttexture;
1532 static rtexturepool_t *particletexturepool;
1533 static rtexture_t *particlefonttexture;
1535 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1537 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1539 #define PARTICLETEXTURESIZE 64
1540 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1542 static qbyte shadebubble(float dx, float dy, vec3_t light)
1546 dz = 1 - (dx*dx+dy*dy);
1547 if (dz > 0) // it does hit the sphere
1551 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1552 VectorNormalize(normal);
1553 dot = DotProduct(normal, light);
1554 if (dot > 0.5) // interior reflection
1555 f += ((dot * 2) - 1);
1556 else if (dot < -0.5) // exterior reflection
1557 f += ((dot * -2) - 1);
1559 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1560 VectorNormalize(normal);
1561 dot = DotProduct(normal, light);
1562 if (dot > 0.5) // interior reflection
1563 f += ((dot * 2) - 1);
1564 else if (dot < -0.5) // exterior reflection
1565 f += ((dot * -2) - 1);
1567 f += 16; // just to give it a haze so you can see the outline
1568 f = bound(0, f, 255);
1575 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1577 int basex, basey, y;
1578 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1579 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1580 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1581 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1582 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1583 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1584 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1585 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1588 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1591 float cx, cy, dx, dy, f, iradius;
1593 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1594 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1595 iradius = 1.0f / radius;
1596 alpha *= (1.0f / 255.0f);
1597 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1599 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1603 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1606 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1607 d[0] += f * (red - d[0]);
1608 d[1] += f * (green - d[1]);
1609 d[2] += f * (blue - d[2]);
1615 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1618 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1620 data[0] = bound(minr, data[0], maxr);
1621 data[1] = bound(ming, data[1], maxg);
1622 data[2] = bound(minb, data[2], maxb);
1626 void particletextureinvert(qbyte *data)
1629 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1631 data[0] = 255 - data[0];
1632 data[1] = 255 - data[1];
1633 data[2] = 255 - data[2];
1637 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1638 static void R_InitBloodTextures (qbyte *particletexturedata)
1641 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1644 for (i = 0;i < 8;i++)
1646 memset(&data[0][0][0], 255, sizeof(data));
1647 for (k = 0;k < 24;k++)
1648 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1649 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1650 particletextureinvert(&data[0][0][0]);
1651 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1655 for (i = 0;i < 8;i++)
1657 memset(&data[0][0][0], 255, sizeof(data));
1659 for (j = 1;j < 10;j++)
1660 for (k = min(j, m - 1);k < m;k++)
1661 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1662 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1663 particletextureinvert(&data[0][0][0]);
1664 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1669 static void R_InitParticleTexture (void)
1671 int x, y, d, i, k, m;
1672 float dx, dy, radius, f, f2;
1673 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1675 qbyte *particletexturedata;
1677 // a note: decals need to modulate (multiply) the background color to
1678 // properly darken it (stain), and they need to be able to alpha fade,
1679 // this is a very difficult challenge because it means fading to white
1680 // (no change to background) rather than black (darkening everything
1681 // behind the whole decal polygon), and to accomplish this the texture is
1682 // inverted (dark red blood on white background becomes brilliant cyan
1683 // and white on black background) so we can alpha fade it to black, then
1684 // we invert it again during the blendfunc to make it work...
1686 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1687 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1690 for (i = 0;i < 8;i++)
1692 memset(&data[0][0][0], 255, sizeof(data));
1695 qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1697 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1698 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1700 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1702 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1703 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1705 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1706 d = (noise2[y][x] - 128) * 3 + 192;
1708 d = d * (1-(dx*dx+dy*dy));
1709 d = (d * noise1[y][x]) >> 7;
1710 d = bound(0, d, 255);
1711 data[y][x][3] = (qbyte) d;
1718 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1722 for (i = 0;i < 16;i++)
1724 memset(&data[0][0][0], 255, sizeof(data));
1725 radius = i * 3.0f / 4.0f / 16.0f;
1726 f2 = 255.0f * ((15.0f - i) / 15.0f);
1727 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1729 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1730 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1732 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1733 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1734 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1737 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1741 memset(&data[0][0][0], 255, sizeof(data));
1742 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1744 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1745 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1747 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1748 d = 256 * (1 - (dx*dx+dy*dy));
1749 d = bound(0, d, 255);
1750 data[y][x][3] = (qbyte) d;
1753 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1756 memset(&data[0][0][0], 255, sizeof(data));
1757 light[0] = 1;light[1] = 1;light[2] = 1;
1758 VectorNormalize(light);
1759 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1761 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1762 // stretch upper half of bubble by +50% and shrink lower half by -50%
1763 // (this gives an elongated teardrop shape)
1765 dy = (dy - 0.5f) * 2.0f;
1767 dy = (dy - 0.5f) / 1.5f;
1768 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1770 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1771 // shrink bubble width to half
1773 data[y][x][3] = shadebubble(dx, dy, light);
1776 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1779 memset(&data[0][0][0], 255, sizeof(data));
1780 light[0] = 1;light[1] = 1;light[2] = 1;
1781 VectorNormalize(light);
1782 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1784 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1785 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1787 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1788 data[y][x][3] = shadebubble(dx, dy, light);
1791 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1793 // Blood particles and blood decals
1794 R_InitBloodTextures (particletexturedata);
1797 for (i = 0;i < 8;i++)
1799 memset(&data[0][0][0], 255, sizeof(data));
1800 for (k = 0;k < 12;k++)
1801 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1802 for (k = 0;k < 3;k++)
1803 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1804 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1805 particletextureinvert(&data[0][0][0]);
1806 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1810 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1811 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1812 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1816 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1819 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1820 if (!particlefonttexture)
1821 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1822 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1823 particletexture[i].texture = particlefonttexture;
1826 fractalnoise(&noise3[0][0], 64, 4);
1828 for (y = 0;y < 64;y++)
1830 dy = (y - 0.5f*64) / (64*0.5f-1);
1831 for (x = 0;x < 16;x++)
1833 dx = (x - 0.5f*16) / (16*0.5f-2);
1834 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1835 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1836 data2[y][x][3] = 255;
1841 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1844 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1845 if (!particletexture[tex_beam].texture)
1846 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1847 particletexture[tex_beam].s1 = 0;
1848 particletexture[tex_beam].t1 = 0;
1849 particletexture[tex_beam].s2 = 1;
1850 particletexture[tex_beam].t2 = 1;
1852 Mem_Free(particletexturedata);
1855 static void r_part_start(void)
1857 particletexturepool = R_AllocTexturePool();
1858 R_InitParticleTexture ();
1861 static void r_part_shutdown(void)
1863 R_FreeTexturePool(&particletexturepool);
1866 static void r_part_newmap(void)
1868 cl_numparticles = 0;
1869 cl_freeparticle = 0;
1872 void R_Particles_Init (void)
1874 Cvar_RegisterVariable(&r_drawparticles);
1875 #ifdef WORKINGLQUAKE
1878 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1882 #ifdef WORKINGLQUAKE
1883 void R_InitParticles(void)
1885 CL_Particles_Init();
1890 float particle_vertex3f[12], particle_texcoord2f[8];
1892 #ifdef WORKINGLQUAKE
1893 void R_DrawParticle(particle_t *p)
1896 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1898 const particle_t *p = calldata1;
1902 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, size;
1903 particletexture_t *tex;
1905 VectorCopy(p->org, org);
1907 blendmode = p->type->blendmode;
1908 tex = &particletexture[p->texnum];
1909 cr = p->color[0] * (1.0f / 255.0f);
1910 cg = p->color[1] * (1.0f / 255.0f);
1911 cb = p->color[2] * (1.0f / 255.0f);
1912 ca = p->alpha * (1.0f / 255.0f);
1913 if (blendmode == PBLEND_MOD)
1923 #ifndef WORKINGLQUAKE
1924 if (p->type->lighting)
1926 float ambient[3], diffuse[3], diffusenormal[3];
1927 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
1928 cr *= (ambient[0] + 0.5 * diffuse[0]);
1929 cg *= (ambient[1] + 0.5 * diffuse[1]);
1930 cb *= (ambient[2] + 0.5 * diffuse[2]);
1934 VectorSubtract(org, r_vieworigin, fogvec);
1935 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1940 if (blendmode == PBLEND_ALPHA)
1942 cr += fogcolor[0] * fog;
1943 cg += fogcolor[1] * fog;
1944 cb += fogcolor[2] * fog;
1948 R_Mesh_Matrix(&r_identitymatrix);
1950 memset(&m, 0, sizeof(m));
1951 m.tex[0] = R_GetTexture(tex->texture);
1952 m.pointer_texcoord[0] = particle_texcoord2f;
1953 m.pointer_vertex = particle_vertex3f;
1956 GL_Color(cr, cg, cb, ca);
1958 if (blendmode == PBLEND_ALPHA)
1959 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1960 else if (blendmode == PBLEND_ADD)
1961 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1962 else //if (blendmode == PBLEND_MOD)
1963 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1964 GL_DepthMask(false);
1967 size = p->size * cl_particles_size.value;
1968 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1970 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1973 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1975 VectorNegate(p->vel, v);
1976 VectorVectors(v, right, up);
1979 VectorVectors(p->vel, right, up);
1980 VectorScale(right, size, right);
1981 VectorScale(up, size, up);
1985 VectorScale(r_viewleft, -size, right);
1986 VectorScale(r_viewup, size, up);
1988 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1989 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1990 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1991 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1992 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1993 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1994 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1995 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1996 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1997 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1998 particle_vertex3f[10] = org[1] + right[1] - up[1];
1999 particle_vertex3f[11] = org[2] + right[2] - up[2];
2000 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2001 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2002 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2003 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2005 else if (p->type->orientation == PARTICLE_SPARK)
2007 VectorMA(p->org, -0.02, p->vel, v);
2008 VectorMA(p->org, 0.02, p->vel, up2);
2009 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2010 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2011 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2012 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2013 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2015 else if (p->type->orientation == PARTICLE_BEAM)
2017 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2018 VectorSubtract(p->vel, p->org, up);
2019 VectorNormalize(up);
2020 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2021 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2022 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2023 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2024 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2025 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2028 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2031 if (blendmode == PBLEND_ALPHA)
2032 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2033 else if (blendmode == PBLEND_ADD)
2034 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2035 else //if (blendmode == PBLEND_MOD)
2036 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2037 glColor4f(cr, cg, cb, ca);
2039 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2040 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2041 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2042 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2045 R_Mesh_Draw(0, 4, 2, polygonelements);
2049 void R_DrawParticles (void)
2052 float minparticledist;
2055 #ifdef WORKINGLQUAKE
2059 // LordHavoc: early out conditions
2060 if ((!cl_numparticles) || (!r_drawparticles.integer))
2063 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2065 #ifdef WORKINGLQUAKE
2066 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2068 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2070 // LordHavoc: only render if not too close
2071 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2072 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2075 glDisable(GL_BLEND);
2076 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2078 // LordHavoc: only render if not too close
2079 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2084 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2086 if (p->type == particletype + pt_decal)
2087 R_DrawParticleCallback(p, 0);
2089 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);