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, void **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;
208 pt_alphastatic, pt_static, pt_spark, pt_beam, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_grow, pt_decal, pt_entityparticle, pt_total
212 // must match ptype_t values
213 particletype_t particletype[pt_total] =
215 {PBLEND_ALPHA, PARTICLE_BILLBOARD}, //pt_alphastatic
216 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_static
217 {PBLEND_ADD, PARTICLE_SPARK}, //pt_spark
218 {PBLEND_ADD, PARTICLE_BEAM}, //pt_beam
219 {PBLEND_ADD, PARTICLE_SPARK}, //pt_rain
220 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED}, //pt_raindecal
221 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_snow
222 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_bubble
223 {PBLEND_MOD, PARTICLE_BILLBOARD}, //pt_blood
224 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_grow
225 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED}, //pt_decal
226 {PBLEND_ALPHA, PARTICLE_BILLBOARD}, //pt_entityparticle
229 typedef struct particle_s
231 particletype_t *type;
234 vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam
236 float alpha; // 0-255
237 float alphafade; // how much alpha reduces per second
238 float time2; // used for snow fluttering and decal fade
239 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)
240 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
241 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
243 #ifndef WORKINGLQUAKE
244 entity_render_t *owner; // decal stuck to this entity
245 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
246 vec3_t relativeorigin; // decal at this location in entity's coordinate space
247 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
252 static int particlepalette[256] =
254 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
255 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
256 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
257 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
258 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
259 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
260 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
261 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
262 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
263 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
264 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
265 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
266 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
267 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
268 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
269 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
270 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
271 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
272 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
273 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
274 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
275 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
276 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
277 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
278 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
279 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
280 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
281 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
282 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
283 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
284 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
285 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
288 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
290 // texture numbers in particle font
291 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
292 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
293 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
294 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
295 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
296 static const int tex_particle = 63;
297 static const int tex_bubble = 62;
298 static const int tex_raindrop = 61;
299 static const int tex_beam = 60;
301 static int cl_maxparticles;
302 static int cl_numparticles;
303 static int cl_freeparticle;
304 static particle_t *particles;
306 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
307 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
308 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
309 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
310 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
311 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
312 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
313 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
314 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
315 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
316 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
317 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
318 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
319 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
320 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
321 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
322 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
323 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
324 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
325 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
327 void CL_Particles_Clear(void)
331 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
339 void CL_ReadPointFile_f (void);
340 void CL_Particles_Init (void)
344 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
345 i = COM_CheckParm ("-particles");
347 if (i && i < com_argc - 1)
349 cl_maxparticles = (int)(atoi(com_argv[i+1]));
350 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
351 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
354 cl_maxparticles = MAX_PARTICLES;
356 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
358 Cvar_RegisterVariable (&cl_particles);
359 Cvar_RegisterVariable (&cl_particles_quality);
360 Cvar_RegisterVariable (&cl_particles_size);
361 Cvar_RegisterVariable (&cl_particles_bloodshowers);
362 Cvar_RegisterVariable (&cl_particles_blood);
363 Cvar_RegisterVariable (&cl_particles_blood_alpha);
364 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
365 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
366 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
367 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
368 Cvar_RegisterVariable (&cl_particles_explosions_shell);
369 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
370 Cvar_RegisterVariable (&cl_particles_smoke);
371 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
372 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
373 Cvar_RegisterVariable (&cl_particles_sparks);
374 Cvar_RegisterVariable (&cl_particles_bubbles);
375 Cvar_RegisterVariable (&cl_decals);
376 Cvar_RegisterVariable (&cl_decals_time);
377 Cvar_RegisterVariable (&cl_decals_fadetime);
380 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
382 particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
384 CL_Particles_Clear();
387 void CL_Particles_Shutdown (void)
390 // No clue what to do here...
394 // list of all 26 parameters:
395 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
396 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
397 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
398 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
399 // palpha - opacity of particle as 0-255 (can be more than 255)
400 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
401 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
402 // pgravity - how much effect gravity has on the particle (0-1)
403 // 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
404 // px,py,pz - starting origin of particle
405 // pvx,pvy,pvz - starting velocity of particle
406 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
407 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)
410 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
411 ptempcolor = (pcolor1);
412 ptempcolor2 = (pcolor2);
413 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
414 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
415 pcb2 = (ptempcolor2) & 0xFF;
416 if (ptempcolor != ptempcolor2)
418 pcr1 = ((ptempcolor) >> 16) & 0xFF;
419 pcg1 = ((ptempcolor) >> 8) & 0xFF;
420 pcb1 = (ptempcolor) & 0xFF;
421 ptempcolor = rand() & 0xFF;
422 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
423 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
424 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
426 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
427 if (cl_freeparticle >= cl_maxparticles)
429 part = &particles[cl_freeparticle++];
430 if (cl_numparticles < cl_freeparticle)
431 cl_numparticles = cl_freeparticle;
432 memset(part, 0, sizeof(*part));
433 part->type = (ptype);
434 part->color[0] = pcr2;
435 part->color[1] = pcg2;
436 part->color[2] = pcb2;
437 part->color[3] = 0xFF;
439 part->size = (psize);
440 part->alpha = (palpha);
441 part->alphafade = (palphafade);
442 part->gravity = (pgravity);
443 part->bounce = (pbounce);
447 part->vel[0] = (pvx);
448 part->vel[1] = (pvy);
449 part->vel[2] = (pvz);
451 part->friction = (pfriction);
455 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
458 if (!cl_decals.integer)
460 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);
464 #ifndef WORKINGLQUAKE
466 p->ownermodel = p->owner->model;
467 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
468 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
469 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
474 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
477 float bestfrac, bestorg[3], bestnormal[3];
478 float frac, v[3], normal[3], org2[3];
480 void *besthitent = NULL, *hitent;
482 entity_render_t *besthitent = NULL, *hitent;
485 for (i = 0;i < 32;i++)
488 VectorMA(org, maxdist, org2, org2);
489 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
494 VectorCopy(v, bestorg);
495 VectorCopy(normal, bestnormal);
499 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
507 void CL_EntityParticles (entity_t *ent)
511 float sp, sy, cp, cy;
515 static vec3_t avelocities[NUMVERTEXNORMALS];
516 if (!cl_particles.integer) return;
521 if (!avelocities[0][0])
522 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
523 avelocities[0][i] = (rand()&255) * 0.01;
525 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
527 angle = cl.time * avelocities[i][0];
530 angle = cl.time * avelocities[i][1];
539 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);
541 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);
547 void CL_ReadPointFile_f (void)
551 char *pointfile = NULL, *pointfilepos, *t, tchar;
552 char name[MAX_OSPATH];
557 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
558 strlcat (name, ".pts", sizeof (name));
560 pointfile = COM_LoadTempFile (name);
562 pointfile = FS_LoadFile(name, tempmempool, true);
566 Con_Printf("Could not open %s\n", name);
570 Con_Printf("Reading %s...\n", name);
573 pointfilepos = pointfile;
574 while (*pointfilepos)
576 while (*pointfilepos == '\n' || *pointfilepos == '\r')
581 while (*t && *t != '\n' && *t != '\r')
585 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
591 VectorCopy(org, leakorg);
594 if (cl_numparticles < cl_maxparticles - 3)
597 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);
600 #ifndef WORKINGLQUAKE
603 VectorCopy(leakorg, org);
604 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
606 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);
607 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);
608 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);
613 CL_ParseParticleEffect
615 Parse an effect out of the server message
618 void CL_ParseParticleEffect (void)
621 int i, count, msgcount, color;
623 MSG_ReadVector(org, cl.protocol);
624 for (i=0 ; i<3 ; i++)
625 dir[i] = MSG_ReadChar () * (1.0/16);
626 msgcount = MSG_ReadByte ();
627 color = MSG_ReadByte ();
634 if (cl_particles_blood_bloodhack.integer)
639 CL_BloodPuff(org, dir, count / 2);
645 CL_BloodPuff(org, dir, count / 2);
649 CL_RunParticleEffect (org, dir, color, count);
658 void CL_ParticleExplosion (vec3_t org)
663 if (cl_stainmaps.integer)
664 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
665 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
667 i = CL_PointSuperContents(org);
668 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
670 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
671 for (i = 0;i < 128 * cl_particles_quality.value;i++)
672 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, (1.0f / cl_particles_quality.value) * lhrandom(128, 255), (1.0f / cl_particles_quality.value) * 128, -0.125, 1.5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-96, 96), lhrandom(-96, 96), lhrandom(-96, 96), (1.0 / 16.0));
676 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
678 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
680 for (i = 0;i < 32;i++)
685 v2[0] = lhrandom(-48, 48);
686 v2[1] = lhrandom(-48, 48);
687 v2[2] = lhrandom(-48, 48);
689 for (k = 0;k < 16;k++)
691 v[0] = org[0] + lhrandom(-48, 48);
692 v[1] = org[1] + lhrandom(-48, 48);
693 v[2] = org[2] + lhrandom(-48, 48);
694 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
697 VectorSubtract(v2, org, v2);
699 VectorScale(v2, 2.0f, v2);
700 particle(particletype + pt_static, 0xFFFFFF, 0xFFFFFF, tex_smoke[rand()&7], 12, 32, 64, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0);
704 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
705 for (i = 0;i < 128 * cl_particles_quality.value;i++)
706 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);
709 if (cl_particles_explosions_shell.integer)
715 CL_ParticleExplosion2
719 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
725 if (!cl_particles.integer) return;
727 for (i = 0;i < 512 * cl_particles_quality.value;i++)
729 VectorRandom (offset);
730 VectorScale (offset, 192, vel);
731 VectorScale (offset, 8, offset);
732 k = particlepalette[colorStart + (i % colorLength)];
733 pscale = lhrandom(0.5, 1.5);
734 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));
744 void CL_BlobExplosion (vec3_t org)
746 CL_ParticleExplosion(org);
755 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
761 CL_ParticleExplosion(org);
764 if (!cl_particles.integer) return;
765 count *= cl_particles_quality.value;
768 k = particlepalette[color + (rand()&7)];
769 if (gamemode == GAME_GOODVSBAD2)
770 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);
772 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);
776 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
782 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
786 if (!cl_particles.integer) return;
788 if (cl_particles_sparks.integer)
791 count *= cl_particles_quality.value;
794 k = particlepalette[0x68 + (rand() & 7)];
795 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);
800 void CL_Smoke (vec3_t org, vec3_t dir, int count)
805 if (!cl_particles.integer) return;
808 if (cl_particles_smoke.integer)
810 k = count * 0.25 * cl_particles_quality.value;
813 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
814 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
815 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
816 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
817 particle(particletype + pt_grow, 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);
822 void CL_BulletMark (vec3_t org)
824 if (cl_stainmaps.integer)
825 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
826 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
829 void CL_PlasmaBurn (vec3_t org)
831 if (cl_stainmaps.integer)
832 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
833 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
836 static float bloodcount = 0;
837 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
841 // bloodcount is used to accumulate counts too small to cause a blood particle
842 if (!cl_particles.integer) return;
843 if (!cl_particles_blood.integer) return;
850 while(bloodcount > 0)
852 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
853 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
854 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
855 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
856 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, org3[0], org3[1], org3[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 1);
857 bloodcount -= 16 / cl_particles_quality.value;
861 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
863 vec3_t org, vel, diff, center, velscale;
864 if (!cl_particles.integer) return;
865 if (!cl_particles_bloodshowers.integer) return;
866 if (!cl_particles_blood.integer) return;
868 VectorSubtract(maxs, mins, diff);
869 center[0] = (mins[0] + maxs[0]) * 0.5;
870 center[1] = (mins[1] + maxs[1]) * 0.5;
871 center[2] = (mins[2] + maxs[2]) * 0.5;
872 velscale[0] = velspeed * 2.0 / diff[0];
873 velscale[1] = velspeed * 2.0 / diff[1];
874 velscale[2] = velspeed * 2.0 / diff[2];
876 bloodcount += count * 5.0f;
877 while (bloodcount > 0)
879 org[0] = lhrandom(mins[0], maxs[0]);
880 org[1] = lhrandom(mins[1], maxs[1]);
881 org[2] = lhrandom(mins[2], maxs[2]);
882 vel[0] = (org[0] - center[0]) * velscale[0];
883 vel[1] = (org[1] - center[1]) * velscale[1];
884 vel[2] = (org[2] - center[2]) * velscale[2];
885 bloodcount -= 16 / cl_particles_quality.value;
886 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 1);
890 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
894 if (!cl_particles.integer) return;
895 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
896 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
897 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
899 count *= cl_particles_quality.value;
902 k = particlepalette[colorbase + (rand()&3)];
903 particle(particletype + pt_alphastatic, k, k, tex_particle, 2, 255 / cl_particles_quality.value, (255 / cl_particles_quality.value) / 2, gravity ? 1 : 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0] + lhrandom(-randomvel, randomvel), dir[1] + lhrandom(-randomvel, randomvel), dir[2] + lhrandom(-randomvel, randomvel), 0);
907 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
910 float t, z, minz, maxz;
911 if (!cl_particles.integer) return;
912 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
913 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
914 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
915 if (dir[2] < 0) // falling
920 minz = z - fabs(dir[2]) * 0.1;
921 maxz = z + fabs(dir[2]) * 0.1;
922 minz = bound(mins[2], minz, maxs[2]);
923 maxz = bound(mins[2], maxz, maxs[2]);
925 count *= cl_particles_quality.value;
930 count *= 4; // ick, this should be in the mod or maps?
934 k = particlepalette[colorbase + (rand()&3)];
935 if (gamemode == GAME_GOODVSBAD2)
936 particle(particletype + pt_rain, k, k, tex_particle, 20, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
938 particle(particletype + pt_rain, k, k, tex_particle, 0.5, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
944 k = particlepalette[colorbase + (rand()&3)];
945 if (gamemode == GAME_GOODVSBAD2)
946 particle(particletype + pt_snow, k, k, tex_particle, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
948 particle(particletype + pt_snow, k, k, tex_particle, 1, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
952 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
956 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
961 if (!cl_particles.integer) return;
963 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
964 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
965 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
967 center[0] = (mins[0] + maxs[0]) * 0.5f;
968 center[1] = (mins[1] + maxs[1]) * 0.5f;
969 center[2] = (mins[2] + maxs[2]) * 0.5f;
971 count *= cl_particles_quality.value;
974 k = particlepalette[224 + (rand()&15)];
975 o[0] = lhrandom(mins[0], maxs[0]);
976 o[1] = lhrandom(mins[1], maxs[1]);
977 o[2] = lhrandom(mins[2], maxs[2]);
978 VectorSubtract(o, center, v);
979 VectorNormalizeFast(v);
980 VectorScale(v, 100, v);
981 v[2] += sv_gravity.value * 0.15f;
982 particle(particletype + pt_static, 0x903010, 0xFFD030, tex_particle, 1.5, lhrandom(64, 128) / cl_particles_quality.value, 128 / cl_particles_quality.value, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0.2);
986 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
990 if (!cl_particles.integer) return;
991 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
992 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
993 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
995 count *= cl_particles_quality.value;
998 k = particlepalette[224 + (rand()&15)];
999 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(0, 64), 1);
1001 particle(particletype + pt_static, 0x303030, 0x606060, tex_smoke[rand()&7], 6, lhrandom(48, 96) / cl_particles_quality.value, 64 / cl_particles_quality.value, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 32), 0);
1005 void CL_Flames (vec3_t org, vec3_t vel, int count)
1008 if (!cl_particles.integer) return;
1010 count *= cl_particles_quality.value;
1013 k = particlepalette[224 + (rand()&15)];
1014 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 1);
1026 void CL_LavaSplash (vec3_t origin)
1028 float i, j, inc, vel;
1031 if (!cl_particles.integer) return;
1033 inc = 32 / cl_particles_quality.value;
1034 for (i = -128;i < 128;i += inc)
1036 for (j = -128;j < 128;j += inc)
1038 dir[0] = j + lhrandom(0, 8);
1039 dir[1] = i + lhrandom(0, 8);
1041 org[0] = origin[0] + dir[0];
1042 org[1] = origin[1] + dir[1];
1043 org[2] = origin[2] + lhrandom(0, 64);
1044 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1045 if (gamemode == GAME_GOODVSBAD2)
1047 k = particlepalette[0 + (rand()&255)];
1048 l = particlepalette[0 + (rand()&255)];
1049 particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 1, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0);
1053 k = l = particlepalette[224 + (rand()&7)];
1054 particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0);
1066 void CL_TeleportSplash (vec3_t org)
1069 if (!cl_particles.integer) return;
1071 inc = 8 / cl_particles_quality.value;
1072 for (i = -16;i < 16;i += inc)
1073 for (j = -16;j < 16;j += inc)
1074 for (k = -24;k < 32;k += inc)
1075 particle(particletype + pt_static, 0xA0A0A0, 0xFFFFFF, tex_particle, 10, inc * lhrandom(8, 16), inc * 32, 0, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-256, 256), 1);
1078 #ifdef WORKINGLQUAKE
1079 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1081 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1084 vec3_t vec, dir, vel, pos;
1085 float len, dec, speed, qd;
1086 int smoke, blood, bubbles;
1087 #ifdef WORKINGLQUAKE
1091 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1094 VectorSubtract(end, start, dir);
1095 VectorNormalize(dir);
1097 VectorSubtract (end, start, vec);
1098 #ifdef WORKINGLQUAKE
1099 len = VectorNormalize (vec);
1101 speed = 1.0f / cl.frametime;
1102 VectorSubtract(end, start, vel);
1104 len = VectorNormalizeLength (vec);
1105 dec = -ent->persistent.trail_time;
1106 ent->persistent.trail_time += len;
1107 if (ent->persistent.trail_time < 0.01f)
1110 // if we skip out, leave it reset
1111 ent->persistent.trail_time = 0.0f;
1113 speed = ent->state_current.time - ent->state_previous.time;
1115 speed = 1.0f / speed;
1116 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1117 color = particlepalette[color];
1119 VectorScale(vel, speed, vel);
1121 // advance into this frame to reach the first puff location
1122 VectorMA(start, dec, vec, pos);
1125 smoke = cl_particles.integer && cl_particles_smoke.integer;
1126 blood = cl_particles.integer && cl_particles_blood.integer;
1127 #ifdef WORKINGLQUAKE
1128 contents = CL_PointQ1Contents(pos);
1129 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1131 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1133 qd = 1.0f / cl_particles_quality.value;
1139 case 0: // rocket trail
1143 particle(particletype + pt_grow, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*125, qd*cl_particles_smoke_alphafade.value*125, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1144 particle(particletype + pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*288, qd*cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], lhrandom(-20, 20), lhrandom(-20, 20), lhrandom(-20, 20), 0);
1147 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, qd*lhrandom(64, 255), qd*256, -0.25, 1.5, pos[0], pos[1], pos[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), (1.0 / 16.0));
1150 case 1: // grenade trail
1151 // FIXME: make it gradually stop smoking
1154 particle(particletype + pt_grow, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*100, qd*cl_particles_smoke_alphafade.value*100, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1159 case 4: // slight blood
1162 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], vel[0] * 0.5f + lhrandom(-64, 64), vel[1] * 0.5f + lhrandom(-64, 64), vel[2] * 0.5f + lhrandom(-64, 64), 1);
1165 case 3: // green tracer
1169 if (gamemode == GAME_GOODVSBAD2)
1170 particle(particletype + pt_static, 0x00002E, 0x000030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1172 particle(particletype + pt_static, 0x002000, 0x003000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1176 case 5: // flame tracer
1179 particle(particletype + pt_static, 0x301000, 0x502000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1182 case 6: // voor trail
1186 if (gamemode == GAME_GOODVSBAD2)
1187 particle(particletype + pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, qd*255, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1188 else if (gamemode == GAME_PRYDON)
1189 particle(particletype + pt_static, 0x103040, 0x204050, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1191 particle(particletype + pt_static, 0x502030, 0x502030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1194 #ifndef WORKINGLQUAKE
1195 case 7: // Nehahra smoke tracer
1198 particle(particletype + pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, qd*64, qd*320, 0, 0, pos[0], pos[1], pos[2], lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(0, 16), 0);
1200 case 8: // Nexuiz plasma trail
1203 particle(particletype + pt_static, 0x283880, 0x283880, tex_particle, 4, qd*255, qd*1024, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0);
1205 case 9: // glow trail
1208 particle(particletype + pt_alphastatic, color, color, tex_particle, 5, qd*128, qd*320, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0);
1213 // advance to next time and position
1215 VectorMA (pos, dec, vec, pos);
1217 #ifndef WORKINGLQUAKE
1218 ent->persistent.trail_time = len;
1222 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1224 int tempcolor2, cr, cg, cb;
1228 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1229 particle(particletype + pt_beam, tempcolor2, tempcolor2, tex_beam, radius, alpha * 255, alpha * 255 / lifetime, 0, 0, start[0], start[1], start[2], end[0], end[1], end[2], 0);
1232 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1235 if (!cl_particles.integer) return;
1238 if (cl_particles_smoke.integer)
1239 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1240 particle(particletype + pt_grow, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count) * 0.5f, dir[1] + lhrandom(-count, count) * 0.5f, dir[2] + lhrandom(-count, count) * 0.5f, 0);
1243 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1246 if (!cl_particles.integer) return;
1248 if (cl_stainmaps.integer)
1249 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1250 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1253 if (cl_particles_smoke.integer)
1254 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1255 particle(particletype + pt_grow, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count), dir[1] + lhrandom(-count, count), dir[2] + lhrandom(-count, count), 0);
1258 if (cl_particles_sparks.integer)
1259 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1260 particle(particletype + pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, lhrandom(64, 255) / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0], org[1], org[2], lhrandom(-count, count) * 3.0f + dir[0], lhrandom(-count, count) * 3.0f + dir[1], lhrandom(-count, count) * 3.0f + dir[2], 0);
1268 void CL_MoveParticles (void)
1271 int i, maxparticle, j, a, content;
1272 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3], oldorg[3];
1273 #ifdef WORKINGLQUAKE
1276 entity_render_t *hitent;
1279 // LordHavoc: early out condition
1280 if (!cl_numparticles)
1282 cl_freeparticle = 0;
1286 #ifdef WORKINGLQUAKE
1287 frametime = cl.frametime;
1289 frametime = cl.time - cl.oldtime;
1291 gravity = frametime * sv_gravity.value;
1292 dvel = 1+4*frametime;
1293 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1297 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1304 p->alpha -= p->alphafade * frametime;
1312 if (p->type->orientation != PARTICLE_BEAM)
1314 VectorCopy(p->org, oldorg);
1315 VectorMA(p->org, frametime, p->vel, p->org);
1316 VectorCopy(p->org, org);
1319 if (p->type == particletype + pt_rain)
1321 // raindrop - splash on solid/water/slime/lava
1322 if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK) < 1)
1324 VectorCopy(v, p->org);
1326 p->type = particletype + pt_raindecal;
1327 // convert from a raindrop particle to a rainsplash decal
1328 p->texnum = tex_rainsplash[0];
1330 p->alphafade = p->alpha / 0.4;
1331 VectorCopy(normal, p->vel);
1332 VectorAdd(p->org, normal, p->org);
1339 else if (p->type == particletype + pt_blood)
1341 // blood - splash on solid
1342 if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1344 VectorCopy(v, p->org);
1345 #ifndef WORKINGLQUAKE
1346 if (cl_stainmaps.integer)
1347 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));
1349 if (!cl_decals.integer)
1355 p->type = particletype + pt_decal;
1356 // convert from a blood particle to a blood decal
1357 p->texnum = tex_blooddecal[rand()&7];
1358 #ifndef WORKINGLQUAKE
1360 p->ownermodel = hitent->model;
1361 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1362 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1363 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1367 VectorCopy(normal, p->vel);
1368 VectorAdd(p->org, normal, p->org);
1377 if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1379 VectorCopy(v, p->org);
1387 dist = DotProduct(p->vel, normal) * -p->bounce;
1388 VectorMA(p->vel, dist, normal, p->vel);
1389 if (DotProduct(p->vel, p->vel) < 0.03)
1390 VectorClear(p->vel);
1395 p->vel[2] -= p->gravity * gravity;
1399 f = p->friction * frametime;
1400 #ifdef WORKINGLQUAKE
1401 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1403 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1407 VectorScale(p->vel, f, p->vel);
1411 if (p->type != particletype + pt_static)
1413 switch (p->type - particletype)
1415 case pt_entityparticle:
1416 // particle that removes itself after one rendered frame
1423 #ifdef WORKINGLQUAKE
1424 a = CL_PointQ1Contents(p->org);
1425 if (a <= CONTENTS_WATER)
1427 a = CL_PointSuperContents(p->org);
1428 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1431 p->size += frametime * 8;
1432 //p->alpha -= bloodwaterfade;
1435 p->vel[2] -= gravity;
1436 #ifdef WORKINGLQUAKE
1437 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1439 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1444 #ifdef WORKINGLQUAKE
1445 a = CL_PointQ1Contents(p->org);
1446 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1448 a = CL_PointSuperContents(p->org);
1449 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1457 #ifdef WORKINGLQUAKE
1458 a = CL_PointQ1Contents(p->org);
1459 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1461 a = CL_PointSuperContents(p->org);
1462 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1467 if (cl.time > p->time2)
1470 p->time2 = cl.time + (rand() & 3) * 0.1;
1471 p->vel[0] += lhrandom(-32, 32);
1472 p->vel[1] += lhrandom(-32, 32);
1473 p->vel[2] += lhrandom(-32, 32);
1475 #ifdef WORKINGLQUAKE
1476 a = CL_PointQ1Contents(p->org);
1477 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1479 a = CL_PointSuperContents(p->org);
1480 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1485 p->size += frametime * 15;
1488 // FIXME: this has fairly wacky handling of alpha
1489 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1490 #ifndef WORKINGLQUAKE
1491 if (p->owner->model == p->ownermodel)
1493 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1494 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel);
1501 a = max(0, (cl.time - p->time2) * 40);
1503 p->texnum = tex_rainsplash[a];
1512 cl_numparticles = maxparticle + 1;
1513 cl_freeparticle = 0;
1516 #define MAX_PARTICLETEXTURES 64
1517 // particletexture_t is a rectangle in the particlefonttexture
1520 rtexture_t *texture;
1521 float s1, t1, s2, t2;
1526 static int particlefonttexture;
1528 static rtexturepool_t *particletexturepool;
1529 static rtexture_t *particlefonttexture;
1531 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1533 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1535 #define PARTICLETEXTURESIZE 64
1536 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1538 static qbyte shadebubble(float dx, float dy, vec3_t light)
1542 dz = 1 - (dx*dx+dy*dy);
1543 if (dz > 0) // it does hit the sphere
1547 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1548 VectorNormalize(normal);
1549 dot = DotProduct(normal, light);
1550 if (dot > 0.5) // interior reflection
1551 f += ((dot * 2) - 1);
1552 else if (dot < -0.5) // exterior reflection
1553 f += ((dot * -2) - 1);
1555 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1556 VectorNormalize(normal);
1557 dot = DotProduct(normal, light);
1558 if (dot > 0.5) // interior reflection
1559 f += ((dot * 2) - 1);
1560 else if (dot < -0.5) // exterior reflection
1561 f += ((dot * -2) - 1);
1563 f += 16; // just to give it a haze so you can see the outline
1564 f = bound(0, f, 255);
1571 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1573 int basex, basey, y;
1574 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1575 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1576 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1577 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1578 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1579 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1580 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1581 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1584 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1587 float cx, cy, dx, dy, f, iradius;
1589 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1590 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1591 iradius = 1.0f / radius;
1592 alpha *= (1.0f / 255.0f);
1593 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1595 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1599 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1602 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1603 d[0] += f * (red - d[0]);
1604 d[1] += f * (green - d[1]);
1605 d[2] += f * (blue - d[2]);
1611 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1614 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1616 data[0] = bound(minr, data[0], maxr);
1617 data[1] = bound(ming, data[1], maxg);
1618 data[2] = bound(minb, data[2], maxb);
1622 void particletextureinvert(qbyte *data)
1625 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1627 data[0] = 255 - data[0];
1628 data[1] = 255 - data[1];
1629 data[2] = 255 - data[2];
1633 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1634 static void R_InitBloodTextures (qbyte *particletexturedata)
1637 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1640 for (i = 0;i < 8;i++)
1642 memset(&data[0][0][0], 255, sizeof(data));
1643 for (k = 0;k < 24;k++)
1644 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1645 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1646 particletextureinvert(&data[0][0][0]);
1647 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1651 for (i = 0;i < 8;i++)
1653 memset(&data[0][0][0], 255, sizeof(data));
1655 for (j = 1;j < 10;j++)
1656 for (k = min(j, m - 1);k < m;k++)
1657 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1658 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1659 particletextureinvert(&data[0][0][0]);
1660 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1665 static void R_InitParticleTexture (void)
1667 int x, y, d, i, k, m;
1668 float dx, dy, radius, f, f2;
1669 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1671 qbyte *particletexturedata;
1673 // a note: decals need to modulate (multiply) the background color to
1674 // properly darken it (stain), and they need to be able to alpha fade,
1675 // this is a very difficult challenge because it means fading to white
1676 // (no change to background) rather than black (darkening everything
1677 // behind the whole decal polygon), and to accomplish this the texture is
1678 // inverted (dark red blood on white background becomes brilliant cyan
1679 // and white on black background) so we can alpha fade it to black, then
1680 // we invert it again during the blendfunc to make it work...
1682 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1683 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1686 for (i = 0;i < 8;i++)
1688 memset(&data[0][0][0], 255, sizeof(data));
1691 qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1693 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1694 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1696 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1698 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1699 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1701 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1702 d = (noise2[y][x] - 128) * 3 + 192;
1704 d = d * (1-(dx*dx+dy*dy));
1705 d = (d * noise1[y][x]) >> 7;
1706 d = bound(0, d, 255);
1707 data[y][x][3] = (qbyte) d;
1714 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1718 for (i = 0;i < 16;i++)
1720 memset(&data[0][0][0], 255, sizeof(data));
1721 radius = i * 3.0f / 4.0f / 16.0f;
1722 f2 = 255.0f * ((15.0f - i) / 15.0f);
1723 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1725 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1726 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1728 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1729 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1730 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1733 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1737 memset(&data[0][0][0], 255, sizeof(data));
1738 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1740 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1741 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1743 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1744 d = 256 * (1 - (dx*dx+dy*dy));
1745 d = bound(0, d, 255);
1746 data[y][x][3] = (qbyte) d;
1749 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1752 memset(&data[0][0][0], 255, sizeof(data));
1753 light[0] = 1;light[1] = 1;light[2] = 1;
1754 VectorNormalize(light);
1755 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1757 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1758 // stretch upper half of bubble by +50% and shrink lower half by -50%
1759 // (this gives an elongated teardrop shape)
1761 dy = (dy - 0.5f) * 2.0f;
1763 dy = (dy - 0.5f) / 1.5f;
1764 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1766 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1767 // shrink bubble width to half
1769 data[y][x][3] = shadebubble(dx, dy, light);
1772 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1775 memset(&data[0][0][0], 255, sizeof(data));
1776 light[0] = 1;light[1] = 1;light[2] = 1;
1777 VectorNormalize(light);
1778 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1780 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1781 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1783 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1784 data[y][x][3] = shadebubble(dx, dy, light);
1787 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1789 // Blood particles and blood decals
1790 R_InitBloodTextures (particletexturedata);
1793 for (i = 0;i < 8;i++)
1795 memset(&data[0][0][0], 255, sizeof(data));
1796 for (k = 0;k < 12;k++)
1797 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1798 for (k = 0;k < 3;k++)
1799 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1800 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1801 particletextureinvert(&data[0][0][0]);
1802 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1806 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1807 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1808 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1812 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1815 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1816 if (!particlefonttexture)
1817 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1818 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1819 particletexture[i].texture = particlefonttexture;
1822 fractalnoise(&noise3[0][0], 64, 4);
1824 for (y = 0;y < 64;y++)
1826 dy = (y - 0.5f*64) / (64*0.5f-1);
1827 for (x = 0;x < 16;x++)
1829 dx = (x - 0.5f*16) / (16*0.5f-2);
1830 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1831 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1832 data2[y][x][3] = 255;
1837 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1840 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1841 if (!particletexture[tex_beam].texture)
1842 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1843 particletexture[tex_beam].s1 = 0;
1844 particletexture[tex_beam].t1 = 0;
1845 particletexture[tex_beam].s2 = 1;
1846 particletexture[tex_beam].t2 = 1;
1848 Mem_Free(particletexturedata);
1851 static void r_part_start(void)
1853 particletexturepool = R_AllocTexturePool();
1854 R_InitParticleTexture ();
1857 static void r_part_shutdown(void)
1859 R_FreeTexturePool(&particletexturepool);
1862 static void r_part_newmap(void)
1864 cl_numparticles = 0;
1865 cl_freeparticle = 0;
1868 void R_Particles_Init (void)
1870 Cvar_RegisterVariable(&r_drawparticles);
1871 #ifdef WORKINGLQUAKE
1874 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1878 #ifdef WORKINGLQUAKE
1879 void R_InitParticles(void)
1881 CL_Particles_Init();
1886 float particle_vertex3f[12], particle_texcoord2f[8];
1888 #ifdef WORKINGLQUAKE
1889 void R_DrawParticle(particle_t *p)
1892 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1894 const particle_t *p = calldata1;
1898 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, size;
1899 particletexture_t *tex;
1901 VectorCopy(p->org, org);
1903 blendmode = p->type->blendmode;
1904 tex = &particletexture[p->texnum];
1905 cr = p->color[0] * (1.0f / 255.0f);
1906 cg = p->color[1] * (1.0f / 255.0f);
1907 cb = p->color[2] * (1.0f / 255.0f);
1908 ca = p->alpha * (1.0f / 255.0f);
1909 if (blendmode == PBLEND_MOD)
1919 #ifndef WORKINGLQUAKE
1922 VectorSubtract(org, r_vieworigin, fogvec);
1923 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1928 if (blendmode == PBLEND_ADD)
1930 cr += fogcolor[0] * fog;
1931 cg += fogcolor[1] * fog;
1932 cb += fogcolor[2] * fog;
1936 R_Mesh_Matrix(&r_identitymatrix);
1938 memset(&m, 0, sizeof(m));
1939 m.tex[0] = R_GetTexture(tex->texture);
1940 m.pointer_texcoord[0] = particle_texcoord2f;
1941 m.pointer_vertex = particle_vertex3f;
1944 GL_Color(cr, cg, cb, ca);
1946 if (blendmode == PBLEND_ALPHA)
1947 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1948 else if (blendmode == PBLEND_ADD)
1949 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1950 else //if (blendmode == PBLEND_MOD)
1951 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1952 GL_DepthMask(false);
1955 size = p->size * cl_particles_size.value;
1956 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1958 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1961 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1963 VectorNegate(p->vel, v);
1964 VectorVectors(v, right, up);
1967 VectorVectors(p->vel, right, up);
1968 VectorScale(right, size, right);
1969 VectorScale(up, size, up);
1973 VectorScale(r_viewleft, -size, right);
1974 VectorScale(r_viewup, size, up);
1976 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1977 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1978 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1979 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1980 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1981 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1982 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1983 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1984 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1985 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1986 particle_vertex3f[10] = org[1] + right[1] - up[1];
1987 particle_vertex3f[11] = org[2] + right[2] - up[2];
1988 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1989 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1990 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1991 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1993 else if (p->type->orientation == PARTICLE_SPARK)
1995 VectorMA(p->org, -0.02, p->vel, v);
1996 VectorMA(p->org, 0.02, p->vel, up2);
1997 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
1998 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1999 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2000 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2001 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2003 else if (p->type->orientation == PARTICLE_BEAM)
2005 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2006 VectorSubtract(p->vel, p->org, up);
2007 VectorNormalizeFast(up);
2008 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2009 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2010 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2011 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2012 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2013 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2016 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2019 if (blendmode == PBLEND_ALPHA)
2020 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2021 else if (blendmode == PBLEND_ADD)
2022 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2023 else //if (blendmode == PBLEND_MOD)
2024 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2025 glColor4f(cr, cg, cb, ca);
2027 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2028 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2029 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2030 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2033 R_Mesh_Draw(0, 4, 2, polygonelements);
2037 void R_DrawParticles (void)
2040 float minparticledist;
2043 #ifdef WORKINGLQUAKE
2047 // LordHavoc: early out conditions
2048 if ((!cl_numparticles) || (!r_drawparticles.integer))
2051 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2053 #ifdef WORKINGLQUAKE
2054 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2056 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2058 // LordHavoc: only render if not too close
2059 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2060 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2063 glDisable(GL_BLEND);
2064 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2066 // LordHavoc: only render if not too close
2067 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2072 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2074 if (p->type == particletype + pt_decal)
2075 R_DrawParticleCallback(p, 0);
2077 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);