2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
25 #define NUMVERTEXNORMALS 162
26 siextern float r_avertexnormals[NUMVERTEXNORMALS][3];
27 #define m_bytenormals r_avertexnormals
28 #define VectorNormalizeFast VectorNormalize
29 #define CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
30 typedef unsigned char qbyte;
31 #define cl_stainmaps.integer 0
32 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
35 #define CL_EntityParticles R_EntityParticles
36 #define CL_ReadPointFile_f R_ReadPointFile_f
37 #define CL_ParseParticleEffect R_ParseParticleEffect
38 #define CL_ParticleExplosion R_ParticleExplosion
39 #define CL_ParticleExplosion2 R_ParticleExplosion2
40 #define CL_TeleportSplash R_TeleportSplash
41 #define CL_BlobExplosion R_BlobExplosion
42 #define CL_RunParticleEffect R_RunParticleEffect
43 #define CL_LavaSplash R_LavaSplash
44 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
46 vec3_t right1, right2, diff, normal;
48 VectorSubtract (org2, org1, normal);
49 VectorNormalizeFast (normal);
51 // calculate 'right' vector for start
52 VectorSubtract (r_vieworigin, org1, diff);
53 VectorNormalizeFast (diff);
54 CrossProduct (normal, diff, right1);
56 // calculate 'right' vector for end
57 VectorSubtract (r_vieworigin, org2, diff);
58 VectorNormalizeFast (diff);
59 CrossProduct (normal, diff, right2);
61 vert[ 0] = org1[0] + width * right1[0];
62 vert[ 1] = org1[1] + width * right1[1];
63 vert[ 2] = org1[2] + width * right1[2];
64 vert[ 3] = org1[0] - width * right1[0];
65 vert[ 4] = org1[1] - width * right1[1];
66 vert[ 5] = org1[2] - width * right1[2];
67 vert[ 6] = org2[0] - width * right2[0];
68 vert[ 7] = org2[1] - width * right2[1];
69 vert[ 8] = org2[2] - width * right2[2];
70 vert[ 9] = org2[0] + width * right2[0];
71 vert[10] = org2[1] + width * right2[1];
72 vert[11] = org2[2] + width * right2[2];
74 void fractalnoise(qbyte *noise, int size, int startgrid)
76 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
78 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
80 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
81 if (size != (1 << sizepower))
82 Sys_Error("fractalnoise: size must be power of 2\n");
84 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
85 if (startgrid != (1 << gridpower))
86 Sys_Error("fractalnoise: grid must be power of 2\n");
88 startgrid = bound(0, startgrid, size);
90 amplitude = 0xFFFF; // this gets halved before use
91 noisebuf = malloc(size*size*sizeof(int));
92 memset(noisebuf, 0, size*size*sizeof(int));
94 for (g2 = startgrid;g2;g2 >>= 1)
96 // brownian motion (at every smaller level there is random behavior)
98 for (y = 0;y < size;y += g2)
99 for (x = 0;x < size;x += g2)
100 n(x,y) += (rand()&litude);
105 // subdivide, diamond-square algorithm (really this has little to do with squares)
107 for (y = 0;y < size;y += g2)
108 for (x = 0;x < size;x += g2)
109 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
111 for (y = 0;y < size;y += g2)
112 for (x = 0;x < size;x += g2)
114 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
115 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
119 // find range of noise values
121 for (y = 0;y < size;y++)
122 for (x = 0;x < size;x++)
124 if (n(x,y) < min) min = n(x,y);
125 if (n(x,y) > max) max = n(x,y);
129 // normalize noise and copy to output
130 for (y = 0;y < size;y++)
131 for (x = 0;x < size;x++)
132 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
136 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
140 right[0] = forward[2];
141 right[1] = -forward[0];
142 right[2] = forward[1];
144 d = DotProduct(forward, right);
145 right[0] -= d * forward[0];
146 right[1] -= d * forward[1];
147 right[2] -= d * forward[2];
148 VectorNormalizeFast(right);
149 CrossProduct(right, forward, up);
153 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
155 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int hitbmodels, int *hitent, int hitsupercontentsmask)
162 memset (&trace, 0, sizeof(trace));
164 VectorCopy (end, trace.endpos);
166 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
168 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
170 VectorCopy(trace.endpos, impact);
171 VectorCopy(trace.plane.normal, normal);
172 return trace.fraction;
175 #include "cl_collision.h"
179 #define MAX_PARTICLES 32768 // default max # of particles at one time
180 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
184 PARTICLE_BILLBOARD = 0,
186 PARTICLE_ORIENTED_DOUBLESIDED = 2,
199 typedef struct particletype_s
202 porientation_t orientation;
209 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
213 // must match ptype_t values
214 particletype_t particletype[pt_total] =
216 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
217 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
218 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
219 {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
220 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
221 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
222 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
223 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
224 {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
225 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
226 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
227 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
230 typedef struct particle_s
232 particletype_t *type;
235 vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam
237 float alpha; // 0-255
238 float alphafade; // how much alpha reduces per second
239 float time2; // used for snow fluttering and decal fade
240 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)
241 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
242 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
244 #ifndef WORKINGLQUAKE
245 unsigned short owner; // decal stuck to this entity
246 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
247 vec3_t relativeorigin; // decal at this location in entity's coordinate space
248 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
253 static int particlepalette[256] =
255 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
256 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
257 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
258 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
259 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
260 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
261 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
262 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
263 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
264 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
265 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
266 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
267 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
268 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
269 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
270 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
271 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
272 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
273 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
274 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
275 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
276 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
277 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
278 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
279 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
280 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
281 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
282 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
283 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
284 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
285 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
286 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
289 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
291 // texture numbers in particle font
292 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
293 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
294 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
295 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
296 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
297 static const int tex_particle = 63;
298 static const int tex_bubble = 62;
299 static const int tex_raindrop = 61;
300 static const int tex_beam = 60;
302 static int cl_maxparticles;
303 static int cl_numparticles;
304 static int cl_freeparticle;
305 static particle_t *particles;
307 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
308 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
309 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
310 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
311 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
312 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
313 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
314 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
315 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
316 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
317 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
318 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
319 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
320 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
321 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
322 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
323 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
324 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
325 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
326 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
328 void CL_Particles_Clear(void)
332 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
340 void CL_ReadPointFile_f (void);
341 void CL_Particles_Init (void)
345 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
346 i = COM_CheckParm ("-particles");
348 if (i && i < com_argc - 1)
350 cl_maxparticles = (int)(atoi(com_argv[i+1]));
351 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
352 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
355 cl_maxparticles = MAX_PARTICLES;
357 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
359 Cvar_RegisterVariable (&cl_particles);
360 Cvar_RegisterVariable (&cl_particles_quality);
361 Cvar_RegisterVariable (&cl_particles_size);
362 Cvar_RegisterVariable (&cl_particles_bloodshowers);
363 Cvar_RegisterVariable (&cl_particles_blood);
364 Cvar_RegisterVariable (&cl_particles_blood_alpha);
365 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
366 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
367 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
368 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
369 Cvar_RegisterVariable (&cl_particles_explosions_shell);
370 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
371 Cvar_RegisterVariable (&cl_particles_smoke);
372 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
373 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
374 Cvar_RegisterVariable (&cl_particles_sparks);
375 Cvar_RegisterVariable (&cl_particles_bubbles);
376 Cvar_RegisterVariable (&cl_decals);
377 Cvar_RegisterVariable (&cl_decals_time);
378 Cvar_RegisterVariable (&cl_decals_fadetime);
381 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
383 particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
385 CL_Particles_Clear();
388 void CL_Particles_Shutdown (void)
391 // No clue what to do here...
395 // list of all 26 parameters:
396 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
397 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
398 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
399 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
400 // palpha - opacity of particle as 0-255 (can be more than 255)
401 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
402 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
403 // pgravity - how much effect gravity has on the particle (0-1)
404 // 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
405 // px,py,pz - starting origin of particle
406 // pvx,pvy,pvz - starting velocity of particle
407 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
408 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)
411 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
412 ptempcolor = (pcolor1);
413 ptempcolor2 = (pcolor2);
414 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
415 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
416 pcb2 = (ptempcolor2) & 0xFF;
417 if (ptempcolor != ptempcolor2)
419 pcr1 = ((ptempcolor) >> 16) & 0xFF;
420 pcg1 = ((ptempcolor) >> 8) & 0xFF;
421 pcb1 = (ptempcolor) & 0xFF;
422 ptempcolor = rand() & 0xFF;
423 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
424 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
425 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
427 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
428 if (cl_freeparticle >= cl_maxparticles)
430 part = &particles[cl_freeparticle++];
431 if (cl_numparticles < cl_freeparticle)
432 cl_numparticles = cl_freeparticle;
433 memset(part, 0, sizeof(*part));
434 part->type = (ptype);
435 part->color[0] = pcr2;
436 part->color[1] = pcg2;
437 part->color[2] = pcb2;
438 part->color[3] = 0xFF;
440 part->size = (psize);
441 part->alpha = (palpha);
442 part->alphafade = (palphafade);
443 part->gravity = (pgravity);
444 part->bounce = (pbounce);
448 part->vel[0] = (pvx);
449 part->vel[1] = (pvy);
450 part->vel[2] = (pvz);
452 part->friction = (pfriction);
456 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
459 if (!cl_decals.integer)
461 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);
465 #ifndef WORKINGLQUAKE
467 p->ownermodel = cl_entities[p->owner].render.model;
468 Matrix4x4_Transform(&cl_entities[p->owner].render.inversematrix, org, p->relativeorigin);
469 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.inversematrix, normal, p->relativedirection);
470 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
475 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
478 float bestfrac, bestorg[3], bestnormal[3];
479 float frac, v[3], normal[3], org2[3];
480 int besthitent = 0, hitent;
482 for (i = 0;i < 32;i++)
485 VectorMA(org, maxdist, org2, org2);
486 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
491 VectorCopy(v, bestorg);
492 VectorCopy(normal, bestnormal);
496 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
504 void CL_EntityParticles (entity_t *ent)
508 float sp, sy, cp, cy;
512 static vec3_t avelocities[NUMVERTEXNORMALS];
513 if (!cl_particles.integer) return;
518 if (!avelocities[0][0])
519 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
520 avelocities[0][i] = (rand()&255) * 0.01;
522 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
524 angle = cl.time * avelocities[i][0];
527 angle = cl.time * avelocities[i][1];
536 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);
538 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);
544 void CL_ReadPointFile_f (void)
548 char *pointfile = NULL, *pointfilepos, *t, tchar;
549 char name[MAX_OSPATH];
554 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
555 strlcat (name, ".pts", sizeof (name));
557 pointfile = COM_LoadTempFile (name);
559 pointfile = FS_LoadFile(name, tempmempool, true);
563 Con_Printf("Could not open %s\n", name);
567 Con_Printf("Reading %s...\n", name);
570 pointfilepos = pointfile;
571 while (*pointfilepos)
573 while (*pointfilepos == '\n' || *pointfilepos == '\r')
578 while (*t && *t != '\n' && *t != '\r')
582 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
588 VectorCopy(org, leakorg);
591 if (cl_numparticles < cl_maxparticles - 3)
594 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);
597 #ifndef WORKINGLQUAKE
600 VectorCopy(leakorg, org);
601 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
603 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);
604 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);
605 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);
610 CL_ParseParticleEffect
612 Parse an effect out of the server message
615 void CL_ParseParticleEffect (void)
618 int i, count, msgcount, color;
620 MSG_ReadVector(org, cl.protocol);
621 for (i=0 ; i<3 ; i++)
622 dir[i] = MSG_ReadChar () * (1.0/16);
623 msgcount = MSG_ReadByte ();
624 color = MSG_ReadByte ();
631 if (cl_particles_blood_bloodhack.integer)
636 CL_BloodPuff(org, dir, count / 2);
642 CL_BloodPuff(org, dir, count / 2);
646 CL_RunParticleEffect (org, dir, color, count);
655 void CL_ParticleExplosion (vec3_t org)
660 if (cl_stainmaps.integer)
661 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
662 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
664 i = CL_PointSuperContents(org);
665 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
667 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
668 for (i = 0;i < 128 * cl_particles_quality.value;i++)
669 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, (1.0f / cl_particles_quality.value) * lhrandom(128, 255), (1.0f / cl_particles_quality.value) * 128, -0.125, 1.5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-96, 96), lhrandom(-96, 96), lhrandom(-96, 96), (1.0 / 16.0));
673 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
675 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
677 for (i = 0;i < 32;i++)
682 v2[0] = lhrandom(-48, 48);
683 v2[1] = lhrandom(-48, 48);
684 v2[2] = lhrandom(-48, 48);
686 for (k = 0;k < 16;k++)
688 v[0] = org[0] + lhrandom(-48, 48);
689 v[1] = org[1] + lhrandom(-48, 48);
690 v[2] = org[2] + lhrandom(-48, 48);
691 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
694 VectorSubtract(v2, org, v2);
696 VectorScale(v2, 2.0f, v2);
697 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);
701 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
702 for (i = 0;i < 128 * cl_particles_quality.value;i++)
703 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);
706 if (cl_particles_explosions_shell.integer)
712 CL_ParticleExplosion2
716 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
722 if (!cl_particles.integer) return;
724 for (i = 0;i < 512 * cl_particles_quality.value;i++)
726 VectorRandom (offset);
727 VectorScale (offset, 192, vel);
728 VectorScale (offset, 8, offset);
729 k = particlepalette[colorStart + (i % colorLength)];
730 pscale = lhrandom(0.5, 1.5);
731 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));
741 void CL_BlobExplosion (vec3_t org)
743 CL_ParticleExplosion(org);
752 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
758 CL_ParticleExplosion(org);
761 if (!cl_particles.integer) return;
762 count *= cl_particles_quality.value;
765 k = particlepalette[color + (rand()&7)];
766 if (gamemode == GAME_GOODVSBAD2)
767 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);
769 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);
773 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
779 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
783 if (!cl_particles.integer) return;
785 if (cl_particles_sparks.integer)
788 count *= cl_particles_quality.value;
791 k = particlepalette[0x68 + (rand() & 7)];
792 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);
797 void CL_Smoke (vec3_t org, vec3_t dir, int count)
802 if (!cl_particles.integer) return;
805 if (cl_particles_smoke.integer)
807 k = count * 0.25 * cl_particles_quality.value;
810 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
811 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
812 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
813 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
814 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, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
819 void CL_BulletMark (vec3_t org)
821 if (cl_stainmaps.integer)
822 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
823 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
826 void CL_PlasmaBurn (vec3_t org)
828 if (cl_stainmaps.integer)
829 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
830 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
833 static float bloodcount = 0;
834 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 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
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, org3[0], org3[1], org3[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;
908 if (!cl_particles.integer) return;
909 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
910 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
911 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
912 if (dir[2] < 0) // falling
917 minz = z - fabs(dir[2]) * 0.1;
918 maxz = z + fabs(dir[2]) * 0.1;
919 minz = bound(mins[2], minz, maxs[2]);
920 maxz = bound(mins[2], maxz, maxs[2]);
922 count *= cl_particles_quality.value;
927 count *= 4; // ick, this should be in the mod or maps?
931 k = particlepalette[colorbase + (rand()&3)];
932 if (gamemode == GAME_GOODVSBAD2)
933 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);
935 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);
941 k = particlepalette[colorbase + (rand()&3)];
942 if (gamemode == GAME_GOODVSBAD2)
943 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);
945 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);
949 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
953 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
958 if (!cl_particles.integer) return;
960 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
961 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
962 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
964 center[0] = (mins[0] + maxs[0]) * 0.5f;
965 center[1] = (mins[1] + maxs[1]) * 0.5f;
966 center[2] = (mins[2] + maxs[2]) * 0.5f;
968 count *= cl_particles_quality.value;
971 k = particlepalette[224 + (rand()&15)];
972 o[0] = lhrandom(mins[0], maxs[0]);
973 o[1] = lhrandom(mins[1], maxs[1]);
974 o[2] = lhrandom(mins[2], maxs[2]);
975 VectorSubtract(o, center, v);
976 VectorNormalizeFast(v);
977 VectorScale(v, 100, v);
978 v[2] += sv_gravity.value * 0.15f;
979 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);
983 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
987 if (!cl_particles.integer) return;
988 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
989 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
990 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
992 count *= cl_particles_quality.value;
995 k = particlepalette[224 + (rand()&15)];
996 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);
998 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);
1002 void CL_Flames (vec3_t org, vec3_t vel, int count)
1005 if (!cl_particles.integer) return;
1007 count *= cl_particles_quality.value;
1010 k = particlepalette[224 + (rand()&15)];
1011 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);
1023 void CL_LavaSplash (vec3_t origin)
1025 float i, j, inc, vel;
1028 if (!cl_particles.integer) return;
1030 inc = 32 / cl_particles_quality.value;
1031 for (i = -128;i < 128;i += inc)
1033 for (j = -128;j < 128;j += inc)
1035 dir[0] = j + lhrandom(0, 8);
1036 dir[1] = i + lhrandom(0, 8);
1038 org[0] = origin[0] + dir[0];
1039 org[1] = origin[1] + dir[1];
1040 org[2] = origin[2] + lhrandom(0, 64);
1041 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1042 if (gamemode == GAME_GOODVSBAD2)
1044 k = particlepalette[0 + (rand()&255)];
1045 l = particlepalette[0 + (rand()&255)];
1046 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);
1050 k = l = particlepalette[224 + (rand()&7)];
1051 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);
1063 void CL_TeleportSplash (vec3_t org)
1066 if (!cl_particles.integer) return;
1068 inc = 8 / cl_particles_quality.value;
1069 for (i = -16;i < 16;i += inc)
1070 for (j = -16;j < 16;j += inc)
1071 for (k = -24;k < 32;k += inc)
1072 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);
1075 #ifdef WORKINGLQUAKE
1076 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1078 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1081 vec3_t vec, dir, vel, pos;
1082 float len, dec, speed, qd;
1083 int smoke, blood, bubbles;
1084 #ifdef WORKINGLQUAKE
1088 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1091 VectorSubtract(end, start, dir);
1092 VectorNormalize(dir);
1094 VectorSubtract (end, start, vec);
1095 #ifdef WORKINGLQUAKE
1096 len = VectorNormalize (vec);
1098 speed = 1.0f / cl.frametime;
1099 VectorSubtract(end, start, vel);
1101 len = VectorNormalizeLength (vec);
1102 dec = -ent->persistent.trail_time;
1103 ent->persistent.trail_time += len;
1104 if (ent->persistent.trail_time < 0.01f)
1107 // if we skip out, leave it reset
1108 ent->persistent.trail_time = 0.0f;
1110 speed = ent->state_current.time - ent->state_previous.time;
1112 speed = 1.0f / speed;
1113 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1114 color = particlepalette[color];
1116 VectorScale(vel, speed, vel);
1118 // advance into this frame to reach the first puff location
1119 VectorMA(start, dec, vec, pos);
1122 smoke = cl_particles.integer && cl_particles_smoke.integer;
1123 blood = cl_particles.integer && cl_particles_blood.integer;
1124 #ifdef WORKINGLQUAKE
1125 contents = CL_PointQ1Contents(pos);
1126 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1128 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1130 qd = 1.0f / cl_particles_quality.value;
1136 case 0: // rocket trail
1140 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);
1141 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);
1144 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));
1147 case 1: // grenade trail
1148 // FIXME: make it gradually stop smoking
1151 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);
1156 case 4: // slight blood
1159 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);
1162 case 3: // green tracer
1166 if (gamemode == GAME_GOODVSBAD2)
1167 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);
1169 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);
1173 case 5: // flame tracer
1176 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);
1179 case 6: // voor trail
1183 if (gamemode == GAME_GOODVSBAD2)
1184 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);
1185 else if (gamemode == GAME_PRYDON)
1186 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);
1188 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);
1191 #ifndef WORKINGLQUAKE
1192 case 7: // Nehahra smoke tracer
1195 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);
1197 case 8: // Nexuiz plasma trail
1200 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);
1202 case 9: // glow trail
1205 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);
1210 // advance to next time and position
1212 VectorMA (pos, dec, vec, pos);
1214 #ifndef WORKINGLQUAKE
1215 ent->persistent.trail_time = len;
1219 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1221 int tempcolor2, cr, cg, cb;
1225 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1226 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);
1229 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1232 if (!cl_particles.integer) return;
1235 if (cl_particles_smoke.integer)
1236 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1237 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);
1240 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1243 if (!cl_particles.integer) return;
1245 if (cl_stainmaps.integer)
1246 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1247 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1250 if (cl_particles_smoke.integer)
1251 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1252 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);
1255 if (cl_particles_sparks.integer)
1256 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1257 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);
1265 void CL_MoveParticles (void)
1268 int i, maxparticle, j, a, content;
1269 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3], oldorg[3];
1272 // LordHavoc: early out condition
1273 if (!cl_numparticles)
1275 cl_freeparticle = 0;
1279 #ifdef WORKINGLQUAKE
1280 frametime = cl.frametime;
1282 frametime = cl.time - cl.oldtime;
1284 gravity = frametime * sv_gravity.value;
1285 dvel = 1+4*frametime;
1286 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1290 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1297 p->alpha -= p->alphafade * frametime;
1305 if (p->type->orientation != PARTICLE_BEAM)
1307 VectorCopy(p->org, oldorg);
1308 VectorMA(p->org, frametime, p->vel, p->org);
1309 VectorCopy(p->org, org);
1312 if (p->type == particletype + pt_rain)
1314 // raindrop - splash on solid/water/slime/lava
1315 if (CL_TraceLine(oldorg, p->org, v, normal, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK) < 1)
1317 VectorCopy(v, p->org);
1319 p->type = particletype + pt_raindecal;
1320 // convert from a raindrop particle to a rainsplash decal
1321 p->texnum = tex_rainsplash[0];
1323 p->alphafade = p->alpha / 0.4;
1324 VectorCopy(normal, p->vel);
1325 VectorAdd(p->org, normal, p->org);
1332 else if (p->type == particletype + pt_blood)
1334 // blood - splash on solid
1335 if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1337 VectorCopy(v, p->org);
1338 #ifndef WORKINGLQUAKE
1339 if (cl_stainmaps.integer)
1340 R_Stain(v, 32, 32, 16, 16, p->alpha * p->size * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->size * (1.0f / 40.0f));
1342 if (!cl_decals.integer)
1348 p->type = particletype + pt_decal;
1349 // convert from a blood particle to a blood decal
1350 p->texnum = tex_blooddecal[rand()&7];
1351 #ifndef WORKINGLQUAKE
1353 p->ownermodel = cl_entities[hitent].render.model;
1354 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, v, p->relativeorigin);
1355 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, normal, p->relativedirection);
1356 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1360 VectorCopy(normal, p->vel);
1361 VectorAdd(p->org, normal, p->org);
1370 if (CL_TraceLine(oldorg, p->org, v, normal, true, NULL, SUPERCONTENTS_SOLID) < 1)
1372 VectorCopy(v, p->org);
1380 dist = DotProduct(p->vel, normal) * -p->bounce;
1381 VectorMA(p->vel, dist, normal, p->vel);
1382 if (DotProduct(p->vel, p->vel) < 0.03)
1383 VectorClear(p->vel);
1388 p->vel[2] -= p->gravity * gravity;
1392 f = p->friction * frametime;
1393 #ifdef WORKINGLQUAKE
1394 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1396 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1400 VectorScale(p->vel, f, p->vel);
1404 if (p->type != particletype + pt_static)
1406 switch (p->type - particletype)
1408 case pt_entityparticle:
1409 // particle that removes itself after one rendered frame
1416 #ifdef WORKINGLQUAKE
1417 a = CL_PointQ1Contents(p->org);
1418 if (a <= CONTENTS_WATER)
1420 a = CL_PointSuperContents(p->org);
1421 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1424 p->size += frametime * 8;
1425 //p->alpha -= bloodwaterfade;
1428 p->vel[2] -= gravity;
1429 #ifdef WORKINGLQUAKE
1430 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1432 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1437 #ifdef WORKINGLQUAKE
1438 a = CL_PointQ1Contents(p->org);
1439 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1441 a = CL_PointSuperContents(p->org);
1442 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1450 #ifdef WORKINGLQUAKE
1451 a = CL_PointQ1Contents(p->org);
1452 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1454 a = CL_PointSuperContents(p->org);
1455 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1460 if (cl.time > p->time2)
1463 p->time2 = cl.time + (rand() & 3) * 0.1;
1464 p->vel[0] += lhrandom(-32, 32);
1465 p->vel[1] += lhrandom(-32, 32);
1466 p->vel[2] += lhrandom(-32, 32);
1468 #ifdef WORKINGLQUAKE
1469 a = CL_PointQ1Contents(p->org);
1470 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1472 a = CL_PointSuperContents(p->org);
1473 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1478 //p->size += frametime * 15;
1481 // FIXME: this has fairly wacky handling of alpha
1482 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1483 #ifndef WORKINGLQUAKE
1484 if (cl_entities[p->owner].render.model == p->ownermodel)
1486 Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1487 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1494 a = max(0, (cl.time - p->time2) * 40);
1496 p->texnum = tex_rainsplash[a];
1505 cl_numparticles = maxparticle + 1;
1506 cl_freeparticle = 0;
1509 #define MAX_PARTICLETEXTURES 64
1510 // particletexture_t is a rectangle in the particlefonttexture
1513 rtexture_t *texture;
1514 float s1, t1, s2, t2;
1519 static int particlefonttexture;
1521 static rtexturepool_t *particletexturepool;
1522 static rtexture_t *particlefonttexture;
1524 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1526 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1528 #define PARTICLETEXTURESIZE 64
1529 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1531 static qbyte shadebubble(float dx, float dy, vec3_t light)
1535 dz = 1 - (dx*dx+dy*dy);
1536 if (dz > 0) // it does hit the sphere
1540 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1541 VectorNormalize(normal);
1542 dot = DotProduct(normal, light);
1543 if (dot > 0.5) // interior reflection
1544 f += ((dot * 2) - 1);
1545 else if (dot < -0.5) // exterior reflection
1546 f += ((dot * -2) - 1);
1548 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1549 VectorNormalize(normal);
1550 dot = DotProduct(normal, light);
1551 if (dot > 0.5) // interior reflection
1552 f += ((dot * 2) - 1);
1553 else if (dot < -0.5) // exterior reflection
1554 f += ((dot * -2) - 1);
1556 f += 16; // just to give it a haze so you can see the outline
1557 f = bound(0, f, 255);
1564 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1566 int basex, basey, y;
1567 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1568 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1569 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1570 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1571 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1572 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1573 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1574 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1577 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1580 float cx, cy, dx, dy, f, iradius;
1582 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1583 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1584 iradius = 1.0f / radius;
1585 alpha *= (1.0f / 255.0f);
1586 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1588 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1592 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1595 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1596 d[0] += f * (red - d[0]);
1597 d[1] += f * (green - d[1]);
1598 d[2] += f * (blue - d[2]);
1604 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1607 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1609 data[0] = bound(minr, data[0], maxr);
1610 data[1] = bound(ming, data[1], maxg);
1611 data[2] = bound(minb, data[2], maxb);
1615 void particletextureinvert(qbyte *data)
1618 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1620 data[0] = 255 - data[0];
1621 data[1] = 255 - data[1];
1622 data[2] = 255 - data[2];
1626 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1627 static void R_InitBloodTextures (qbyte *particletexturedata)
1630 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1633 for (i = 0;i < 8;i++)
1635 memset(&data[0][0][0], 255, sizeof(data));
1636 for (k = 0;k < 24;k++)
1637 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1638 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1639 particletextureinvert(&data[0][0][0]);
1640 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1644 for (i = 0;i < 8;i++)
1646 memset(&data[0][0][0], 255, sizeof(data));
1648 for (j = 1;j < 10;j++)
1649 for (k = min(j, m - 1);k < m;k++)
1650 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1651 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1652 particletextureinvert(&data[0][0][0]);
1653 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1658 static void R_InitParticleTexture (void)
1660 int x, y, d, i, k, m;
1661 float dx, dy, radius, f, f2;
1662 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1664 qbyte *particletexturedata;
1666 // a note: decals need to modulate (multiply) the background color to
1667 // properly darken it (stain), and they need to be able to alpha fade,
1668 // this is a very difficult challenge because it means fading to white
1669 // (no change to background) rather than black (darkening everything
1670 // behind the whole decal polygon), and to accomplish this the texture is
1671 // inverted (dark red blood on white background becomes brilliant cyan
1672 // and white on black background) so we can alpha fade it to black, then
1673 // we invert it again during the blendfunc to make it work...
1675 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1676 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1679 for (i = 0;i < 8;i++)
1681 memset(&data[0][0][0], 255, sizeof(data));
1684 qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1686 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1687 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1689 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1691 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1692 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1694 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1695 d = (noise2[y][x] - 128) * 3 + 192;
1697 d = d * (1-(dx*dx+dy*dy));
1698 d = (d * noise1[y][x]) >> 7;
1699 d = bound(0, d, 255);
1700 data[y][x][3] = (qbyte) d;
1707 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1711 for (i = 0;i < 16;i++)
1713 memset(&data[0][0][0], 255, sizeof(data));
1714 radius = i * 3.0f / 4.0f / 16.0f;
1715 f2 = 255.0f * ((15.0f - i) / 15.0f);
1716 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1718 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1719 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1721 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1722 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1723 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1726 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1730 memset(&data[0][0][0], 255, sizeof(data));
1731 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1733 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1734 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1736 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1737 d = 256 * (1 - (dx*dx+dy*dy));
1738 d = bound(0, d, 255);
1739 data[y][x][3] = (qbyte) d;
1742 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1745 memset(&data[0][0][0], 255, sizeof(data));
1746 light[0] = 1;light[1] = 1;light[2] = 1;
1747 VectorNormalize(light);
1748 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1750 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1751 // stretch upper half of bubble by +50% and shrink lower half by -50%
1752 // (this gives an elongated teardrop shape)
1754 dy = (dy - 0.5f) * 2.0f;
1756 dy = (dy - 0.5f) / 1.5f;
1757 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1759 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1760 // shrink bubble width to half
1762 data[y][x][3] = shadebubble(dx, dy, light);
1765 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1768 memset(&data[0][0][0], 255, sizeof(data));
1769 light[0] = 1;light[1] = 1;light[2] = 1;
1770 VectorNormalize(light);
1771 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1773 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1774 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1776 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1777 data[y][x][3] = shadebubble(dx, dy, light);
1780 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1782 // Blood particles and blood decals
1783 R_InitBloodTextures (particletexturedata);
1786 for (i = 0;i < 8;i++)
1788 memset(&data[0][0][0], 255, sizeof(data));
1789 for (k = 0;k < 12;k++)
1790 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1791 for (k = 0;k < 3;k++)
1792 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1793 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1794 particletextureinvert(&data[0][0][0]);
1795 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1799 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1800 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1801 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1805 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1808 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1809 if (!particlefonttexture)
1810 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1811 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1812 particletexture[i].texture = particlefonttexture;
1815 fractalnoise(&noise3[0][0], 64, 4);
1817 for (y = 0;y < 64;y++)
1819 dy = (y - 0.5f*64) / (64*0.5f-1);
1820 for (x = 0;x < 16;x++)
1822 dx = (x - 0.5f*16) / (16*0.5f-2);
1823 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1824 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1825 data2[y][x][3] = 255;
1830 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1833 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1834 if (!particletexture[tex_beam].texture)
1835 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1836 particletexture[tex_beam].s1 = 0;
1837 particletexture[tex_beam].t1 = 0;
1838 particletexture[tex_beam].s2 = 1;
1839 particletexture[tex_beam].t2 = 1;
1841 Mem_Free(particletexturedata);
1844 static void r_part_start(void)
1846 particletexturepool = R_AllocTexturePool();
1847 R_InitParticleTexture ();
1850 static void r_part_shutdown(void)
1852 R_FreeTexturePool(&particletexturepool);
1855 static void r_part_newmap(void)
1857 cl_numparticles = 0;
1858 cl_freeparticle = 0;
1861 void R_Particles_Init (void)
1863 Cvar_RegisterVariable(&r_drawparticles);
1864 #ifdef WORKINGLQUAKE
1867 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1871 #ifdef WORKINGLQUAKE
1872 void R_InitParticles(void)
1874 CL_Particles_Init();
1879 float particle_vertex3f[12], particle_texcoord2f[8];
1881 #ifdef WORKINGLQUAKE
1882 void R_DrawParticle(particle_t *p)
1885 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1887 const particle_t *p = calldata1;
1891 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, size;
1892 particletexture_t *tex;
1894 VectorCopy(p->org, org);
1896 blendmode = p->type->blendmode;
1897 tex = &particletexture[p->texnum];
1898 cr = p->color[0] * (1.0f / 255.0f);
1899 cg = p->color[1] * (1.0f / 255.0f);
1900 cb = p->color[2] * (1.0f / 255.0f);
1901 ca = p->alpha * (1.0f / 255.0f);
1902 if (blendmode == PBLEND_MOD)
1912 #ifndef WORKINGLQUAKE
1913 if (p->type->lighting)
1915 float ambient[3], diffuse[3], diffusenormal[3];
1916 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
1917 cr *= (ambient[0] + 0.5 * diffuse[0]);
1918 cg *= (ambient[1] + 0.5 * diffuse[1]);
1919 cb *= (ambient[2] + 0.5 * diffuse[2]);
1923 VectorSubtract(org, r_vieworigin, fogvec);
1924 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1929 if (blendmode == PBLEND_ALPHA)
1931 cr += fogcolor[0] * fog;
1932 cg += fogcolor[1] * fog;
1933 cb += fogcolor[2] * fog;
1937 R_Mesh_Matrix(&r_identitymatrix);
1939 memset(&m, 0, sizeof(m));
1940 m.tex[0] = R_GetTexture(tex->texture);
1941 m.pointer_texcoord[0] = particle_texcoord2f;
1942 m.pointer_vertex = particle_vertex3f;
1945 GL_Color(cr, cg, cb, ca);
1947 if (blendmode == PBLEND_ALPHA)
1948 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1949 else if (blendmode == PBLEND_ADD)
1950 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1951 else //if (blendmode == PBLEND_MOD)
1952 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1953 GL_DepthMask(false);
1956 size = p->size * cl_particles_size.value;
1957 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1959 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1962 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1964 VectorNegate(p->vel, v);
1965 VectorVectors(v, right, up);
1968 VectorVectors(p->vel, right, up);
1969 VectorScale(right, size, right);
1970 VectorScale(up, size, up);
1974 VectorScale(r_viewleft, -size, right);
1975 VectorScale(r_viewup, size, up);
1977 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1978 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1979 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1980 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1981 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1982 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1983 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1984 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1985 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1986 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1987 particle_vertex3f[10] = org[1] + right[1] - up[1];
1988 particle_vertex3f[11] = org[2] + right[2] - up[2];
1989 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1990 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1991 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1992 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1994 else if (p->type->orientation == PARTICLE_SPARK)
1996 VectorMA(p->org, -0.02, p->vel, v);
1997 VectorMA(p->org, 0.02, p->vel, up2);
1998 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
1999 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2000 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2001 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2002 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2004 else if (p->type->orientation == PARTICLE_BEAM)
2006 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2007 VectorSubtract(p->vel, p->org, up);
2008 VectorNormalizeFast(up);
2009 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2010 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2011 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2012 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2013 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2014 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2017 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2020 if (blendmode == PBLEND_ALPHA)
2021 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2022 else if (blendmode == PBLEND_ADD)
2023 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2024 else //if (blendmode == PBLEND_MOD)
2025 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2026 glColor4f(cr, cg, cb, ca);
2028 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2029 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2030 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2031 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2034 R_Mesh_Draw(0, 4, 2, polygonelements);
2038 void R_DrawParticles (void)
2041 float minparticledist;
2044 #ifdef WORKINGLQUAKE
2048 // LordHavoc: early out conditions
2049 if ((!cl_numparticles) || (!r_drawparticles.integer))
2052 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2054 #ifdef WORKINGLQUAKE
2055 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2057 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2059 // LordHavoc: only render if not too close
2060 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2061 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2064 glDisable(GL_BLEND);
2065 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2067 // LordHavoc: only render if not too close
2068 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2073 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2075 if (p->type == particletype + pt_decal)
2076 R_DrawParticleCallback(p, 0);
2078 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);