2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
25 #define NUMVERTEXNORMALS 162
26 siextern float r_avertexnormals[NUMVERTEXNORMALS][3];
27 #define m_bytenormals r_avertexnormals
28 #define VectorNormalizeFast VectorNormalize
29 #define CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
30 typedef unsigned char qbyte;
31 #define cl_stainmaps.integer 0
32 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
35 #define CL_EntityParticles R_EntityParticles
36 #define CL_ReadPointFile_f R_ReadPointFile_f
37 #define CL_ParseParticleEffect R_ParseParticleEffect
38 #define CL_ParticleExplosion R_ParticleExplosion
39 #define CL_ParticleExplosion2 R_ParticleExplosion2
40 #define CL_TeleportSplash R_TeleportSplash
41 #define CL_BlobExplosion R_BlobExplosion
42 #define CL_RunParticleEffect R_RunParticleEffect
43 #define CL_LavaSplash R_LavaSplash
44 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
46 vec3_t right1, right2, diff, normal;
48 VectorSubtract (org2, org1, normal);
49 VectorNormalizeFast (normal);
51 // calculate 'right' vector for start
52 VectorSubtract (r_vieworigin, org1, diff);
53 VectorNormalizeFast (diff);
54 CrossProduct (normal, diff, right1);
56 // calculate 'right' vector for end
57 VectorSubtract (r_vieworigin, org2, diff);
58 VectorNormalizeFast (diff);
59 CrossProduct (normal, diff, right2);
61 vert[ 0] = org1[0] + width * right1[0];
62 vert[ 1] = org1[1] + width * right1[1];
63 vert[ 2] = org1[2] + width * right1[2];
64 vert[ 3] = org1[0] - width * right1[0];
65 vert[ 4] = org1[1] - width * right1[1];
66 vert[ 5] = org1[2] - width * right1[2];
67 vert[ 6] = org2[0] - width * right2[0];
68 vert[ 7] = org2[1] - width * right2[1];
69 vert[ 8] = org2[2] - width * right2[2];
70 vert[ 9] = org2[0] + width * right2[0];
71 vert[10] = org2[1] + width * right2[1];
72 vert[11] = org2[2] + width * right2[2];
74 void fractalnoise(qbyte *noise, int size, int startgrid)
76 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
78 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
80 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
81 if (size != (1 << sizepower))
82 Sys_Error("fractalnoise: size must be power of 2\n");
84 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
85 if (startgrid != (1 << gridpower))
86 Sys_Error("fractalnoise: grid must be power of 2\n");
88 startgrid = bound(0, startgrid, size);
90 amplitude = 0xFFFF; // this gets halved before use
91 noisebuf = malloc(size*size*sizeof(int));
92 memset(noisebuf, 0, size*size*sizeof(int));
94 for (g2 = startgrid;g2;g2 >>= 1)
96 // brownian motion (at every smaller level there is random behavior)
98 for (y = 0;y < size;y += g2)
99 for (x = 0;x < size;x += g2)
100 n(x,y) += (rand()&litude);
105 // subdivide, diamond-square algorithm (really this has little to do with squares)
107 for (y = 0;y < size;y += g2)
108 for (x = 0;x < size;x += g2)
109 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
111 for (y = 0;y < size;y += g2)
112 for (x = 0;x < size;x += g2)
114 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
115 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
119 // find range of noise values
121 for (y = 0;y < size;y++)
122 for (x = 0;x < size;x++)
124 if (n(x,y) < min) min = n(x,y);
125 if (n(x,y) > max) max = n(x,y);
129 // normalize noise and copy to output
130 for (y = 0;y < size;y++)
131 for (x = 0;x < size;x++)
132 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
136 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
140 right[0] = forward[2];
141 right[1] = -forward[0];
142 right[2] = forward[1];
144 d = DotProduct(forward, right);
145 right[0] -= d * forward[0];
146 right[1] -= d * forward[1];
147 right[2] -= d * forward[2];
148 VectorNormalizeFast(right);
149 CrossProduct(right, forward, up);
153 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
155 trace_t CL_TraceBox (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hitbmodels, int *hitent, int hitsupercontentsmask, qboolean hitplayers)
162 memset (&trace, 0, sizeof(trace));
164 VectorCopy (end, trace.endpos);
166 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
168 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
173 #include "cl_collision.h"
177 #define MAX_PARTICLES 32768 // default max # of particles at one time
178 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
182 PARTICLE_BILLBOARD = 0,
184 PARTICLE_ORIENTED_DOUBLESIDED = 2,
197 typedef struct particletype_s
200 porientation_t orientation;
207 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
211 // must match ptype_t values
212 particletype_t particletype[pt_total] =
214 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
215 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
216 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
217 {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
218 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
219 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
220 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
221 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
222 {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
223 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
224 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
225 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
228 typedef struct particle_s
230 particletype_t *type;
233 vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam
235 float alpha; // 0-255
236 float alphafade; // how much alpha reduces per second
237 float time2; // used for snow fluttering and decal fade
238 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)
239 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
240 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
242 #ifndef WORKINGLQUAKE
243 unsigned short owner; // decal stuck to this entity
244 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
245 vec3_t relativeorigin; // decal at this location in entity's coordinate space
246 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
251 static int particlepalette[256] =
253 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
254 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
255 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
256 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
257 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
258 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
259 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
260 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
261 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
262 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
263 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
264 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
265 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
266 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
267 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
268 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
269 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
270 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
271 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
272 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
273 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
274 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
275 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
276 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
277 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
278 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
279 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
280 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
281 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
282 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
283 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
284 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
287 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
289 // texture numbers in particle font
290 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
291 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
292 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
293 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
294 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
295 static const int tex_particle = 63;
296 static const int tex_bubble = 62;
297 static const int tex_raindrop = 61;
298 static const int tex_beam = 60;
300 static int cl_maxparticles;
301 static int cl_numparticles;
302 static int cl_freeparticle;
303 static particle_t *particles;
305 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
306 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
307 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
308 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
309 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
310 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
311 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
312 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
313 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
314 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
315 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
316 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
317 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
318 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
319 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
320 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
321 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
322 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
323 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
324 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
326 void CL_Particles_Clear(void)
330 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
338 void CL_ReadPointFile_f (void);
339 void CL_Particles_Init (void)
343 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
344 i = COM_CheckParm ("-particles");
346 if (i && i < com_argc - 1)
348 cl_maxparticles = (int)(atoi(com_argv[i+1]));
349 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
350 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
353 cl_maxparticles = MAX_PARTICLES;
355 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
357 Cvar_RegisterVariable (&cl_particles);
358 Cvar_RegisterVariable (&cl_particles_quality);
359 Cvar_RegisterVariable (&cl_particles_size);
360 Cvar_RegisterVariable (&cl_particles_bloodshowers);
361 Cvar_RegisterVariable (&cl_particles_blood);
362 Cvar_RegisterVariable (&cl_particles_blood_alpha);
363 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
364 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
365 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
366 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
367 Cvar_RegisterVariable (&cl_particles_explosions_shell);
368 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
369 Cvar_RegisterVariable (&cl_particles_smoke);
370 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
371 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
372 Cvar_RegisterVariable (&cl_particles_sparks);
373 Cvar_RegisterVariable (&cl_particles_bubbles);
374 Cvar_RegisterVariable (&cl_decals);
375 Cvar_RegisterVariable (&cl_decals_time);
376 Cvar_RegisterVariable (&cl_decals_fadetime);
379 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
381 particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
383 CL_Particles_Clear();
386 void CL_Particles_Shutdown (void)
389 // No clue what to do here...
393 // list of all 26 parameters:
394 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
395 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
396 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
397 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
398 // palpha - opacity of particle as 0-255 (can be more than 255)
399 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
400 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
401 // pgravity - how much effect gravity has on the particle (0-1)
402 // 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
403 // px,py,pz - starting origin of particle
404 // pvx,pvy,pvz - starting velocity of particle
405 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
406 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)
409 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
410 ptempcolor = (pcolor1);
411 ptempcolor2 = (pcolor2);
412 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
413 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
414 pcb2 = (ptempcolor2) & 0xFF;
415 if (ptempcolor != ptempcolor2)
417 pcr1 = ((ptempcolor) >> 16) & 0xFF;
418 pcg1 = ((ptempcolor) >> 8) & 0xFF;
419 pcb1 = (ptempcolor) & 0xFF;
420 ptempcolor = rand() & 0xFF;
421 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
422 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
423 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
425 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
426 if (cl_freeparticle >= cl_maxparticles)
428 part = &particles[cl_freeparticle++];
429 if (cl_numparticles < cl_freeparticle)
430 cl_numparticles = cl_freeparticle;
431 memset(part, 0, sizeof(*part));
432 part->type = (ptype);
433 part->color[0] = pcr2;
434 part->color[1] = pcg2;
435 part->color[2] = pcb2;
436 part->color[3] = 0xFF;
438 part->size = (psize);
439 part->alpha = (palpha);
440 part->alphafade = (palphafade);
441 part->gravity = (pgravity);
442 part->bounce = (pbounce);
446 part->vel[0] = (pvx);
447 part->vel[1] = (pvy);
448 part->vel[2] = (pvz);
450 part->friction = (pfriction);
454 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
457 if (!cl_decals.integer)
459 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);
463 #ifndef WORKINGLQUAKE
465 p->ownermodel = cl_entities[p->owner].render.model;
466 Matrix4x4_Transform(&cl_entities[p->owner].render.inversematrix, org, p->relativeorigin);
467 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.inversematrix, normal, p->relativedirection);
468 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
473 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
476 float bestfrac, bestorg[3], bestnormal[3];
478 int besthitent = 0, hitent;
481 for (i = 0;i < 32;i++)
484 VectorMA(org, maxdist, org2, org2);
485 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, &hitent, SUPERCONTENTS_SOLID, false);
486 if (bestfrac > trace.fraction)
488 bestfrac = trace.fraction;
490 VectorCopy(trace.endpos, bestorg);
491 VectorCopy(trace.plane.normal, bestnormal);
495 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
503 void CL_EntityParticles (entity_t *ent)
507 float sp, sy, cp, cy;
511 static vec3_t avelocities[NUMVERTEXNORMALS];
512 if (!cl_particles.integer) return;
517 if (!avelocities[0][0])
518 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
519 avelocities[0][i] = (rand()&255) * 0.01;
521 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
523 angle = cl.time * avelocities[i][0];
526 angle = cl.time * avelocities[i][1];
535 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);
537 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);
543 void CL_ReadPointFile_f (void)
547 char *pointfile = NULL, *pointfilepos, *t, tchar;
548 char name[MAX_OSPATH];
553 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
554 strlcat (name, ".pts", sizeof (name));
556 pointfile = COM_LoadTempFile (name);
558 pointfile = FS_LoadFile(name, tempmempool, true);
562 Con_Printf("Could not open %s\n", name);
566 Con_Printf("Reading %s...\n", name);
569 pointfilepos = pointfile;
570 while (*pointfilepos)
572 while (*pointfilepos == '\n' || *pointfilepos == '\r')
577 while (*t && *t != '\n' && *t != '\r')
581 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
587 VectorCopy(org, leakorg);
590 if (cl_numparticles < cl_maxparticles - 3)
593 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);
596 #ifndef WORKINGLQUAKE
599 VectorCopy(leakorg, org);
600 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
602 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);
603 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);
604 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);
609 CL_ParseParticleEffect
611 Parse an effect out of the server message
614 void CL_ParseParticleEffect (void)
617 int i, count, msgcount, color;
619 MSG_ReadVector(org, cl.protocol);
620 for (i=0 ; i<3 ; i++)
621 dir[i] = MSG_ReadChar () * (1.0/16);
622 msgcount = MSG_ReadByte ();
623 color = MSG_ReadByte ();
630 if (cl_particles_blood_bloodhack.integer)
635 CL_BloodPuff(org, dir, count / 2);
641 CL_BloodPuff(org, dir, count / 2);
645 CL_RunParticleEffect (org, dir, color, count);
654 void CL_ParticleExplosion (vec3_t org)
660 if (cl_stainmaps.integer)
661 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
662 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
664 i = CL_PointSuperContents(org);
665 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
667 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
668 for (i = 0;i < 128 * cl_particles_quality.value;i++)
669 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));
673 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
675 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
677 for (i = 0;i < 32;i++)
682 v2[0] = lhrandom(-48, 48);
683 v2[1] = lhrandom(-48, 48);
684 v2[2] = lhrandom(-48, 48);
686 for (k = 0;k < 16;k++)
688 v[0] = org[0] + lhrandom(-48, 48);
689 v[1] = org[1] + lhrandom(-48, 48);
690 v[2] = org[2] + lhrandom(-48, 48);
691 trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
692 if (trace.fraction >= 0.1)
695 VectorSubtract(trace.endpos, org, v2);
697 VectorScale(v2, 2.0f, v2);
698 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);
702 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
703 for (i = 0;i < 128 * cl_particles_quality.value;i++)
704 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);
707 if (cl_particles_explosions_shell.integer)
713 CL_ParticleExplosion2
717 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
723 if (!cl_particles.integer) return;
725 for (i = 0;i < 512 * cl_particles_quality.value;i++)
727 VectorRandom (offset);
728 VectorScale (offset, 192, vel);
729 VectorScale (offset, 8, offset);
730 k = particlepalette[colorStart + (i % colorLength)];
731 pscale = lhrandom(0.5, 1.5);
732 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));
742 void CL_BlobExplosion (vec3_t org)
744 CL_ParticleExplosion(org);
753 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
759 CL_ParticleExplosion(org);
762 if (!cl_particles.integer) return;
763 count *= cl_particles_quality.value;
766 k = particlepalette[color + (rand()&7)];
767 if (gamemode == GAME_GOODVSBAD2)
768 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);
770 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);
774 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
780 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
784 if (!cl_particles.integer) return;
786 if (cl_particles_sparks.integer)
789 count *= cl_particles_quality.value;
792 k = particlepalette[0x68 + (rand() & 7)];
793 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);
798 void CL_Smoke (vec3_t org, vec3_t dir, int count)
804 if (!cl_particles.integer) return;
807 if (cl_particles_smoke.integer)
809 k = count * 0.25 * cl_particles_quality.value;
812 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
813 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
814 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
815 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
816 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);
821 void CL_BulletMark (vec3_t org)
823 if (cl_stainmaps.integer)
824 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
825 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
828 void CL_PlasmaBurn (vec3_t org)
830 if (cl_stainmaps.integer)
831 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
832 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
835 static float bloodcount = 0;
836 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
841 // bloodcount is used to accumulate counts too small to cause a blood particle
842 if (!cl_particles.integer) return;
843 if (!cl_particles_blood.integer) return;
850 while(bloodcount > 0)
852 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
853 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
854 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
855 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
856 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);
857 bloodcount -= 16 / cl_particles_quality.value;
861 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
863 vec3_t org, vel, diff, center, velscale;
864 if (!cl_particles.integer) return;
865 if (!cl_particles_bloodshowers.integer) return;
866 if (!cl_particles_blood.integer) return;
868 VectorSubtract(maxs, mins, diff);
869 center[0] = (mins[0] + maxs[0]) * 0.5;
870 center[1] = (mins[1] + maxs[1]) * 0.5;
871 center[2] = (mins[2] + maxs[2]) * 0.5;
872 velscale[0] = velspeed * 2.0 / diff[0];
873 velscale[1] = velspeed * 2.0 / diff[1];
874 velscale[2] = velspeed * 2.0 / diff[2];
876 bloodcount += count * 5.0f;
877 while (bloodcount > 0)
879 org[0] = lhrandom(mins[0], maxs[0]);
880 org[1] = lhrandom(mins[1], maxs[1]);
881 org[2] = lhrandom(mins[2], maxs[2]);
882 vel[0] = (org[0] - center[0]) * velscale[0];
883 vel[1] = (org[1] - center[1]) * velscale[1];
884 vel[2] = (org[2] - center[2]) * velscale[2];
885 bloodcount -= 16 / cl_particles_quality.value;
886 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);
890 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
894 if (!cl_particles.integer) return;
895 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
896 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
897 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
899 count *= cl_particles_quality.value;
902 k = particlepalette[colorbase + (rand()&3)];
903 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);
907 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
910 float t, z, minz, maxz;
911 if (!cl_particles.integer) return;
912 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
913 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
914 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
915 if (dir[2] < 0) // falling
920 minz = z - fabs(dir[2]) * 0.1;
921 maxz = z + fabs(dir[2]) * 0.1;
922 minz = bound(mins[2], minz, maxs[2]);
923 maxz = bound(mins[2], maxz, maxs[2]);
925 count *= cl_particles_quality.value;
930 count *= 4; // ick, this should be in the mod or maps?
934 k = particlepalette[colorbase + (rand()&3)];
935 if (gamemode == GAME_GOODVSBAD2)
936 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);
938 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);
944 k = particlepalette[colorbase + (rand()&3)];
945 if (gamemode == GAME_GOODVSBAD2)
946 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);
948 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);
952 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
956 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
961 if (!cl_particles.integer) return;
963 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
964 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
965 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
967 center[0] = (mins[0] + maxs[0]) * 0.5f;
968 center[1] = (mins[1] + maxs[1]) * 0.5f;
969 center[2] = (mins[2] + maxs[2]) * 0.5f;
971 count *= cl_particles_quality.value;
974 k = particlepalette[224 + (rand()&15)];
975 o[0] = lhrandom(mins[0], maxs[0]);
976 o[1] = lhrandom(mins[1], maxs[1]);
977 o[2] = lhrandom(mins[2], maxs[2]);
978 VectorSubtract(o, center, v);
979 VectorNormalizeFast(v);
980 VectorScale(v, 100, v);
981 v[2] += sv_gravity.value * 0.15f;
982 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);
986 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
990 if (!cl_particles.integer) return;
991 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
992 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
993 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
995 count *= cl_particles_quality.value;
998 k = particlepalette[224 + (rand()&15)];
999 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);
1001 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);
1005 void CL_Flames (vec3_t org, vec3_t vel, int count)
1008 if (!cl_particles.integer) return;
1010 count *= cl_particles_quality.value;
1013 k = particlepalette[224 + (rand()&15)];
1014 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);
1026 void CL_LavaSplash (vec3_t origin)
1028 float i, j, inc, vel;
1031 if (!cl_particles.integer) return;
1033 inc = 32 / cl_particles_quality.value;
1034 for (i = -128;i < 128;i += inc)
1036 for (j = -128;j < 128;j += inc)
1038 dir[0] = j + lhrandom(0, 8);
1039 dir[1] = i + lhrandom(0, 8);
1041 org[0] = origin[0] + dir[0];
1042 org[1] = origin[1] + dir[1];
1043 org[2] = origin[2] + lhrandom(0, 64);
1044 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1045 if (gamemode == GAME_GOODVSBAD2)
1047 k = particlepalette[0 + (rand()&255)];
1048 l = particlepalette[0 + (rand()&255)];
1049 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);
1053 k = l = particlepalette[224 + (rand()&7)];
1054 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);
1066 void CL_TeleportSplash (vec3_t org)
1069 if (!cl_particles.integer) return;
1071 inc = 8 / cl_particles_quality.value;
1072 for (i = -16;i < 16;i += inc)
1073 for (j = -16;j < 16;j += inc)
1074 for (k = -24;k < 32;k += inc)
1075 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);
1078 #ifdef WORKINGLQUAKE
1079 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1081 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1084 vec3_t vec, dir, vel, pos;
1085 float len, dec, speed, qd;
1086 int smoke, blood, bubbles;
1087 #ifdef WORKINGLQUAKE
1091 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1094 VectorSubtract(end, start, dir);
1095 VectorNormalize(dir);
1097 VectorSubtract (end, start, vec);
1098 #ifdef WORKINGLQUAKE
1099 len = VectorNormalize (vec);
1101 speed = 1.0f / cl.frametime;
1102 VectorSubtract(end, start, vel);
1104 len = VectorNormalizeLength (vec);
1105 dec = -ent->persistent.trail_time;
1106 ent->persistent.trail_time += len;
1107 if (ent->persistent.trail_time < 0.01f)
1110 // if we skip out, leave it reset
1111 ent->persistent.trail_time = 0.0f;
1113 speed = ent->state_current.time - ent->state_previous.time;
1115 speed = 1.0f / speed;
1116 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1117 color = particlepalette[color];
1119 VectorScale(vel, speed, vel);
1121 // advance into this frame to reach the first puff location
1122 VectorMA(start, dec, vec, pos);
1125 smoke = cl_particles.integer && cl_particles_smoke.integer;
1126 blood = cl_particles.integer && cl_particles_blood.integer;
1127 #ifdef WORKINGLQUAKE
1128 contents = CL_PointQ1Contents(pos);
1129 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1131 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1133 qd = 1.0f / cl_particles_quality.value;
1139 case 0: // rocket trail
1143 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);
1144 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);
1147 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));
1150 case 1: // grenade trail
1151 // FIXME: make it gradually stop smoking
1154 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);
1159 case 4: // slight blood
1162 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);
1165 case 3: // green tracer
1169 if (gamemode == GAME_GOODVSBAD2)
1170 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);
1172 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);
1176 case 5: // flame tracer
1179 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);
1182 case 6: // voor trail
1186 if (gamemode == GAME_GOODVSBAD2)
1187 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);
1188 else if (gamemode == GAME_PRYDON)
1189 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);
1191 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);
1194 #ifndef WORKINGLQUAKE
1195 case 7: // Nehahra smoke tracer
1198 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);
1200 case 8: // Nexuiz plasma trail
1203 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);
1205 case 9: // glow trail
1208 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);
1213 // advance to next time and position
1215 VectorMA (pos, dec, vec, pos);
1217 #ifndef WORKINGLQUAKE
1218 ent->persistent.trail_time = len;
1222 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1224 int tempcolor2, cr, cg, cb;
1228 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1229 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);
1232 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1235 if (!cl_particles.integer) return;
1238 if (cl_particles_smoke.integer)
1239 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1240 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);
1243 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1246 if (!cl_particles.integer) return;
1248 if (cl_stainmaps.integer)
1249 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1250 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1253 if (cl_particles_smoke.integer)
1254 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1255 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);
1258 if (cl_particles_sparks.integer)
1259 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1260 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);
1268 void CL_MoveParticles (void)
1271 int i, maxparticle, j, a, content;
1272 float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
1276 // LordHavoc: early out condition
1277 if (!cl_numparticles)
1279 cl_freeparticle = 0;
1283 #ifdef WORKINGLQUAKE
1284 frametime = cl.frametime;
1286 frametime = cl.time - cl.oldtime;
1288 gravity = frametime * sv_gravity.value;
1289 dvel = 1+4*frametime;
1290 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1294 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1301 p->alpha -= p->alphafade * frametime;
1309 if (p->type->orientation != PARTICLE_BEAM)
1311 VectorCopy(p->org, oldorg);
1312 VectorMA(p->org, frametime, p->vel, p->org);
1313 VectorCopy(p->org, org);
1316 if (p->type == particletype + pt_rain)
1318 // raindrop - splash on solid/water/slime/lava
1319 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
1320 if (trace.fraction < 1)
1322 // convert from a raindrop particle to a rainsplash decal
1323 VectorCopy(trace.endpos, p->org);
1324 VectorCopy(trace.plane.normal, p->vel);
1325 VectorAdd(p->org, p->vel, p->org);
1326 p->type = particletype + pt_raindecal;
1327 p->texnum = tex_rainsplash[0];
1329 p->alphafade = p->alpha / 0.4;
1336 else if (p->type == particletype + pt_blood)
1338 // blood - splash on solid
1339 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
1340 if (trace.fraction < 1)
1342 // convert from a blood particle to a blood decal
1343 VectorCopy(trace.endpos, p->org);
1344 VectorCopy(trace.plane.normal, p->vel);
1345 VectorAdd(p->org, p->vel, p->org);
1346 #ifndef WORKINGLQUAKE
1347 if (cl_stainmaps.integer)
1348 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));
1350 if (!cl_decals.integer)
1356 p->type = particletype + pt_decal;
1357 p->texnum = tex_blooddecal[rand()&7];
1358 #ifndef WORKINGLQUAKE
1360 p->ownermodel = cl_entities[hitent].render.model;
1361 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1362 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1374 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
1375 if (trace.fraction < 1)
1377 VectorCopy(trace.endpos, p->org);
1385 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1386 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1387 if (DotProduct(p->vel, p->vel) < 0.03)
1388 VectorClear(p->vel);
1393 p->vel[2] -= p->gravity * gravity;
1397 f = p->friction * frametime;
1398 #ifdef WORKINGLQUAKE
1399 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1401 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1405 VectorScale(p->vel, f, p->vel);
1409 if (p->type != particletype + pt_static)
1411 switch (p->type - particletype)
1413 case pt_entityparticle:
1414 // particle that removes itself after one rendered frame
1421 #ifdef WORKINGLQUAKE
1422 a = CL_PointQ1Contents(p->org);
1423 if (a <= CONTENTS_WATER)
1425 a = CL_PointSuperContents(p->org);
1426 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1429 p->size += frametime * 8;
1430 //p->alpha -= bloodwaterfade;
1433 p->vel[2] -= gravity;
1434 #ifdef WORKINGLQUAKE
1435 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1437 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1442 #ifdef WORKINGLQUAKE
1443 a = CL_PointQ1Contents(p->org);
1444 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1446 a = CL_PointSuperContents(p->org);
1447 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1455 #ifdef WORKINGLQUAKE
1456 a = CL_PointQ1Contents(p->org);
1457 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1459 a = CL_PointSuperContents(p->org);
1460 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1465 if (cl.time > p->time2)
1468 p->time2 = cl.time + (rand() & 3) * 0.1;
1469 p->vel[0] += lhrandom(-32, 32);
1470 p->vel[1] += lhrandom(-32, 32);
1471 p->vel[2] += lhrandom(-32, 32);
1473 #ifdef WORKINGLQUAKE
1474 a = CL_PointQ1Contents(p->org);
1475 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1477 a = CL_PointSuperContents(p->org);
1478 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1483 //p->size += frametime * 15;
1486 // FIXME: this has fairly wacky handling of alpha
1487 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1488 #ifndef WORKINGLQUAKE
1489 if (cl_entities[p->owner].render.model == p->ownermodel)
1491 Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1492 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1499 a = max(0, (cl.time - p->time2) * 40);
1501 p->texnum = tex_rainsplash[a];
1510 cl_numparticles = maxparticle + 1;
1511 cl_freeparticle = 0;
1514 #define MAX_PARTICLETEXTURES 64
1515 // particletexture_t is a rectangle in the particlefonttexture
1518 rtexture_t *texture;
1519 float s1, t1, s2, t2;
1524 static int particlefonttexture;
1526 static rtexturepool_t *particletexturepool;
1527 static rtexture_t *particlefonttexture;
1529 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1531 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1533 #define PARTICLETEXTURESIZE 64
1534 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1536 static qbyte shadebubble(float dx, float dy, vec3_t light)
1540 dz = 1 - (dx*dx+dy*dy);
1541 if (dz > 0) // it does hit the sphere
1545 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1546 VectorNormalize(normal);
1547 dot = DotProduct(normal, light);
1548 if (dot > 0.5) // interior reflection
1549 f += ((dot * 2) - 1);
1550 else if (dot < -0.5) // exterior reflection
1551 f += ((dot * -2) - 1);
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 f += 16; // just to give it a haze so you can see the outline
1562 f = bound(0, f, 255);
1569 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1571 int basex, basey, y;
1572 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1573 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1574 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1575 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1576 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1577 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1578 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1579 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1582 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1585 float cx, cy, dx, dy, f, iradius;
1587 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1588 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1589 iradius = 1.0f / radius;
1590 alpha *= (1.0f / 255.0f);
1591 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1593 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1597 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1600 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1601 d[0] += f * (red - d[0]);
1602 d[1] += f * (green - d[1]);
1603 d[2] += f * (blue - d[2]);
1609 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1612 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1614 data[0] = bound(minr, data[0], maxr);
1615 data[1] = bound(ming, data[1], maxg);
1616 data[2] = bound(minb, data[2], maxb);
1620 void particletextureinvert(qbyte *data)
1623 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1625 data[0] = 255 - data[0];
1626 data[1] = 255 - data[1];
1627 data[2] = 255 - data[2];
1631 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1632 static void R_InitBloodTextures (qbyte *particletexturedata)
1635 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1638 for (i = 0;i < 8;i++)
1640 memset(&data[0][0][0], 255, sizeof(data));
1641 for (k = 0;k < 24;k++)
1642 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1643 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1644 particletextureinvert(&data[0][0][0]);
1645 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1649 for (i = 0;i < 8;i++)
1651 memset(&data[0][0][0], 255, sizeof(data));
1653 for (j = 1;j < 10;j++)
1654 for (k = min(j, m - 1);k < m;k++)
1655 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1656 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1657 particletextureinvert(&data[0][0][0]);
1658 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1663 static void R_InitParticleTexture (void)
1665 int x, y, d, i, k, m;
1666 float dx, dy, radius, f, f2;
1667 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1669 qbyte *particletexturedata;
1671 // a note: decals need to modulate (multiply) the background color to
1672 // properly darken it (stain), and they need to be able to alpha fade,
1673 // this is a very difficult challenge because it means fading to white
1674 // (no change to background) rather than black (darkening everything
1675 // behind the whole decal polygon), and to accomplish this the texture is
1676 // inverted (dark red blood on white background becomes brilliant cyan
1677 // and white on black background) so we can alpha fade it to black, then
1678 // we invert it again during the blendfunc to make it work...
1680 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1681 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1684 for (i = 0;i < 8;i++)
1686 memset(&data[0][0][0], 255, sizeof(data));
1689 qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1691 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1692 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1694 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1696 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1697 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1699 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1700 d = (noise2[y][x] - 128) * 3 + 192;
1702 d = d * (1-(dx*dx+dy*dy));
1703 d = (d * noise1[y][x]) >> 7;
1704 d = bound(0, d, 255);
1705 data[y][x][3] = (qbyte) d;
1712 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1716 for (i = 0;i < 16;i++)
1718 memset(&data[0][0][0], 255, sizeof(data));
1719 radius = i * 3.0f / 4.0f / 16.0f;
1720 f2 = 255.0f * ((15.0f - i) / 15.0f);
1721 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1723 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1724 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1726 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1727 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1728 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1731 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1735 memset(&data[0][0][0], 255, sizeof(data));
1736 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1738 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1739 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1741 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1742 d = 256 * (1 - (dx*dx+dy*dy));
1743 d = bound(0, d, 255);
1744 data[y][x][3] = (qbyte) d;
1747 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1750 memset(&data[0][0][0], 255, sizeof(data));
1751 light[0] = 1;light[1] = 1;light[2] = 1;
1752 VectorNormalize(light);
1753 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1755 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1756 // stretch upper half of bubble by +50% and shrink lower half by -50%
1757 // (this gives an elongated teardrop shape)
1759 dy = (dy - 0.5f) * 2.0f;
1761 dy = (dy - 0.5f) / 1.5f;
1762 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1764 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1765 // shrink bubble width to half
1767 data[y][x][3] = shadebubble(dx, dy, light);
1770 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1773 memset(&data[0][0][0], 255, sizeof(data));
1774 light[0] = 1;light[1] = 1;light[2] = 1;
1775 VectorNormalize(light);
1776 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1778 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1779 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1781 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1782 data[y][x][3] = shadebubble(dx, dy, light);
1785 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1787 // Blood particles and blood decals
1788 R_InitBloodTextures (particletexturedata);
1791 for (i = 0;i < 8;i++)
1793 memset(&data[0][0][0], 255, sizeof(data));
1794 for (k = 0;k < 12;k++)
1795 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1796 for (k = 0;k < 3;k++)
1797 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1798 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1799 particletextureinvert(&data[0][0][0]);
1800 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1804 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1805 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1806 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1810 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1813 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1814 if (!particlefonttexture)
1815 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1816 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1817 particletexture[i].texture = particlefonttexture;
1820 fractalnoise(&noise3[0][0], 64, 4);
1822 for (y = 0;y < 64;y++)
1824 dy = (y - 0.5f*64) / (64*0.5f-1);
1825 for (x = 0;x < 16;x++)
1827 dx = (x - 0.5f*16) / (16*0.5f-2);
1828 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1829 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1830 data2[y][x][3] = 255;
1835 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1838 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1839 if (!particletexture[tex_beam].texture)
1840 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1841 particletexture[tex_beam].s1 = 0;
1842 particletexture[tex_beam].t1 = 0;
1843 particletexture[tex_beam].s2 = 1;
1844 particletexture[tex_beam].t2 = 1;
1846 Mem_Free(particletexturedata);
1849 static void r_part_start(void)
1851 particletexturepool = R_AllocTexturePool();
1852 R_InitParticleTexture ();
1855 static void r_part_shutdown(void)
1857 R_FreeTexturePool(&particletexturepool);
1860 static void r_part_newmap(void)
1862 cl_numparticles = 0;
1863 cl_freeparticle = 0;
1866 void R_Particles_Init (void)
1868 Cvar_RegisterVariable(&r_drawparticles);
1869 #ifdef WORKINGLQUAKE
1872 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1876 #ifdef WORKINGLQUAKE
1877 void R_InitParticles(void)
1879 CL_Particles_Init();
1884 float particle_vertex3f[12], particle_texcoord2f[8];
1886 #ifdef WORKINGLQUAKE
1887 void R_DrawParticle(particle_t *p)
1890 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1892 const particle_t *p = calldata1;
1896 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, size;
1897 particletexture_t *tex;
1899 VectorCopy(p->org, org);
1901 blendmode = p->type->blendmode;
1902 tex = &particletexture[p->texnum];
1903 cr = p->color[0] * (1.0f / 255.0f);
1904 cg = p->color[1] * (1.0f / 255.0f);
1905 cb = p->color[2] * (1.0f / 255.0f);
1906 ca = p->alpha * (1.0f / 255.0f);
1907 if (blendmode == PBLEND_MOD)
1917 #ifndef WORKINGLQUAKE
1918 if (p->type->lighting)
1920 float ambient[3], diffuse[3], diffusenormal[3];
1921 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
1922 cr *= (ambient[0] + 0.5 * diffuse[0]);
1923 cg *= (ambient[1] + 0.5 * diffuse[1]);
1924 cb *= (ambient[2] + 0.5 * diffuse[2]);
1928 VectorSubtract(org, r_vieworigin, fogvec);
1929 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1934 if (blendmode == PBLEND_ALPHA)
1936 cr += fogcolor[0] * fog;
1937 cg += fogcolor[1] * fog;
1938 cb += fogcolor[2] * fog;
1942 R_Mesh_Matrix(&r_identitymatrix);
1944 memset(&m, 0, sizeof(m));
1945 m.tex[0] = R_GetTexture(tex->texture);
1946 m.pointer_texcoord[0] = particle_texcoord2f;
1947 m.pointer_vertex = particle_vertex3f;
1950 GL_Color(cr, cg, cb, ca);
1952 if (blendmode == PBLEND_ALPHA)
1953 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1954 else if (blendmode == PBLEND_ADD)
1955 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1956 else //if (blendmode == PBLEND_MOD)
1957 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1958 GL_DepthMask(false);
1961 size = p->size * cl_particles_size.value;
1962 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1964 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1967 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1969 VectorNegate(p->vel, v);
1970 VectorVectors(v, right, up);
1973 VectorVectors(p->vel, right, up);
1974 VectorScale(right, size, right);
1975 VectorScale(up, size, up);
1979 VectorScale(r_viewleft, -size, right);
1980 VectorScale(r_viewup, size, up);
1982 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1983 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1984 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1985 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1986 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1987 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1988 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1989 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1990 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1991 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1992 particle_vertex3f[10] = org[1] + right[1] - up[1];
1993 particle_vertex3f[11] = org[2] + right[2] - up[2];
1994 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1995 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1996 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1997 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1999 else if (p->type->orientation == PARTICLE_SPARK)
2001 VectorMA(p->org, -0.02, p->vel, v);
2002 VectorMA(p->org, 0.02, p->vel, up2);
2003 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2004 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2005 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2006 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2007 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2009 else if (p->type->orientation == PARTICLE_BEAM)
2011 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2012 VectorSubtract(p->vel, p->org, up);
2013 VectorNormalizeFast(up);
2014 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2015 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2016 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2017 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2018 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2019 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2022 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2025 if (blendmode == PBLEND_ALPHA)
2026 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2027 else if (blendmode == PBLEND_ADD)
2028 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2029 else //if (blendmode == PBLEND_MOD)
2030 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2031 glColor4f(cr, cg, cb, ca);
2033 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2034 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2035 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2036 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2039 R_Mesh_Draw(0, 4, 2, polygonelements);
2043 void R_DrawParticles (void)
2046 float minparticledist;
2049 #ifdef WORKINGLQUAKE
2053 // LordHavoc: early out conditions
2054 if ((!cl_numparticles) || (!r_drawparticles.integer))
2057 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2059 #ifdef WORKINGLQUAKE
2060 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2062 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2064 // LordHavoc: only render if not too close
2065 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2066 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2069 glDisable(GL_BLEND);
2070 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2072 // LordHavoc: only render if not too close
2073 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2078 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2080 if (p->type == particletype + pt_decal)
2081 R_DrawParticleCallback(p, 0);
2083 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);