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))
81 Sys_Error("fractalnoise: size must be power of 2\n");
83 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
84 if (startgrid != (1 << gridpower))
85 Sys_Error("fractalnoise: grid must be power of 2\n");
87 startgrid = bound(0, startgrid, size);
89 amplitude = 0xFFFF; // this gets halved before use
90 noisebuf = malloc(size*size*sizeof(int));
91 memset(noisebuf, 0, size*size*sizeof(int));
93 for (g2 = startgrid;g2;g2 >>= 1)
95 // brownian motion (at every smaller level there is random behavior)
97 for (y = 0;y < size;y += g2)
98 for (x = 0;x < size;x += g2)
99 n(x,y) += (rand()&litude);
104 // subdivide, diamond-square algorithm (really this has little to do with squares)
106 for (y = 0;y < size;y += g2)
107 for (x = 0;x < size;x += g2)
108 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
110 for (y = 0;y < size;y += g2)
111 for (x = 0;x < size;x += g2)
113 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
114 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
118 // find range of noise values
120 for (y = 0;y < size;y++)
121 for (x = 0;x < size;x++)
123 if (n(x,y) < min) min = n(x,y);
124 if (n(x,y) > max) max = n(x,y);
128 // normalize noise and copy to output
129 for (y = 0;y < size;y++)
130 for (x = 0;x < size;x++)
131 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
135 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
139 right[0] = forward[2];
140 right[1] = -forward[0];
141 right[2] = forward[1];
143 d = DotProduct(forward, right);
144 right[0] -= d * forward[0];
145 right[1] -= d * forward[1];
146 right[2] -= d * forward[2];
147 VectorNormalize(right);
148 CrossProduct(right, forward, up);
152 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
154 trace_t CL_TraceBox (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hitbmodels, int *hitent, int hitsupercontentsmask, qboolean hitplayers)
161 memset (&trace, 0, sizeof(trace));
163 VectorCopy (end, trace.endpos);
165 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
167 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
172 #include "cl_collision.h"
176 #define MAX_PARTICLES 32768 // default max # of particles at one time
177 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
181 PARTICLE_BILLBOARD = 0,
183 PARTICLE_ORIENTED_DOUBLESIDED = 2,
196 typedef struct particletype_s
199 porientation_t orientation;
206 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
210 // must match ptype_t values
211 particletype_t particletype[pt_total] =
213 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
214 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
215 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
216 {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
217 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
218 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
219 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
220 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
221 {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
222 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
223 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
224 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
227 typedef struct particle_s
229 particletype_t *type;
232 vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam
234 float alpha; // 0-255
235 float alphafade; // how much alpha reduces per second
236 float time2; // used for snow fluttering and decal fade
237 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)
238 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
239 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
241 unsigned short owner; // decal stuck to this entity
242 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
243 vec3_t relativeorigin; // decal at this location in entity's coordinate space
244 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
248 static int particlepalette[256] =
250 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
251 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
252 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
253 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
254 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
255 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
256 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
257 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
258 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
259 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
260 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
261 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
262 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
263 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
264 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
265 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
266 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
267 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
268 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
269 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
270 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
271 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
272 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
273 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
274 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
275 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
276 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
277 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
278 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
279 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
280 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
281 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
284 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
286 // texture numbers in particle font
287 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
288 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
289 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
290 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
291 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
292 static const int tex_particle = 63;
293 static const int tex_bubble = 62;
294 static const int tex_raindrop = 61;
295 static const int tex_beam = 60;
297 static int cl_maxparticles;
298 static int cl_numparticles;
299 static int cl_freeparticle;
300 static particle_t *particles;
302 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
303 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
304 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
305 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
306 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
307 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
308 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
309 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
310 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
311 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
312 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
313 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
314 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
315 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
316 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
317 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
318 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
319 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
320 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
321 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
323 void CL_Particles_Clear(void)
327 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
335 void CL_ReadPointFile_f (void);
336 void CL_Particles_Init (void)
340 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
341 i = COM_CheckParm ("-particles");
343 if (i && i < com_argc - 1)
345 cl_maxparticles = (int)(atoi(com_argv[i+1]));
346 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
347 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
350 cl_maxparticles = MAX_PARTICLES;
352 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
354 Cvar_RegisterVariable (&cl_particles);
355 Cvar_RegisterVariable (&cl_particles_quality);
356 Cvar_RegisterVariable (&cl_particles_size);
357 Cvar_RegisterVariable (&cl_particles_bloodshowers);
358 Cvar_RegisterVariable (&cl_particles_blood);
359 Cvar_RegisterVariable (&cl_particles_blood_alpha);
360 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
361 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
362 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
363 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
364 Cvar_RegisterVariable (&cl_particles_explosions_shell);
365 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
366 Cvar_RegisterVariable (&cl_particles_smoke);
367 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
368 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
369 Cvar_RegisterVariable (&cl_particles_sparks);
370 Cvar_RegisterVariable (&cl_particles_bubbles);
371 Cvar_RegisterVariable (&cl_decals);
372 Cvar_RegisterVariable (&cl_decals_time);
373 Cvar_RegisterVariable (&cl_decals_fadetime);
376 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
378 particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
380 CL_Particles_Clear();
383 void CL_Particles_Shutdown (void)
386 // No clue what to do here...
390 // list of all 26 parameters:
391 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
392 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
393 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
394 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
395 // palpha - opacity of particle as 0-255 (can be more than 255)
396 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
397 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
398 // pgravity - how much effect gravity has on the particle (0-1)
399 // 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
400 // px,py,pz - starting origin of particle
401 // pvx,pvy,pvz - starting velocity of particle
402 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
403 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)
406 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
407 ptempcolor = (pcolor1);
408 ptempcolor2 = (pcolor2);
409 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
410 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
411 pcb2 = (ptempcolor2) & 0xFF;
412 if (ptempcolor != ptempcolor2)
414 pcr1 = ((ptempcolor) >> 16) & 0xFF;
415 pcg1 = ((ptempcolor) >> 8) & 0xFF;
416 pcb1 = (ptempcolor) & 0xFF;
417 ptempcolor = rand() & 0xFF;
418 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
419 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
420 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
422 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
423 if (cl_freeparticle >= cl_maxparticles)
425 part = &particles[cl_freeparticle++];
426 if (cl_numparticles < cl_freeparticle)
427 cl_numparticles = cl_freeparticle;
428 memset(part, 0, sizeof(*part));
429 part->type = (ptype);
430 part->color[0] = pcr2;
431 part->color[1] = pcg2;
432 part->color[2] = pcb2;
433 part->color[3] = 0xFF;
435 part->size = (psize);
436 part->alpha = (palpha);
437 part->alphafade = (palphafade);
438 part->gravity = (pgravity);
439 part->bounce = (pbounce);
443 part->vel[0] = (pvx);
444 part->vel[1] = (pvy);
445 part->vel[2] = (pvz);
447 part->friction = (pfriction);
451 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
454 if (!cl_decals.integer)
456 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);
460 #ifndef WORKINGLQUAKE
462 p->ownermodel = cl_entities[p->owner].render.model;
463 Matrix4x4_Transform(&cl_entities[p->owner].render.inversematrix, org, p->relativeorigin);
464 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.inversematrix, normal, p->relativedirection);
465 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
470 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
473 float bestfrac, bestorg[3], bestnormal[3];
475 int besthitent = 0, hitent;
478 for (i = 0;i < 32;i++)
481 VectorMA(org, maxdist, org2, org2);
482 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, &hitent, SUPERCONTENTS_SOLID, false);
483 if (bestfrac > trace.fraction)
485 bestfrac = trace.fraction;
487 VectorCopy(trace.endpos, bestorg);
488 VectorCopy(trace.plane.normal, bestnormal);
492 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
500 void CL_EntityParticles (entity_t *ent)
504 float sp, sy, cp, cy;
508 static vec3_t avelocities[NUMVERTEXNORMALS];
509 if (!cl_particles.integer) return;
514 if (!avelocities[0][0])
515 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
516 avelocities[0][i] = (rand()&255) * 0.01;
518 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
520 angle = cl.time * avelocities[i][0];
523 angle = cl.time * avelocities[i][1];
532 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);
534 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);
540 void CL_ReadPointFile_f (void)
544 char *pointfile = NULL, *pointfilepos, *t, tchar;
545 char name[MAX_OSPATH];
550 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
551 strlcat (name, ".pts", sizeof (name));
553 pointfile = COM_LoadTempFile (name);
555 pointfile = FS_LoadFile(name, tempmempool, true);
559 Con_Printf("Could not open %s\n", name);
563 Con_Printf("Reading %s...\n", name);
566 pointfilepos = pointfile;
567 while (*pointfilepos)
569 while (*pointfilepos == '\n' || *pointfilepos == '\r')
574 while (*t && *t != '\n' && *t != '\r')
578 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
584 VectorCopy(org, leakorg);
587 if (cl_numparticles < cl_maxparticles - 3)
590 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);
593 #ifndef WORKINGLQUAKE
596 VectorCopy(leakorg, org);
597 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
599 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);
600 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);
601 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);
606 CL_ParseParticleEffect
608 Parse an effect out of the server message
611 void CL_ParseParticleEffect (void)
614 int i, count, msgcount, color;
616 MSG_ReadVector(org, cl.protocol);
617 for (i=0 ; i<3 ; i++)
618 dir[i] = MSG_ReadChar () * (1.0/16);
619 msgcount = MSG_ReadByte ();
620 color = MSG_ReadByte ();
627 if (cl_particles_blood_bloodhack.integer)
632 CL_BloodPuff(org, dir, count / 2);
638 CL_BloodPuff(org, dir, count / 2);
642 CL_RunParticleEffect (org, dir, color, count);
651 void CL_ParticleExplosion (vec3_t org)
657 if (cl_stainmaps.integer)
658 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
659 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
661 i = CL_PointSuperContents(org);
662 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
664 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
665 for (i = 0;i < 128 * cl_particles_quality.value;i++)
666 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));
670 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
672 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
674 for (i = 0;i < 32;i++)
679 v2[0] = lhrandom(-48, 48);
680 v2[1] = lhrandom(-48, 48);
681 v2[2] = lhrandom(-48, 48);
683 for (k = 0;k < 16;k++)
685 v[0] = org[0] + lhrandom(-48, 48);
686 v[1] = org[1] + lhrandom(-48, 48);
687 v[2] = org[2] + lhrandom(-48, 48);
688 trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
689 if (trace.fraction >= 0.1)
692 VectorSubtract(trace.endpos, org, v2);
694 VectorScale(v2, 2.0f, v2);
695 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);
699 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
700 for (i = 0;i < 128 * cl_particles_quality.value;i++)
701 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);
704 if (cl_particles_explosions_shell.integer)
710 CL_ParticleExplosion2
714 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
720 if (!cl_particles.integer) return;
722 for (i = 0;i < 512 * cl_particles_quality.value;i++)
724 VectorRandom (offset);
725 VectorScale (offset, 192, vel);
726 VectorScale (offset, 8, offset);
727 k = particlepalette[colorStart + (i % colorLength)];
728 pscale = lhrandom(0.5, 1.5);
729 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));
739 void CL_BlobExplosion (vec3_t org)
741 CL_ParticleExplosion(org);
750 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
756 CL_ParticleExplosion(org);
759 if (!cl_particles.integer) return;
760 count *= cl_particles_quality.value;
763 k = particlepalette[color + (rand()&7)];
764 if (gamemode == GAME_GOODVSBAD2)
765 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);
767 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);
771 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
777 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
781 if (!cl_particles.integer) return;
783 if (cl_particles_sparks.integer)
786 count *= cl_particles_quality.value;
789 k = particlepalette[0x68 + (rand() & 7)];
790 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);
795 void CL_Smoke (vec3_t org, vec3_t dir, int count)
801 if (!cl_particles.integer) return;
804 if (cl_particles_smoke.integer)
806 k = count * 0.25 * cl_particles_quality.value;
809 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
810 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
811 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
812 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
813 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);
818 void CL_BulletMark (vec3_t org)
820 if (cl_stainmaps.integer)
821 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
822 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
825 void CL_PlasmaBurn (vec3_t org)
827 if (cl_stainmaps.integer)
828 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
829 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
832 static float bloodcount = 0;
833 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
838 // bloodcount is used to accumulate counts too small to cause a blood particle
839 if (!cl_particles.integer) return;
840 if (!cl_particles_blood.integer) return;
847 while(bloodcount > 0)
849 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
850 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
851 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
852 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
853 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);
854 bloodcount -= 16 / cl_particles_quality.value;
858 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
860 vec3_t org, vel, diff, center, velscale;
861 if (!cl_particles.integer) return;
862 if (!cl_particles_bloodshowers.integer) return;
863 if (!cl_particles_blood.integer) return;
865 VectorSubtract(maxs, mins, diff);
866 center[0] = (mins[0] + maxs[0]) * 0.5;
867 center[1] = (mins[1] + maxs[1]) * 0.5;
868 center[2] = (mins[2] + maxs[2]) * 0.5;
869 velscale[0] = velspeed * 2.0 / diff[0];
870 velscale[1] = velspeed * 2.0 / diff[1];
871 velscale[2] = velspeed * 2.0 / diff[2];
873 bloodcount += count * 5.0f;
874 while (bloodcount > 0)
876 org[0] = lhrandom(mins[0], maxs[0]);
877 org[1] = lhrandom(mins[1], maxs[1]);
878 org[2] = lhrandom(mins[2], maxs[2]);
879 vel[0] = (org[0] - center[0]) * velscale[0];
880 vel[1] = (org[1] - center[1]) * velscale[1];
881 vel[2] = (org[2] - center[2]) * velscale[2];
882 bloodcount -= 16 / cl_particles_quality.value;
883 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);
887 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
891 if (!cl_particles.integer) return;
892 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
893 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
894 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
896 count *= cl_particles_quality.value;
899 k = particlepalette[colorbase + (rand()&3)];
900 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);
904 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
907 float t, z, minz, maxz;
909 if (!cl_particles.integer) return;
910 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
911 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
912 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
913 if (dir[2] < 0) // falling
918 minz = z - fabs(dir[2]) * 0.1;
919 maxz = z + fabs(dir[2]) * 0.1;
920 minz = bound(mins[2], minz, maxs[2]);
921 maxz = bound(mins[2], maxz, maxs[2]);
923 count *= cl_particles_quality.value;
928 count *= 4; // ick, this should be in the mod or maps?
932 k = particlepalette[colorbase + (rand()&3)];
933 if (gamemode == GAME_GOODVSBAD2)
934 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);
936 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);
942 k = particlepalette[colorbase + (rand()&3)];
943 if (gamemode == GAME_GOODVSBAD2)
944 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);
946 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);
948 VectorCopy(p->vel, p->relativedirection);
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);
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] = p->relativedirection[0] + lhrandom(-32, 32);
1470 p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1471 //p->vel[2] = p->relativedirection[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 VectorNormalize(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);