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 unsigned char;
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(unsigned char *noise, int size, int startgrid)
75 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
77 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
79 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
80 if (size != (1 << sizepower))
82 Con_Printf("fractalnoise: size must be power of 2\n");
86 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
87 if (startgrid != (1 << gridpower))
89 Con_Printf("fractalnoise: grid must be power of 2\n");
93 startgrid = bound(0, startgrid, size);
95 amplitude = 0xFFFF; // this gets halved before use
96 noisebuf = malloc(size*size*sizeof(int));
97 memset(noisebuf, 0, size*size*sizeof(int));
99 for (g2 = startgrid;g2;g2 >>= 1)
101 // brownian motion (at every smaller level there is random behavior)
103 for (y = 0;y < size;y += g2)
104 for (x = 0;x < size;x += g2)
105 n(x,y) += (rand()&litude);
110 // subdivide, diamond-square algorithm (really this has little to do with squares)
112 for (y = 0;y < size;y += g2)
113 for (x = 0;x < size;x += g2)
114 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
116 for (y = 0;y < size;y += g2)
117 for (x = 0;x < size;x += g2)
119 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
120 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
124 // find range of noise values
126 for (y = 0;y < size;y++)
127 for (x = 0;x < size;x++)
129 if (n(x,y) < min) min = n(x,y);
130 if (n(x,y) > max) max = n(x,y);
134 // normalize noise and copy to output
135 for (y = 0;y < size;y++)
136 for (x = 0;x < size;x++)
137 *noise++ = (unsigned char) (((n(x,y) - min) * 256) / max);
141 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
145 right[0] = forward[2];
146 right[1] = -forward[0];
147 right[2] = forward[1];
149 d = DotProduct(forward, right);
150 right[0] -= d * forward[0];
151 right[1] -= d * forward[1];
152 right[2] -= d * forward[2];
153 VectorNormalize(right);
154 CrossProduct(right, forward, up);
158 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
160 trace_t CL_TraceBox (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hitbmodels, int *hitent, int hitsupercontentsmask, qboolean hitplayers)
167 memset (&trace, 0, sizeof(trace));
169 VectorCopy (end, trace.endpos);
171 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
173 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
178 #include "cl_collision.h"
182 #define MAX_PARTICLES 32768 // default max # of particles at one time
183 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
187 PARTICLE_BILLBOARD = 0,
189 PARTICLE_ORIENTED_DOUBLESIDED = 2,
202 typedef struct particletype_s
205 porientation_t orientation;
212 pt_alphastatic, pt_static, pt_spark, pt_beam, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_smoke, pt_decal, pt_entityparticle, pt_total
216 // must match ptype_t values
217 particletype_t particletype[pt_total] =
219 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
220 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
221 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
222 {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
223 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
224 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
225 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
226 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
227 {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
228 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
229 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
230 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
233 typedef struct particle_s
235 particletype_t *type;
238 vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam
240 float alpha; // 0-255
241 float alphafade; // how much alpha reduces per second
242 float time2; // used for snow fluttering and decal fade
243 float bounce; // how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide, 2 = keep bouncing forever, 1.5 is typical)
244 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
245 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
246 unsigned char color[4];
247 unsigned short owner; // decal stuck to this entity
248 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
249 vec3_t relativeorigin; // decal at this location in entity's coordinate space
250 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
254 static int particlepalette[256] =
256 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
257 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
258 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
259 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
260 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
261 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
262 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
263 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
264 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
265 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
266 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
267 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
268 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
269 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
270 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
271 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
272 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
273 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
274 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
275 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
276 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
277 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
278 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
279 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
280 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
281 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
282 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
283 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
284 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
285 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
286 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
287 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
290 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
292 // texture numbers in particle font
293 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
294 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
295 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
296 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
297 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
298 static const int tex_particle = 63;
299 static const int tex_bubble = 62;
300 static const int tex_raindrop = 61;
301 static const int tex_beam = 60;
303 static int cl_maxparticles;
304 static int cl_numparticles;
305 static int cl_freeparticle;
306 static particle_t *particles;
308 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
309 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
310 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
311 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
312 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
313 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
314 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
315 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
316 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
317 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
318 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
319 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
320 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
321 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
322 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
323 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
324 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
325 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
326 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
327 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
329 void CL_Particles_Clear(void)
333 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
341 void CL_ReadPointFile_f (void);
342 void CL_Particles_Init (void)
346 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
347 i = COM_CheckParm ("-particles");
349 if (i && i < com_argc - 1)
351 cl_maxparticles = (int)(atoi(com_argv[i+1]));
352 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
353 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
356 cl_maxparticles = MAX_PARTICLES;
358 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
360 Cvar_RegisterVariable (&cl_particles);
361 Cvar_RegisterVariable (&cl_particles_quality);
362 Cvar_RegisterVariable (&cl_particles_size);
363 Cvar_RegisterVariable (&cl_particles_bloodshowers);
364 Cvar_RegisterVariable (&cl_particles_blood);
365 Cvar_RegisterVariable (&cl_particles_blood_alpha);
366 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
367 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
368 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
369 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
370 Cvar_RegisterVariable (&cl_particles_explosions_shell);
371 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
372 Cvar_RegisterVariable (&cl_particles_smoke);
373 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
374 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
375 Cvar_RegisterVariable (&cl_particles_sparks);
376 Cvar_RegisterVariable (&cl_particles_bubbles);
377 Cvar_RegisterVariable (&cl_decals);
378 Cvar_RegisterVariable (&cl_decals_time);
379 Cvar_RegisterVariable (&cl_decals_fadetime);
382 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
384 particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
386 CL_Particles_Clear();
389 void CL_Particles_Shutdown (void)
392 // No clue what to do here...
396 // list of all 26 parameters:
397 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
398 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
399 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
400 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
401 // palpha - opacity of particle as 0-255 (can be more than 255)
402 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
403 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
404 // pgravity - how much effect gravity has on the particle (0-1)
405 // pbounce - how much bounce the particle has when it hits a surface (0-1), -1 makes a blood splat when it hits a surface, 0 does not even check for collisions
406 // px,py,pz - starting origin of particle
407 // pvx,pvy,pvz - starting velocity of particle
408 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
409 particle_t *particle(particletype_t *ptype, int pcolor1, int pcolor2, int ptex, float psize, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pfriction)
412 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
413 ptempcolor = (pcolor1);
414 ptempcolor2 = (pcolor2);
415 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
416 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
417 pcb2 = (ptempcolor2) & 0xFF;
418 if (ptempcolor != ptempcolor2)
420 pcr1 = ((ptempcolor) >> 16) & 0xFF;
421 pcg1 = ((ptempcolor) >> 8) & 0xFF;
422 pcb1 = (ptempcolor) & 0xFF;
423 ptempcolor = rand() & 0xFF;
424 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
425 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
426 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
428 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
429 if (cl_freeparticle >= cl_maxparticles)
431 part = &particles[cl_freeparticle++];
432 if (cl_numparticles < cl_freeparticle)
433 cl_numparticles = cl_freeparticle;
434 memset(part, 0, sizeof(*part));
435 part->type = (ptype);
436 part->color[0] = pcr2;
437 part->color[1] = pcg2;
438 part->color[2] = pcb2;
439 part->color[3] = 0xFF;
441 part->size = (psize);
442 part->alpha = (palpha);
443 part->alphafade = (palphafade);
444 part->gravity = (pgravity);
445 part->bounce = (pbounce);
449 part->vel[0] = (pvx);
450 part->vel[1] = (pvy);
451 part->vel[2] = (pvz);
453 part->friction = (pfriction);
457 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
460 if (!cl_decals.integer)
462 p = particle(particletype + pt_decal, color1, color2, texnum, size, alpha, 0, 0, 0, org[0] + normal[0], org[1] + normal[1], org[2] + normal[2], normal[0], normal[1], normal[2], 0);
466 #ifndef WORKINGLQUAKE
468 p->ownermodel = cl_entities[p->owner].render.model;
469 Matrix4x4_Transform(&cl_entities[p->owner].render.inversematrix, org, p->relativeorigin);
470 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.inversematrix, normal, p->relativedirection);
471 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
476 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
479 float bestfrac, bestorg[3], bestnormal[3];
481 int besthitent = 0, hitent;
484 for (i = 0;i < 32;i++)
487 VectorMA(org, maxdist, org2, org2);
488 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, &hitent, SUPERCONTENTS_SOLID, false);
489 if (bestfrac > trace.fraction)
491 bestfrac = trace.fraction;
493 VectorCopy(trace.endpos, bestorg);
494 VectorCopy(trace.plane.normal, bestnormal);
498 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
506 void CL_EntityParticles (entity_t *ent)
509 float pitch, yaw, dist = 64, beamlength = 16, org[3], v[3];
510 static vec3_t avelocities[NUMVERTEXNORMALS];
511 if (!cl_particles.integer) return;
514 VectorCopy(ent->origin, org);
516 Matrix4x4_OriginFromMatrix(&ent->render.matrix, org);
519 if (!avelocities[0][0])
520 for (i = 0;i < NUMVERTEXNORMALS * 3;i++)
521 avelocities[0][i] = lhrandom(0, 2.55);
523 for (i = 0;i < NUMVERTEXNORMALS;i++)
525 yaw = cl.time * avelocities[i][0];
526 pitch = cl.time * avelocities[i][1];
527 v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
528 v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
529 v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
530 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 0, 0, 0, v[0], v[1], v[2], 0, 0, 0, 0);
535 void CL_ReadPointFile_f (void)
539 char *pointfile = NULL, *pointfilepos, *t, tchar;
540 char name[MAX_OSPATH];
545 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
546 strlcat (name, ".pts", sizeof (name));
548 pointfile = COM_LoadTempFile (name);
550 pointfile = (char *)FS_LoadFile(name, tempmempool, true, NULL);
554 Con_Printf("Could not open %s\n", name);
558 Con_Printf("Reading %s...\n", name);
559 VectorClear(leakorg);
562 pointfilepos = pointfile;
563 while (*pointfilepos)
565 while (*pointfilepos == '\n' || *pointfilepos == '\r')
570 while (*t && *t != '\n' && *t != '\r')
574 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
580 VectorCopy(org, leakorg);
583 if (cl_numparticles < cl_maxparticles - 3)
586 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);
589 #ifndef WORKINGLQUAKE
592 VectorCopy(leakorg, org);
593 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
595 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);
596 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);
597 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);
602 CL_ParseParticleEffect
604 Parse an effect out of the server message
607 void CL_ParseParticleEffect (void)
610 int i, count, msgcount, color;
612 MSG_ReadVector(org, cl.protocol);
613 for (i=0 ; i<3 ; i++)
614 dir[i] = MSG_ReadChar () * (1.0/16);
615 msgcount = MSG_ReadByte ();
616 color = MSG_ReadByte ();
623 if (cl_particles_blood_bloodhack.integer)
628 CL_BloodPuff(org, dir, count / 2);
634 CL_BloodPuff(org, dir, count / 2);
638 CL_RunParticleEffect (org, dir, color, count);
647 void CL_ParticleExplosion (vec3_t org)
653 if (cl_stainmaps.integer)
654 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
655 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
657 i = CL_PointSuperContents(org);
658 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
660 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
661 for (i = 0;i < 128 * cl_particles_quality.value;i++)
662 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));
666 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
668 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
670 for (i = 0;i < 32;i++)
675 v2[0] = lhrandom(-48, 48);
676 v2[1] = lhrandom(-48, 48);
677 v2[2] = lhrandom(-48, 48);
679 for (k = 0;k < 16;k++)
681 v[0] = org[0] + lhrandom(-48, 48);
682 v[1] = org[1] + lhrandom(-48, 48);
683 v[2] = org[2] + lhrandom(-48, 48);
684 trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
685 if (trace.fraction >= 0.1)
688 VectorSubtract(trace.endpos, org, v2);
690 VectorScale(v2, 2.0f, v2);
691 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);
695 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
696 for (i = 0;i < 128 * cl_particles_quality.value;i++)
697 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);
700 if (cl_particles_explosions_shell.integer)
706 CL_ParticleExplosion2
710 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
716 if (!cl_particles.integer) return;
718 for (i = 0;i < 512 * cl_particles_quality.value;i++)
720 VectorRandom (offset);
721 VectorScale (offset, 192, vel);
722 VectorScale (offset, 8, offset);
723 k = particlepalette[colorStart + (i % colorLength)];
724 pscale = lhrandom(0.5, 1.5);
725 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));
735 void CL_BlobExplosion (vec3_t org)
737 CL_ParticleExplosion(org);
746 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
752 CL_ParticleExplosion(org);
755 if (!cl_particles.integer) return;
756 count *= cl_particles_quality.value;
759 k = particlepalette[color + (rand()&7)];
760 if (gamemode == GAME_GOODVSBAD2)
761 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);
763 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);
767 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
773 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
777 if (!cl_particles.integer) return;
779 if (cl_particles_sparks.integer)
782 count *= cl_particles_quality.value;
785 k = particlepalette[0x68 + (rand() & 7)];
786 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);
791 void CL_Smoke (vec3_t org, vec3_t dir, int count)
797 if (!cl_particles.integer) return;
800 if (cl_particles_smoke.integer)
802 k = count * 0.25 * cl_particles_quality.value;
805 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
806 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
807 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
808 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
809 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);
814 void CL_BulletMark (vec3_t org)
816 if (cl_stainmaps.integer)
817 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
818 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
821 void CL_PlasmaBurn (vec3_t org)
823 if (cl_stainmaps.integer)
824 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
825 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
828 static float bloodcount = 0;
829 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
834 // bloodcount is used to accumulate counts too small to cause a blood particle
835 if (!cl_particles.integer) return;
836 if (!cl_particles_blood.integer) return;
843 while(bloodcount > 0)
845 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
846 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
847 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
848 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
849 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);
850 bloodcount -= 16 / cl_particles_quality.value;
854 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
856 vec3_t org, vel, diff, center, velscale;
857 if (!cl_particles.integer) return;
858 if (!cl_particles_bloodshowers.integer) return;
859 if (!cl_particles_blood.integer) return;
861 VectorSubtract(maxs, mins, diff);
862 center[0] = (mins[0] + maxs[0]) * 0.5;
863 center[1] = (mins[1] + maxs[1]) * 0.5;
864 center[2] = (mins[2] + maxs[2]) * 0.5;
865 velscale[0] = velspeed * 2.0 / diff[0];
866 velscale[1] = velspeed * 2.0 / diff[1];
867 velscale[2] = velspeed * 2.0 / diff[2];
869 bloodcount += count * 5.0f;
870 while (bloodcount > 0)
872 org[0] = lhrandom(mins[0], maxs[0]);
873 org[1] = lhrandom(mins[1], maxs[1]);
874 org[2] = lhrandom(mins[2], maxs[2]);
875 vel[0] = (org[0] - center[0]) * velscale[0];
876 vel[1] = (org[1] - center[1]) * velscale[1];
877 vel[2] = (org[2] - center[2]) * velscale[2];
878 bloodcount -= 16 / cl_particles_quality.value;
879 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);
883 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
887 if (!cl_particles.integer) return;
888 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
889 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
890 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
892 count *= cl_particles_quality.value;
895 k = particlepalette[colorbase + (rand()&3)];
896 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);
900 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
903 float t, z, minz, maxz;
905 if (!cl_particles.integer) return;
906 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
907 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
908 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
909 if (dir[2] < 0) // falling
914 minz = z - fabs(dir[2]) * 0.1;
915 maxz = z + fabs(dir[2]) * 0.1;
916 minz = bound(mins[2], minz, maxs[2]);
917 maxz = bound(mins[2], maxz, maxs[2]);
919 count *= cl_particles_quality.value;
924 count *= 4; // ick, this should be in the mod or maps?
928 k = particlepalette[colorbase + (rand()&3)];
929 if (gamemode == GAME_GOODVSBAD2)
930 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);
932 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);
938 k = particlepalette[colorbase + (rand()&3)];
939 if (gamemode == GAME_GOODVSBAD2)
940 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);
942 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);
944 VectorCopy(p->vel, p->relativedirection);
948 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
952 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
957 if (!cl_particles.integer) return;
959 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
960 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
961 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
963 center[0] = (mins[0] + maxs[0]) * 0.5f;
964 center[1] = (mins[1] + maxs[1]) * 0.5f;
965 center[2] = (mins[2] + maxs[2]) * 0.5f;
967 count *= cl_particles_quality.value;
970 k = particlepalette[224 + (rand()&15)];
971 o[0] = lhrandom(mins[0], maxs[0]);
972 o[1] = lhrandom(mins[1], maxs[1]);
973 o[2] = lhrandom(mins[2], maxs[2]);
974 VectorSubtract(o, center, v);
976 VectorScale(v, 100, v);
977 v[2] += sv_gravity.value * 0.15f;
978 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);
982 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
986 if (!cl_particles.integer) return;
987 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
988 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
989 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
991 count *= cl_particles_quality.value;
994 k = particlepalette[224 + (rand()&15)];
995 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);
997 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);
1001 void CL_Flames (vec3_t org, vec3_t vel, int count)
1004 if (!cl_particles.integer) return;
1006 count *= cl_particles_quality.value;
1009 k = particlepalette[224 + (rand()&15)];
1010 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);
1022 void CL_LavaSplash (vec3_t origin)
1024 float i, j, inc, vel;
1027 if (!cl_particles.integer) return;
1029 inc = 32 / cl_particles_quality.value;
1030 for (i = -128;i < 128;i += inc)
1032 for (j = -128;j < 128;j += inc)
1034 dir[0] = j + lhrandom(0, 8);
1035 dir[1] = i + lhrandom(0, 8);
1037 org[0] = origin[0] + dir[0];
1038 org[1] = origin[1] + dir[1];
1039 org[2] = origin[2] + lhrandom(0, 64);
1040 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1041 if (gamemode == GAME_GOODVSBAD2)
1043 k = particlepalette[0 + (rand()&255)];
1044 l = particlepalette[0 + (rand()&255)];
1045 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);
1049 k = l = particlepalette[224 + (rand()&7)];
1050 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);
1062 void CL_TeleportSplash (vec3_t org)
1065 if (!cl_particles.integer) return;
1067 inc = 8 / cl_particles_quality.value;
1068 for (i = -16;i < 16;i += inc)
1069 for (j = -16;j < 16;j += inc)
1070 for (k = -24;k < 32;k += inc)
1071 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);
1074 #ifdef WORKINGLQUAKE
1075 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1077 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1080 vec3_t vec, dir, vel, pos;
1081 float len, dec, speed, qd;
1082 int smoke, blood, bubbles;
1083 #ifdef WORKINGLQUAKE
1087 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1090 VectorSubtract(end, start, dir);
1091 VectorNormalize(dir);
1093 VectorSubtract (end, start, vec);
1094 #ifdef WORKINGLQUAKE
1095 len = VectorNormalize (vec);
1097 speed = 1.0f / cl.frametime;
1098 VectorSubtract(end, start, vel);
1100 len = VectorNormalizeLength (vec);
1101 dec = -ent->persistent.trail_time;
1102 ent->persistent.trail_time += len;
1103 if (ent->persistent.trail_time < 0.01f)
1106 // if we skip out, leave it reset
1107 ent->persistent.trail_time = 0.0f;
1109 speed = ent->state_current.time - ent->state_previous.time;
1111 speed = 1.0f / speed;
1112 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1113 color = particlepalette[color];
1115 VectorScale(vel, speed, vel);
1117 // advance into this frame to reach the first puff location
1118 VectorMA(start, dec, vec, pos);
1121 smoke = cl_particles.integer && cl_particles_smoke.integer;
1122 blood = cl_particles.integer && cl_particles_blood.integer;
1123 #ifdef WORKINGLQUAKE
1124 contents = CL_PointQ1Contents(pos);
1125 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1127 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1129 qd = 1.0f / cl_particles_quality.value;
1135 case 0: // rocket trail
1139 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);
1140 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);
1143 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));
1146 case 1: // grenade trail
1147 // FIXME: make it gradually stop smoking
1150 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);
1155 case 4: // slight blood
1158 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);
1161 case 3: // green tracer
1165 if (gamemode == GAME_GOODVSBAD2)
1166 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);
1168 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);
1172 case 5: // flame tracer
1175 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);
1178 case 6: // voor trail
1182 if (gamemode == GAME_GOODVSBAD2)
1183 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);
1184 else if (gamemode == GAME_PRYDON)
1185 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);
1187 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);
1190 #ifndef WORKINGLQUAKE
1191 case 7: // Nehahra smoke tracer
1194 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);
1196 case 8: // Nexuiz plasma trail
1199 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);
1201 case 9: // glow trail
1204 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);
1208 Sys_Error("CL_RocketTrail: unknown trail type %i", type);
1211 // advance to next time and position
1213 VectorMA (pos, dec, vec, pos);
1215 #ifndef WORKINGLQUAKE
1216 ent->persistent.trail_time = len;
1220 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1222 int tempcolor2, cr, cg, cb;
1226 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1227 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);
1230 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1233 if (!cl_particles.integer) return;
1236 if (cl_particles_smoke.integer)
1237 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1238 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);
1241 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1244 if (!cl_particles.integer) return;
1246 if (cl_stainmaps.integer)
1247 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1248 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1251 if (cl_particles_smoke.integer)
1252 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1253 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);
1256 if (cl_particles_sparks.integer)
1257 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1258 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);
1266 void CL_MoveParticles (void)
1269 int i, maxparticle, j, a, content;
1270 float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
1274 // LordHavoc: early out condition
1275 if (!cl_numparticles)
1277 cl_freeparticle = 0;
1281 #ifdef WORKINGLQUAKE
1282 frametime = cl.frametime;
1284 frametime = cl.time - cl.oldtime;
1286 gravity = frametime * sv_gravity.value;
1287 dvel = 1+4*frametime;
1288 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1292 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1299 p->alpha -= p->alphafade * frametime;
1307 if (p->type->orientation != PARTICLE_BEAM)
1309 VectorCopy(p->org, oldorg);
1310 VectorMA(p->org, frametime, p->vel, p->org);
1311 VectorCopy(p->org, org);
1314 if (p->type == particletype + pt_rain)
1316 // raindrop - splash on solid/water/slime/lava
1317 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
1318 if (trace.fraction < 1)
1320 // convert from a raindrop particle to a rainsplash decal
1321 VectorCopy(trace.endpos, p->org);
1322 VectorCopy(trace.plane.normal, p->vel);
1323 VectorAdd(p->org, p->vel, p->org);
1324 p->type = particletype + pt_raindecal;
1325 p->texnum = tex_rainsplash[0];
1327 p->alphafade = p->alpha / 0.4;
1334 else if (p->type == particletype + pt_blood)
1336 // blood - splash on solid
1337 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
1338 if (trace.fraction < 1)
1340 // convert from a blood particle to a blood decal
1341 VectorCopy(trace.endpos, p->org);
1342 VectorCopy(trace.plane.normal, p->vel);
1343 VectorAdd(p->org, p->vel, p->org);
1344 #ifndef WORKINGLQUAKE
1345 if (cl_stainmaps.integer)
1346 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));
1348 if (!cl_decals.integer)
1354 p->type = particletype + pt_decal;
1355 p->texnum = tex_blooddecal[rand()&7];
1356 #ifndef WORKINGLQUAKE
1358 p->ownermodel = cl_entities[hitent].render.model;
1359 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1360 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1372 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
1373 if (trace.fraction < 1)
1375 VectorCopy(trace.endpos, p->org);
1383 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1384 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1385 if (DotProduct(p->vel, p->vel) < 0.03)
1386 VectorClear(p->vel);
1391 p->vel[2] -= p->gravity * gravity;
1395 f = p->friction * frametime;
1396 #ifdef WORKINGLQUAKE
1397 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1399 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1403 VectorScale(p->vel, f, p->vel);
1407 if (p->type != particletype + pt_static)
1409 switch (p->type - particletype)
1411 case pt_entityparticle:
1412 // particle that removes itself after one rendered frame
1419 #ifdef WORKINGLQUAKE
1420 a = CL_PointQ1Contents(p->org);
1421 if (a <= CONTENTS_WATER)
1423 a = CL_PointSuperContents(p->org);
1424 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1427 p->size += frametime * 8;
1428 //p->alpha -= bloodwaterfade;
1431 p->vel[2] -= gravity;
1432 #ifdef WORKINGLQUAKE
1433 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1435 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1440 #ifdef WORKINGLQUAKE
1441 a = CL_PointQ1Contents(p->org);
1442 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1444 a = CL_PointSuperContents(p->org);
1445 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1453 #ifdef WORKINGLQUAKE
1454 a = CL_PointQ1Contents(p->org);
1455 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1457 a = CL_PointSuperContents(p->org);
1458 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1463 if (cl.time > p->time2)
1466 p->time2 = cl.time + (rand() & 3) * 0.1;
1467 p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
1468 p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1469 //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
1471 #ifdef WORKINGLQUAKE
1472 a = CL_PointQ1Contents(p->org);
1473 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1475 a = CL_PointSuperContents(p->org);
1476 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1481 //p->size += frametime * 15;
1484 // FIXME: this has fairly wacky handling of alpha
1485 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1486 #ifndef WORKINGLQUAKE
1487 if (cl_entities[p->owner].render.model == p->ownermodel)
1489 Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1490 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1497 a = max(0, (cl.time - p->time2) * 40);
1499 p->texnum = tex_rainsplash[a];
1508 cl_numparticles = maxparticle + 1;
1509 cl_freeparticle = 0;
1512 #define MAX_PARTICLETEXTURES 64
1513 // particletexture_t is a rectangle in the particlefonttexture
1514 typedef struct particletexture_s
1516 rtexture_t *texture;
1517 float s1, t1, s2, t2;
1522 static int particlefonttexture;
1524 static rtexturepool_t *particletexturepool;
1525 static rtexture_t *particlefonttexture;
1527 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1529 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1531 #define PARTICLETEXTURESIZE 64
1532 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1534 static unsigned char shadebubble(float dx, float dy, vec3_t light)
1538 dz = 1 - (dx*dx+dy*dy);
1539 if (dz > 0) // it does hit the sphere
1543 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1544 VectorNormalize(normal);
1545 dot = DotProduct(normal, light);
1546 if (dot > 0.5) // interior reflection
1547 f += ((dot * 2) - 1);
1548 else if (dot < -0.5) // exterior reflection
1549 f += ((dot * -2) - 1);
1551 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1552 VectorNormalize(normal);
1553 dot = DotProduct(normal, light);
1554 if (dot > 0.5) // interior reflection
1555 f += ((dot * 2) - 1);
1556 else if (dot < -0.5) // exterior reflection
1557 f += ((dot * -2) - 1);
1559 f += 16; // just to give it a haze so you can see the outline
1560 f = bound(0, f, 255);
1561 return (unsigned char) f;
1567 static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
1569 int basex, basey, y;
1570 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1571 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1572 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1573 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1574 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1575 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1576 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1577 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1580 void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
1583 float cx, cy, dx, dy, f, iradius;
1585 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1586 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1587 iradius = 1.0f / radius;
1588 alpha *= (1.0f / 255.0f);
1589 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1591 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1595 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1598 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1599 d[0] += f * (red - d[0]);
1600 d[1] += f * (green - d[1]);
1601 d[2] += f * (blue - d[2]);
1607 void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1610 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1612 data[0] = bound(minr, data[0], maxr);
1613 data[1] = bound(ming, data[1], maxg);
1614 data[2] = bound(minb, data[2], maxb);
1618 void particletextureinvert(unsigned char *data)
1621 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1623 data[0] = 255 - data[0];
1624 data[1] = 255 - data[1];
1625 data[2] = 255 - data[2];
1629 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1630 static void R_InitBloodTextures (unsigned char *particletexturedata)
1633 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1636 for (i = 0;i < 8;i++)
1638 memset(&data[0][0][0], 255, sizeof(data));
1639 for (k = 0;k < 24;k++)
1640 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1641 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1642 particletextureinvert(&data[0][0][0]);
1643 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1647 for (i = 0;i < 8;i++)
1649 memset(&data[0][0][0], 255, sizeof(data));
1651 for (j = 1;j < 10;j++)
1652 for (k = min(j, m - 1);k < m;k++)
1653 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1654 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1655 particletextureinvert(&data[0][0][0]);
1656 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1661 static void R_InitParticleTexture (void)
1663 int x, y, d, i, k, m;
1664 float dx, dy, radius, f, f2;
1665 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1667 unsigned char *particletexturedata;
1669 // a note: decals need to modulate (multiply) the background color to
1670 // properly darken it (stain), and they need to be able to alpha fade,
1671 // this is a very difficult challenge because it means fading to white
1672 // (no change to background) rather than black (darkening everything
1673 // behind the whole decal polygon), and to accomplish this the texture is
1674 // inverted (dark red blood on white background becomes brilliant cyan
1675 // and white on black background) so we can alpha fade it to black, then
1676 // we invert it again during the blendfunc to make it work...
1678 particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1679 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1682 for (i = 0;i < 8;i++)
1684 memset(&data[0][0][0], 255, sizeof(data));
1687 unsigned char noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1689 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1690 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1692 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1694 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1695 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1697 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1698 d = (noise2[y][x] - 128) * 3 + 192;
1700 d = d * (1-(dx*dx+dy*dy));
1701 d = (d * noise1[y][x]) >> 7;
1702 d = bound(0, d, 255);
1703 data[y][x][3] = (unsigned char) d;
1710 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1714 for (i = 0;i < 16;i++)
1716 memset(&data[0][0][0], 255, sizeof(data));
1717 radius = i * 3.0f / 4.0f / 16.0f;
1718 f2 = 255.0f * ((15.0f - i) / 15.0f);
1719 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1721 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1722 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1724 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1725 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1726 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1729 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1733 memset(&data[0][0][0], 255, sizeof(data));
1734 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1736 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1737 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1739 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1740 d = 256 * (1 - (dx*dx+dy*dy));
1741 d = bound(0, d, 255);
1742 data[y][x][3] = (unsigned char) d;
1745 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1748 memset(&data[0][0][0], 255, sizeof(data));
1749 light[0] = 1;light[1] = 1;light[2] = 1;
1750 VectorNormalize(light);
1751 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1753 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1754 // stretch upper half of bubble by +50% and shrink lower half by -50%
1755 // (this gives an elongated teardrop shape)
1757 dy = (dy - 0.5f) * 2.0f;
1759 dy = (dy - 0.5f) / 1.5f;
1760 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1762 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1763 // shrink bubble width to half
1765 data[y][x][3] = shadebubble(dx, dy, light);
1768 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1771 memset(&data[0][0][0], 255, sizeof(data));
1772 light[0] = 1;light[1] = 1;light[2] = 1;
1773 VectorNormalize(light);
1774 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1776 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1777 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1779 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1780 data[y][x][3] = shadebubble(dx, dy, light);
1783 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1785 // Blood particles and blood decals
1786 R_InitBloodTextures (particletexturedata);
1789 for (i = 0;i < 8;i++)
1791 memset(&data[0][0][0], 255, sizeof(data));
1792 for (k = 0;k < 12;k++)
1793 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1794 for (k = 0;k < 3;k++)
1795 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1796 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1797 particletextureinvert(&data[0][0][0]);
1798 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1802 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1803 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1804 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1808 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1811 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1812 if (!particlefonttexture)
1813 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1814 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1815 particletexture[i].texture = particlefonttexture;
1818 fractalnoise(&noise3[0][0], 64, 4);
1820 for (y = 0;y < 64;y++)
1822 dy = (y - 0.5f*64) / (64*0.5f-1);
1823 for (x = 0;x < 16;x++)
1825 dx = (x - 0.5f*16) / (16*0.5f-2);
1826 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1827 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
1828 data2[y][x][3] = 255;
1833 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1836 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1837 if (!particletexture[tex_beam].texture)
1838 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1839 particletexture[tex_beam].s1 = 0;
1840 particletexture[tex_beam].t1 = 0;
1841 particletexture[tex_beam].s2 = 1;
1842 particletexture[tex_beam].t2 = 1;
1844 Mem_Free(particletexturedata);
1847 static void r_part_start(void)
1849 particletexturepool = R_AllocTexturePool();
1850 R_InitParticleTexture ();
1853 static void r_part_shutdown(void)
1855 R_FreeTexturePool(&particletexturepool);
1858 static void r_part_newmap(void)
1860 cl_numparticles = 0;
1861 cl_freeparticle = 0;
1864 void R_Particles_Init (void)
1866 Cvar_RegisterVariable(&r_drawparticles);
1867 #ifdef WORKINGLQUAKE
1870 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1874 #ifdef WORKINGLQUAKE
1875 void R_InitParticles(void)
1877 CL_Particles_Init();
1882 float particle_vertex3f[12], particle_texcoord2f[8];
1884 #ifdef WORKINGLQUAKE
1885 void R_DrawParticle(particle_t *p)
1888 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1890 const particle_t *p = (particle_t *)calldata1;
1894 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, cr, cg, cb, ca, size;
1895 particletexture_t *tex;
1897 VectorCopy(p->org, org);
1899 blendmode = p->type->blendmode;
1900 tex = &particletexture[p->texnum];
1901 cr = p->color[0] * (1.0f / 255.0f);
1902 cg = p->color[1] * (1.0f / 255.0f);
1903 cb = p->color[2] * (1.0f / 255.0f);
1904 ca = p->alpha * (1.0f / 255.0f);
1905 if (blendmode == PBLEND_MOD)
1915 #ifndef WORKINGLQUAKE
1916 if (p->type->lighting)
1918 float ambient[3], diffuse[3], diffusenormal[3];
1919 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
1920 cr *= (ambient[0] + 0.5 * diffuse[0]);
1921 cg *= (ambient[1] + 0.5 * diffuse[1]);
1922 cb *= (ambient[2] + 0.5 * diffuse[2]);
1926 fog = VERTEXFOGTABLE(VectorDistance(org, r_vieworigin));
1931 if (blendmode == PBLEND_ALPHA)
1933 cr += fogcolor[0] * fog;
1934 cg += fogcolor[1] * fog;
1935 cb += fogcolor[2] * fog;
1939 R_Mesh_Matrix(&r_identitymatrix);
1941 memset(&m, 0, sizeof(m));
1942 m.tex[0] = R_GetTexture(tex->texture);
1943 m.pointer_texcoord[0] = particle_texcoord2f;
1944 m.pointer_vertex = particle_vertex3f;
1947 GL_Color(cr, cg, cb, ca);
1949 if (blendmode == PBLEND_ALPHA)
1950 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1951 else if (blendmode == PBLEND_ADD)
1952 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1953 else //if (blendmode == PBLEND_MOD)
1954 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1955 GL_DepthMask(false);
1958 size = p->size * cl_particles_size.value;
1959 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1961 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1964 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1966 VectorNegate(p->vel, v);
1967 VectorVectors(v, right, up);
1970 VectorVectors(p->vel, right, up);
1971 VectorScale(right, size, right);
1972 VectorScale(up, size, up);
1976 VectorScale(r_viewleft, -size, right);
1977 VectorScale(r_viewup, size, up);
1979 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1980 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1981 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1982 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1983 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1984 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1985 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1986 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1987 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1988 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1989 particle_vertex3f[10] = org[1] + right[1] - up[1];
1990 particle_vertex3f[11] = org[2] + right[2] - up[2];
1991 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1992 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1993 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1994 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1996 else if (p->type->orientation == PARTICLE_SPARK)
1998 VectorMA(p->org, -0.02, p->vel, v);
1999 VectorMA(p->org, 0.02, p->vel, up2);
2000 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2001 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2002 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2003 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2004 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2006 else if (p->type->orientation == PARTICLE_BEAM)
2008 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2009 VectorSubtract(p->vel, p->org, up);
2010 VectorNormalize(up);
2011 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2012 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2013 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2014 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2015 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2016 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2020 Con_Printf("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++)
2077 renderstats.particles++;
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);