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_BlobExplosion R_BlobExplosion
41 #define CL_RunParticleEffect R_RunParticleEffect
42 #define CL_LavaSplash R_LavaSplash
43 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
45 vec3_t right1, right2, diff, normal;
47 VectorSubtract (org2, org1, normal);
48 VectorNormalizeFast (normal);
50 // calculate 'right' vector for start
51 VectorSubtract (r_vieworigin, org1, diff);
52 VectorNormalizeFast (diff);
53 CrossProduct (normal, diff, right1);
55 // calculate 'right' vector for end
56 VectorSubtract (r_vieworigin, org2, diff);
57 VectorNormalizeFast (diff);
58 CrossProduct (normal, diff, right2);
60 vert[ 0] = org1[0] + width * right1[0];
61 vert[ 1] = org1[1] + width * right1[1];
62 vert[ 2] = org1[2] + width * right1[2];
63 vert[ 3] = org1[0] - width * right1[0];
64 vert[ 4] = org1[1] - width * right1[1];
65 vert[ 5] = org1[2] - width * right1[2];
66 vert[ 6] = org2[0] - width * right2[0];
67 vert[ 7] = org2[1] - width * right2[1];
68 vert[ 8] = org2[2] - width * right2[2];
69 vert[ 9] = org2[0] + width * right2[0];
70 vert[10] = org2[1] + width * right2[1];
71 vert[11] = org2[2] + width * right2[2];
73 void fractalnoise(qbyte *noise, int size, int startgrid)
75 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
77 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
79 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
80 if (size != (1 << sizepower))
81 Sys_Error("fractalnoise: size must be power of 2\n");
83 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
84 if (startgrid != (1 << gridpower))
85 Sys_Error("fractalnoise: grid must be power of 2\n");
87 startgrid = bound(0, startgrid, size);
89 amplitude = 0xFFFF; // this gets halved before use
90 noisebuf = malloc(size*size*sizeof(int));
91 memset(noisebuf, 0, size*size*sizeof(int));
93 for (g2 = startgrid;g2;g2 >>= 1)
95 // brownian motion (at every smaller level there is random behavior)
97 for (y = 0;y < size;y += g2)
98 for (x = 0;x < size;x += g2)
99 n(x,y) += (rand()&litude);
104 // subdivide, diamond-square algorithm (really this has little to do with squares)
106 for (y = 0;y < size;y += g2)
107 for (x = 0;x < size;x += g2)
108 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
110 for (y = 0;y < size;y += g2)
111 for (x = 0;x < size;x += g2)
113 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
114 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
118 // find range of noise values
120 for (y = 0;y < size;y++)
121 for (x = 0;x < size;x++)
123 if (n(x,y) < min) min = n(x,y);
124 if (n(x,y) > max) max = n(x,y);
128 // normalize noise and copy to output
129 for (y = 0;y < size;y++)
130 for (x = 0;x < size;x++)
131 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
135 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
139 right[0] = forward[2];
140 right[1] = -forward[0];
141 right[2] = forward[1];
143 d = DotProduct(forward, right);
144 right[0] -= d * forward[0];
145 right[1] -= d * forward[1];
146 right[2] -= d * forward[2];
147 VectorNormalizeFast(right);
148 CrossProduct(right, forward, up);
152 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
154 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int hitbmodels, void **hitent, int hitsupercontentsmask)
161 memset (&trace, 0, sizeof(trace));
163 VectorCopy (end, trace.endpos);
165 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
167 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
169 VectorCopy(trace.endpos, impact);
170 VectorCopy(trace.plane.normal, normal);
171 return trace.fraction;
174 #include "cl_collision.h"
178 #define MAX_PARTICLES 32768 // default max # of particles at one time
179 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
183 pt_dead, pt_static, pt_rain, pt_bubble, pt_blood, pt_grow, pt_decal, pt_decalfade, pt_ember
189 PARTICLE_BILLBOARD = 0,
191 PARTICLE_ORIENTED_DOUBLESIDED = 2,
204 typedef struct particle_s
215 float alpha; // 0-255
216 float alphafade; // how much alpha reduces per second
217 float time2; // used for various things (snow fluttering, for example)
218 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)
219 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
221 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
222 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
223 float pressure; // if non-zero, apply pressure to other particles
225 #ifndef WORKINGLQUAKE
226 entity_render_t *owner; // decal stuck to this entity
227 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
228 vec3_t relativeorigin; // decal at this location in entity's coordinate space
229 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
234 static int particlepalette[256] =
236 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
237 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
238 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
239 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
240 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
241 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
242 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
243 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
244 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
245 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
246 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
247 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
248 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
249 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
250 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
251 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
252 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
253 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
254 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
255 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
256 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
257 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
258 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
259 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
260 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
261 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
262 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
263 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
264 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
265 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
266 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
267 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
270 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
272 // texture numbers in particle font
273 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
274 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
275 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
276 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
277 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
278 static const int tex_particle = 63;
279 static const int tex_bubble = 62;
280 static const int tex_raindrop = 61;
281 static const int tex_beam = 60;
283 static int cl_maxparticles;
284 static int cl_numparticles;
285 static int cl_freeparticle;
286 static particle_t *particles;
288 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
289 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
290 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
291 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
292 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
293 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
294 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
295 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
296 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
297 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
298 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
299 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
300 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
301 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
302 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
303 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
304 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
305 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
306 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
307 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
309 #ifndef WORKINGLQUAKE
310 static mempool_t *cl_part_mempool;
313 void CL_Particles_Clear(void)
317 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
325 void CL_ReadPointFile_f (void);
326 void CL_Particles_Init (void)
330 i = COM_CheckParm ("-particles");
332 if (i && i < com_argc - 1)
334 cl_maxparticles = (int)(atoi(com_argv[i+1]));
335 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
336 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
339 cl_maxparticles = MAX_PARTICLES;
341 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
343 Cvar_RegisterVariable (&cl_particles);
344 Cvar_RegisterVariable (&cl_particles_quality);
345 Cvar_RegisterVariable (&cl_particles_size);
346 Cvar_RegisterVariable (&cl_particles_bloodshowers);
347 Cvar_RegisterVariable (&cl_particles_blood);
348 Cvar_RegisterVariable (&cl_particles_blood_alpha);
349 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
350 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
351 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
352 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
353 Cvar_RegisterVariable (&cl_particles_explosions_shell);
354 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
355 Cvar_RegisterVariable (&cl_particles_smoke);
356 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
357 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
358 Cvar_RegisterVariable (&cl_particles_sparks);
359 Cvar_RegisterVariable (&cl_particles_bubbles);
360 Cvar_RegisterVariable (&cl_decals);
361 Cvar_RegisterVariable (&cl_decals_time);
362 Cvar_RegisterVariable (&cl_decals_fadetime);
365 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
367 cl_part_mempool = Mem_AllocPool("CL_Part", 0, NULL);
368 particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
370 CL_Particles_Clear();
373 // list of all 26 parameters:
374 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
375 // porientation - PARTICLE_ enum values (PARTICLE_BILLBOARD, PARTICLE_SPARK, etc)
376 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
377 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
378 // plight - no longer used (this used to turn on particle lighting)
379 // pblendmode - PBLEND_ enum values (PBLEND_ALPHA, PBLEND_ADD, etc)
380 // pscalex,pscaley - width and height of particle (according to orientation), these are normally the same except when making sparks and beams
381 // palpha - opacity of particle as 0-255 (can be more than 255)
382 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
383 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
384 // pgravity - how much effect gravity has on the particle (0-1)
385 // 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
386 // px,py,pz - starting origin of particle
387 // pvx,pvy,pvz - starting velocity of particle
388 // ptime2 - extra time parameter for certain particle types (pt_decal delayed fades and pt_rain snowflutter use this)
389 // pvx2,pvy2,pvz2 - for PARTICLE_ORIENTED_DOUBLESIDED this is the surface normal of the orientation (forward vector), pt_rain uses this for snow fluttering
390 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
391 // ppressure - pushes other particles away if they are within 64 units distance, the force is based on scalex, this feature is supported but not currently used
392 particle_t *particle(ptype_t ptype, porientation_t porientation, int pcolor1, int pcolor2, int ptex, int plight, pblend_t pblendmode, float pscalex, float pscaley, float palpha, float palphafade, float ptime, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float ptime2, float pvx2, float pvy2, float pvz2, float pfriction, float ppressure)
395 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
396 ptempcolor = (pcolor1);
397 ptempcolor2 = (pcolor2);
398 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
399 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
400 pcb2 = (ptempcolor2) & 0xFF;
401 if (ptempcolor != ptempcolor2)
403 pcr1 = ((ptempcolor) >> 16) & 0xFF;
404 pcg1 = ((ptempcolor) >> 8) & 0xFF;
405 pcb1 = (ptempcolor) & 0xFF;
406 ptempcolor = rand() & 0xFF;
407 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
408 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
409 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
411 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
412 if (cl_freeparticle >= cl_maxparticles)
414 part = &particles[cl_freeparticle++];
415 if (cl_numparticles < cl_freeparticle)
416 cl_numparticles = cl_freeparticle;
417 memset(part, 0, sizeof(*part));
418 part->type = (ptype);
419 part->color[0] = pcr2;
420 part->color[1] = pcg2;
421 part->color[2] = pcb2;
422 part->color[3] = 0xFF;
423 part->orientation = porientation;
425 part->blendmode = pblendmode;
426 part->scalex = (pscalex);
427 part->scaley = (pscaley);
428 part->alpha = (palpha);
429 part->alphafade = (palphafade);
430 part->die = cl.time + (ptime);
431 part->gravity = (pgravity);
432 part->bounce = (pbounce);
436 part->vel[0] = (pvx);
437 part->vel[1] = (pvy);
438 part->vel[2] = (pvz);
439 part->time2 = (ptime2);
440 part->vel2[0] = (pvx2);
441 part->vel2[1] = (pvy2);
442 part->vel2[2] = (pvz2);
443 part->friction = (pfriction);
444 part->pressure = (ppressure);
448 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
451 if (!cl_decals.integer)
453 p = particle(pt_decal, PARTICLE_ORIENTED_DOUBLESIDED, color1, color2, texnum, false, PBLEND_MOD, size, size, alpha, 0, cl_decals_time.value + cl_decals_fadetime.value, 0, 0, org[0] + normal[0], org[1] + normal[1], org[2] + normal[2], 0, 0, 0, cl.time + cl_decals_time.value, normal[0], normal[1], normal[2], 0, 0);
454 #ifndef WORKINGLQUAKE
458 p->ownermodel = p->owner->model;
459 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
460 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
461 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
466 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
469 float bestfrac, bestorg[3], bestnormal[3];
470 float frac, v[3], normal[3], org2[3];
472 void *besthitent = NULL, *hitent;
474 entity_render_t *besthitent = NULL, *hitent;
477 for (i = 0;i < 32;i++)
480 VectorMA(org, maxdist, org2, org2);
481 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
486 VectorCopy(v, bestorg);
487 VectorCopy(normal, bestnormal);
491 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
499 void CL_EntityParticles (entity_t *ent)
503 float sp, sy, cp, cy;
507 static vec3_t avelocities[NUMVERTEXNORMALS];
508 if (!cl_particles.integer) return;
513 if (!avelocities[0][0])
514 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
515 avelocities[0][i] = (rand()&255) * 0.01;
517 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
519 angle = cl.time * avelocities[i][0];
522 angle = cl.time * avelocities[i][1];
531 particle(pt_static, PARTICLE_BILLBOARD, particlepalette[0x6f], particlepalette[0x6f], tex_particle, false, PBLEND_ADD, 2, 2, 255, 0, 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, 0, 0, 0, 0, 0);
533 particle(pt_static, PARTICLE_BILLBOARD, particlepalette[0x6f], particlepalette[0x6f], tex_particle, false, PBLEND_ADD, 2, 2, 255, 0, 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, 0, 0, 0, 0, 0);
539 void CL_ReadPointFile_f (void)
543 char *pointfile = NULL, *pointfilepos, *t, tchar;
544 char name[MAX_OSPATH];
549 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
550 strlcat (name, ".pts", sizeof (name));
552 pointfile = COM_LoadTempFile (name);
554 pointfile = FS_LoadFile(name, tempmempool, true);
558 Con_Printf("Could not open %s\n", name);
562 Con_Printf("Reading %s...\n", name);
565 pointfilepos = pointfile;
566 while (*pointfilepos)
568 while (*pointfilepos == '\n' || *pointfilepos == '\r')
573 while (*t && *t != '\n' && *t != '\r')
577 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
583 VectorCopy(org, leakorg);
586 if (cl_numparticles < cl_maxparticles - 3)
589 particle(pt_static, PARTICLE_BILLBOARD, particlepalette[(-c)&15], particlepalette[(-c)&15], tex_particle, false, PBLEND_ALPHA, 2, 2, 255, 0, 99999, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
592 #ifndef WORKINGLQUAKE
595 VectorCopy(leakorg, org);
596 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
598 particle(pt_static, PARTICLE_BEAM, 0xFF0000, 0xFF0000, tex_beam, false, PBLEND_ALPHA, 64, 64, 255, 0, 99999, 0, 0, org[0] - 4096, org[1], org[2], 0, 0, 0, 0, org[0] + 4096, org[1], org[2], 0, 0);
599 particle(pt_static, PARTICLE_BEAM, 0x00FF00, 0x00FF00, tex_beam, false, PBLEND_ALPHA, 64, 64, 255, 0, 99999, 0, 0, org[0], org[1] - 4096, org[2], 0, 0, 0, 0, org[0], org[1] + 4096, org[2], 0, 0);
600 particle(pt_static, PARTICLE_BEAM, 0x0000FF, 0x0000FF, tex_beam, false, PBLEND_ALPHA, 64, 64, 255, 0, 99999, 0, 0, org[0], org[1], org[2] - 4096, 0, 0, 0, 0, org[0], org[1], org[2] + 4096, 0, 0);
605 CL_ParseParticleEffect
607 Parse an effect out of the server message
610 void CL_ParseParticleEffect (void)
613 int i, count, msgcount, color;
615 MSG_ReadVector(org, cl.protocol);
616 for (i=0 ; i<3 ; i++)
617 dir[i] = MSG_ReadChar () * (1.0/16);
618 msgcount = MSG_ReadByte ();
619 color = MSG_ReadByte ();
626 if (cl_particles_blood_bloodhack.integer)
631 CL_BloodPuff(org, dir, count / 2);
637 CL_BloodPuff(org, dir, count / 2);
641 CL_RunParticleEffect (org, dir, color, count);
650 void CL_ParticleExplosion (vec3_t org)
655 if (cl_stainmaps.integer)
656 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
657 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
659 i = CL_PointQ1Contents(org);
660 if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
662 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
663 for (i = 0;i < 128 * cl_particles_quality.value;i++)
664 particle(pt_bubble, PARTICLE_BILLBOARD, 0x404040, 0x808080, tex_bubble, false, PBLEND_ADD, 2, 2, (1.0f / cl_particles_quality.value) * lhrandom(128, 255), (1.0f / cl_particles_quality.value) * 256, 9999, -0.25, 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), 0, 0, 0, 0, (1.0 / 16.0), 0);
668 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
670 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
672 for (i = 0;i < 32;i++)
677 v2[0] = lhrandom(-48, 48);
678 v2[1] = lhrandom(-48, 48);
679 v2[2] = lhrandom(-48, 48);
681 for (k = 0;k < 16;k++)
683 v[0] = org[0] + lhrandom(-48, 48);
684 v[1] = org[1] + lhrandom(-48, 48);
685 v[2] = org[2] + lhrandom(-48, 48);
686 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
689 VectorSubtract(v2, org, v2);
691 VectorScale(v2, 2.0f, v2);
692 particle(pt_static, PARTICLE_BILLBOARD, 0xFFFFFF, 0xFFFFFF, tex_smoke[rand()&7], true, PBLEND_ADD, 12, 12, 32, 64, 9999, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0, 0, 0, 0, 0, 0);
697 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
698 for (i = 0;i < 128 * cl_particles_quality.value;i++)
699 particle(pt_static, PARTICLE_SPARK, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, 1.0f, 0.02f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 512, 9999, 1, 0, org[0], org[1], org[2], lhrandom(-256, 256), lhrandom(-256, 256), lhrandom(-256, 256) + 80, 0, 0, 0, 0, 0.2, 0);
701 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
702 for (i = 0;i < 64 * cl_particles_quality.value;i++)
703 particle(pt_ember, PARTICLE_SPARK, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, 1.0f, 0.01f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 256, 9999, 0.7, 0, org[0], org[1], org[2], lhrandom(-256, 256), lhrandom(-256, 256), lhrandom(-256, 256) + 80, cl.time, 0, 0, 0, 0, 0);
705 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
706 for (i = 0;i < 256 * cl_particles_quality.value;i++)
707 particle(pt_static, PARTICLE_SPARK, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, 1.5f, 0.05f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 512, 9999, 1, 0, org[0], org[1], org[2], lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192) + 160, 0, 0, 0, 0, 0.2, 0);
711 if (cl_particles_explosions_shell.integer)
717 CL_ParticleExplosion2
721 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
727 if (!cl_particles.integer) return;
729 for (i = 0;i < 512 * cl_particles_quality.value;i++)
731 VectorRandom (offset);
732 VectorScale (offset, 192, vel);
733 VectorScale (offset, 8, offset);
734 k = particlepalette[colorStart + (i % colorLength)];
735 pscale = lhrandom(0.5, 1.5);
736 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, pscale, pscale, (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*512, 9999, 0, 0, org[0] + offset[0], org[1] + offset[1], org[2] + offset[2], vel[0], vel[1], vel[2], 0, 0, 0, 0, lhrandom(1.5, 3), 0);
746 void CL_BlobExplosion (vec3_t org)
748 CL_ParticleExplosion(org);
757 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
763 CL_ParticleExplosion(org);
766 if (!cl_particles.integer) return;
767 count *= cl_particles_quality.value;
770 k = particlepalette[color + (rand()&7)];
771 if (gamemode == GAME_GOODVSBAD2)
772 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ALPHA, 5, 5, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 300, 9999, 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, 0, 0, 0, 0, 0);
774 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ALPHA, 1, 1, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 512, 9999, 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, 0, 0, 0, 0, 0);
778 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
784 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
789 if (cl_stainmaps.integer)
790 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
791 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
793 if (!cl_particles.integer) return;
795 if (cl_particles_bulletimpacts.integer)
798 if (cl_particles_smoke.integer)
800 k = count * 0.25 * cl_particles_quality.value;
803 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
804 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
805 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
806 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
807 particle(pt_grow, PARTICLE_BILLBOARD, 0x101010, 0x202020, tex_smoke[rand()&7], true, PBLEND_ADD, 3, 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 9999, -0.2, 0, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 16), 15, 0, 0, 0, 0.2, 0);
811 if (cl_particles_sparks.integer)
814 count *= cl_particles_quality.value;
817 k = particlepalette[0x68 + (rand() & 7)];
818 particle(pt_static, PARTICLE_SPARK, k, k, tex_particle, false, PBLEND_ADD, 0.4f, 0.015f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, 9999, 1, 0, org[0], org[1], org[2], lhrandom(-64, 64) + dir[0], lhrandom(-64, 64) + dir[1], lhrandom(0, 128) + dir[2], 0, 0, 0, 0, 0.2, 0);
824 void CL_PlasmaBurn (vec3_t org)
826 if (cl_stainmaps.integer)
827 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
828 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
831 static float bloodcount = 0;
832 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
836 // bloodcount is used to accumulate counts too small to cause a blood particle
837 if (!cl_particles.integer) return;
838 if (!cl_particles_blood.integer) return;
845 while(bloodcount > 0)
847 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
848 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
849 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
850 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
851 particle(pt_blood, PARTICLE_BILLBOARD, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], true, PBLEND_MOD, 8, 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 9999, 0, -1, org3[0], org3[1], org3[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 0, 0, 0, 0, 1, 0);
852 bloodcount -= 16 / cl_particles_quality.value;
856 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
858 vec3_t org, vel, diff, center, velscale;
859 if (!cl_particles.integer) return;
860 if (!cl_particles_bloodshowers.integer) return;
861 if (!cl_particles_blood.integer) return;
863 VectorSubtract(maxs, mins, diff);
864 center[0] = (mins[0] + maxs[0]) * 0.5;
865 center[1] = (mins[1] + maxs[1]) * 0.5;
866 center[2] = (mins[2] + maxs[2]) * 0.5;
867 velscale[0] = velspeed * 2.0 / diff[0];
868 velscale[1] = velspeed * 2.0 / diff[1];
869 velscale[2] = velspeed * 2.0 / diff[2];
871 bloodcount += count * 5.0f;
872 while (bloodcount > 0)
874 org[0] = lhrandom(mins[0], maxs[0]);
875 org[1] = lhrandom(mins[1], maxs[1]);
876 org[2] = lhrandom(mins[2], maxs[2]);
877 vel[0] = (org[0] - center[0]) * velscale[0];
878 vel[1] = (org[1] - center[1]) * velscale[1];
879 vel[2] = (org[2] - center[2]) * velscale[2];
880 bloodcount -= 16 / cl_particles_quality.value;
881 particle(pt_blood, PARTICLE_BILLBOARD, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], true, PBLEND_MOD, 8, 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 9999, 0, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 0, 0, 0, 0, 1, 0);
885 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
889 if (!cl_particles.integer) return;
890 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
891 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
892 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
894 count *= cl_particles_quality.value;
897 k = particlepalette[colorbase + (rand()&3)];
898 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ALPHA, 2, 2, 255 / cl_particles_quality.value, 0, lhrandom(1, 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, 0, 0, 0, 0, 0);
902 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
905 float t, z, minz, maxz;
906 if (!cl_particles.integer) return;
907 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
908 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
909 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
910 if (dir[2] < 0) // falling
912 t = (maxs[2] - mins[2]) / -dir[2];
917 t = (maxs[2] - mins[2]) / dir[2];
920 if (t < 0 || t > 2) // sanity check
923 minz = z - fabs(dir[2]) * 0.1;
924 maxz = z + fabs(dir[2]) * 0.1;
925 minz = bound(mins[2], minz, maxs[2]);
926 maxz = bound(mins[2], maxz, maxs[2]);
928 count *= cl_particles_quality.value;
933 count *= 4; // ick, this should be in the mod or maps?
937 k = particlepalette[colorbase + (rand()&3)];
938 if (gamemode == GAME_GOODVSBAD2)
940 particle(pt_rain, PARTICLE_SPARK, k, k, tex_particle, true, PBLEND_ADD, 20, 20, lhrandom(8, 16) / cl_particles_quality.value, 0, t, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], cl.time + 9999, dir[0], dir[1], dir[2], 0, 0);
944 particle(pt_rain, PARTICLE_SPARK, k, k, tex_particle, true, PBLEND_ADD, 0.5, 0.02, lhrandom(8, 16) / cl_particles_quality.value, 0, t, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], cl.time + 9999, dir[0], dir[1], dir[2], 0, 0);
951 k = particlepalette[colorbase + (rand()&3)];
952 if (gamemode == GAME_GOODVSBAD2)
954 particle(pt_rain, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, 20, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, t, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, dir[0], dir[1], dir[2], 0, 0);
958 particle(pt_rain, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, 1, 1, lhrandom(64, 128) / cl_particles_quality.value, 0, t, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, dir[0], dir[1], dir[2], 0, 0);
963 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
967 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
972 if (!cl_particles.integer) return;
974 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
975 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
976 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
978 center[0] = (mins[0] + maxs[0]) * 0.5f;
979 center[1] = (mins[1] + maxs[1]) * 0.5f;
980 center[2] = (mins[2] + maxs[2]) * 0.5f;
982 count *= cl_particles_quality.value;
985 k = particlepalette[224 + (rand()&15)];
986 o[0] = lhrandom(mins[0], maxs[0]);
987 o[1] = lhrandom(mins[1], maxs[1]);
988 o[2] = lhrandom(mins[2], maxs[2]);
989 VectorSubtract(o, center, v);
990 VectorNormalizeFast(v);
991 VectorScale(v, 100, v);
992 v[2] += sv_gravity.value * 0.15f;
993 particle(pt_static, PARTICLE_BILLBOARD, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, 1.5, 1.5, lhrandom(64, 128) / cl_particles_quality.value, 128 / cl_particles_quality.value, 9999, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0, 0, 0, 0, 0.2, 0);
997 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
1001 if (!cl_particles.integer) return;
1002 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1003 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1004 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1006 count *= cl_particles_quality.value;
1009 k = particlepalette[224 + (rand()&15)];
1010 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, 4, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, 9999, -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), 0, 0, 0, 0, 1, 0);
1012 particle(pt_static, PARTICLE_BILLBOARD, 0x303030, 0x606060, tex_smoke[rand()&7], false, PBLEND_ADD, 6, 6, lhrandom(48, 96) / cl_particles_quality.value, 64 / cl_particles_quality.value, 9999, 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, 0, 0, 0, 0, 0);
1016 void CL_Flames (vec3_t org, vec3_t vel, int count)
1019 if (!cl_particles.integer) return;
1021 count *= cl_particles_quality.value;
1024 k = particlepalette[224 + (rand()&15)];
1025 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, 4, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, 9999, -1, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 0, 0, 0, 0, 1, 0);
1037 void CL_LavaSplash (vec3_t origin)
1039 float i, j, inc, vel;
1042 if (!cl_particles.integer) return;
1044 inc = 32 / cl_particles_quality.value;
1045 for (i = -128;i < 128;i += inc)
1047 for (j = -128;j < 128;j += inc)
1049 dir[0] = j + lhrandom(0, 8);
1050 dir[1] = i + lhrandom(0, 8);
1052 org[0] = origin[0] + dir[0];
1053 org[1] = origin[1] + dir[1];
1054 org[2] = origin[2] + lhrandom(0, 64);
1055 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1056 if (gamemode == GAME_GOODVSBAD2)
1058 k = particlepalette[0 + (rand()&255)];
1059 l = particlepalette[0 + (rand()&255)];
1060 particle(pt_static, PARTICLE_BILLBOARD, k, l, tex_particle, false, PBLEND_ADD, 12, 12, inc * 8, inc * 8, 9999, 0.05, 1, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, 0, 0);
1064 k = l = particlepalette[224 + (rand()&7)];
1065 particle(pt_static, PARTICLE_BILLBOARD, k, l, tex_particle, false, PBLEND_ADD, 12, 12, inc * 8, inc * 8, 9999, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, 0, 0);
1078 void R_TeleportSplash (vec3_t org)
1081 if (!cl_particles.integer) return;
1083 inc = 8 / cl_particles_quality.value;
1084 for (i = -16;i < 16;i += inc)
1085 for (j = -16;j < 16;j += inc)
1086 for (k = -24;k < 32;k += inc)
1087 particle(pt_static, PARTICLE_BILLBOARD, 0xA0A0A0, 0xFFFFFF, tex_particle, false, PBLEND_ADD, 10, 10, inc * 32, inc * lhrandom(8, 16), inc * 32, 9999, 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), 0, 0, 0, 0, 1, 0);
1091 #ifdef WORKINGLQUAKE
1092 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1094 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1097 vec3_t vec, dir, vel, pos;
1098 float len, dec, speed, qd;
1099 int contents, smoke, blood, bubbles;
1101 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1104 VectorSubtract(end, start, dir);
1105 VectorNormalize(dir);
1107 VectorSubtract (end, start, vec);
1108 #ifdef WORKINGLQUAKE
1109 len = VectorNormalize (vec);
1111 speed = 1.0f / cl.frametime;
1112 VectorSubtract(end, start, vel);
1114 len = VectorNormalizeLength (vec);
1115 dec = -ent->persistent.trail_time;
1116 ent->persistent.trail_time += len;
1117 if (ent->persistent.trail_time < 0.01f)
1120 // if we skip out, leave it reset
1121 ent->persistent.trail_time = 0.0f;
1123 speed = ent->state_current.time - ent->state_previous.time;
1125 speed = 1.0f / speed;
1126 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1128 VectorScale(vel, speed, vel);
1130 // advance into this frame to reach the first puff location
1131 VectorMA(start, dec, vec, pos);
1134 contents = CL_PointQ1Contents(pos);
1135 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1138 smoke = cl_particles.integer && cl_particles_smoke.integer;
1139 blood = cl_particles.integer && cl_particles_blood.integer;
1140 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1141 qd = 1.0f / cl_particles_quality.value;
1147 case 0: // rocket trail
1151 particle(pt_grow, PARTICLE_BILLBOARD, 0x303030, 0x606060, tex_smoke[rand()&7], false, PBLEND_ADD, 3, 3, qd*cl_particles_smoke_alpha.value*125, qd*cl_particles_smoke_alphafade.value*125, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 7, 0, 0, 0, 0, 0);
1152 particle(pt_static, PARTICLE_BILLBOARD, 0x801010, 0xFFA020, tex_smoke[rand()&7], false, PBLEND_ADD, 3, 3, qd*cl_particles_smoke_alpha.value*288, qd*cl_particles_smoke_alphafade.value*1400, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-20, 20), lhrandom(-20, 20), lhrandom(-20, 20), 0, 0, 0, 0, 0, 0);
1155 particle(pt_bubble, PARTICLE_BILLBOARD, 0x404040, 0x808080, tex_bubble, false, PBLEND_ADD, 2, 2, qd*lhrandom(64, 255), qd*256, 9999, -0.25, 1.5, pos[0], pos[1], pos[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), 0, 0, 0, 0, (1.0 / 16.0), 0);
1158 case 1: // grenade trail
1159 // FIXME: make it gradually stop smoking
1162 particle(pt_grow, PARTICLE_BILLBOARD, 0x303030, 0x606060, tex_smoke[rand()&7], false, PBLEND_ADD, 3, 3, qd*cl_particles_smoke_alpha.value*100, qd*cl_particles_smoke_alphafade.value*100, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 7, 0, 0, 0, 0, 0);
1167 case 4: // slight blood
1170 particle(pt_blood, PARTICLE_BILLBOARD, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], true, PBLEND_MOD, 8, 8, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 9999, 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), 0, 0, 0, 0, 1, 0);
1173 case 3: // green tracer
1177 if (gamemode == GAME_GOODVSBAD2)
1178 particle(pt_static, PARTICLE_BILLBOARD, 0x00002E, 0x000030, tex_particle, false, PBLEND_ADD, 6, 6, qd*128, qd*384, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0, 0, 0, 0, 0, 0);
1180 particle(pt_static, PARTICLE_BILLBOARD, 0x002000, 0x003000, tex_particle, false, PBLEND_ADD, 6, 6, qd*128, qd*384, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0, 0, 0, 0, 0, 0);
1184 case 5: // flame tracer
1187 particle(pt_static, PARTICLE_BILLBOARD, 0x301000, 0x502000, tex_particle, false, PBLEND_ADD, 6, 6, qd*128, qd*384, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0, 0, 0, 0, 0, 0);
1190 case 6: // voor trail
1194 if (gamemode == GAME_GOODVSBAD2)
1195 particle(pt_static, PARTICLE_BILLBOARD, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, false, PBLEND_ALPHA, 6, 6, qd*255, qd*384, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0, 0, 0, 0, 0, 0);
1196 else if (gamemode == GAME_PRYDON)
1197 particle(pt_static, PARTICLE_BILLBOARD, 0x103040, 0x204050, tex_particle, false, PBLEND_ADD, 6, 6, qd*128, qd*384, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0, 0, 0, 0, 0, 0);
1199 particle(pt_static, PARTICLE_BILLBOARD, 0x502030, 0x502030, tex_particle, false, PBLEND_ADD, 6, 6, qd*128, qd*384, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0, 0, 0, 0, 0, 0);
1202 #ifndef WORKINGLQUAKE
1203 case 7: // Nehahra smoke tracer
1206 particle(pt_static, PARTICLE_BILLBOARD, 0x303030, 0x606060, tex_smoke[rand()&7], true, PBLEND_ALPHA, 7, 7, qd*64, qd*320, 9999, 0, 0, pos[0], pos[1], pos[2], lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(0, 16), 0, 0, 0, 0, 0, 0);
1208 case 8: // Nexuiz plasma trail
1211 particle(pt_static, PARTICLE_BILLBOARD, 0x283880, 0x283880, tex_particle, false, PBLEND_ADD, 4, 4, qd*255, qd*1024, 9999, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1213 case 9: // glow trail
1216 particle(pt_static, PARTICLE_BILLBOARD, color, color, tex_particle, false, PBLEND_ALPHA, 5, 5, qd*128, qd*320, 9999, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1221 // advance to next time and position
1223 VectorMA (pos, dec, vec, pos);
1225 #ifndef WORKINGLQUAKE
1226 ent->persistent.trail_time = len;
1230 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1232 int tempcolor2, cr, cg, cb;
1236 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1237 particle(pt_static, PARTICLE_BEAM, tempcolor2, tempcolor2, tex_beam, false, PBLEND_ADD, radius, radius, alpha * 255, alpha * 255 / lifetime, 9999, 0, 0, start[0], start[1], start[2], 0, 0, 0, 0, end[0], end[1], end[2], 0, 0);
1240 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1243 if (!cl_particles.integer) return;
1246 if (cl_particles_smoke.integer)
1247 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1248 particle(pt_grow, PARTICLE_BILLBOARD, 0x202020, 0x404040, tex_smoke[rand()&7], true, PBLEND_ADD, 5, 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 9999, 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, 15, 0, 0, 0, 0, 0);
1251 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1254 if (!cl_particles.integer) return;
1256 if (cl_stainmaps.integer)
1257 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1258 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1261 if (cl_particles_smoke.integer)
1262 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1263 particle(pt_grow, PARTICLE_BILLBOARD, 0x202020, 0x404040, tex_smoke[rand()&7], true, PBLEND_ADD, 5, 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 9999, 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), 15, 0, 0, 0, 0, 0);
1266 if (cl_particles_sparks.integer)
1267 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1268 particle(pt_static, PARTICLE_SPARK, 0x2030FF, 0x80C0FF, tex_particle, false, PBLEND_ADD, 2.0f, 0.1f, lhrandom(64, 255) / cl_particles_quality.value, 512 / cl_particles_quality.value, 9999, 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, 0, 0, 0, 0, 0);
1276 void CL_MoveParticles (void)
1279 int i, maxparticle, j, a, content;
1280 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1281 #ifdef WORKINGLQUAKE
1284 entity_render_t *hitent;
1287 // LordHavoc: early out condition
1288 if (!cl_numparticles)
1290 cl_freeparticle = 0;
1294 #ifdef WORKINGLQUAKE
1295 frametime = cl.frametime;
1297 frametime = cl.time - cl.oldtime;
1299 gravity = frametime * sv_gravity.value;
1300 dvel = 1+4*frametime;
1301 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1305 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1311 VectorCopy(p->org, p->oldorg);
1312 VectorMA(p->org, frametime, p->vel, p->org);
1313 VectorCopy(p->org, org);
1316 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1318 VectorCopy(v, p->org);
1321 // assume it's blood (lame, but...)
1322 #ifndef WORKINGLQUAKE
1323 if (cl_stainmaps.integer)
1324 R_Stain(v, 32, 32, 16, 16, p->alpha * p->scalex * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->scalex * (1.0f / 40.0f));
1326 if (!cl_decals.integer)
1333 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1334 // convert from a blood particle to a blood decal
1335 p->texnum = tex_blooddecal[rand()&7];
1336 #ifndef WORKINGLQUAKE
1338 p->ownermodel = hitent->model;
1339 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1340 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1341 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1343 p->time2 = cl.time + cl_decals_time.value;
1344 p->die = p->time2 + cl_decals_fadetime.value;
1346 VectorCopy(normal, p->vel2);
1347 VectorClear(p->vel);
1348 VectorAdd(p->org, normal, p->org);
1357 dist = DotProduct(p->vel, normal) * -p->bounce;
1358 VectorMA(p->vel, dist, normal, p->vel);
1359 if (DotProduct(p->vel, p->vel) < 0.03)
1360 VectorClear(p->vel);
1365 p->vel[2] -= p->gravity * gravity;
1367 p->alpha -= p->alphafade * frametime;
1369 if (p->alpha <= 0 || cl.time > p->die)
1377 f = p->friction * frametime;
1379 content = CL_PointQ1Contents(p->org);
1380 if (content != CONTENTS_EMPTY)
1383 VectorScale(p->vel, f, p->vel);
1386 if (p->type != pt_static)
1392 content = CL_PointQ1Contents(p->org);
1394 if (a != CONTENTS_EMPTY)
1396 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1398 p->scalex += frametime * 8;
1399 p->scaley += frametime * 8;
1400 //p->alpha -= bloodwaterfade;
1406 p->vel[2] -= gravity;
1410 content = CL_PointQ1Contents(p->org);
1411 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1418 if (cl.time > p->time2)
1421 p->time2 = cl.time + (rand() & 3) * 0.1;
1422 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1423 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1424 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1427 content = CL_PointQ1Contents(p->org);
1429 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1433 p->scalex += frametime * p->time2;
1434 p->scaley += frametime * p->time2;
1437 #ifndef WORKINGLQUAKE
1438 if (p->owner->model == p->ownermodel)
1440 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1441 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1442 if (cl.time > p->time2)
1444 p->alphafade = p->alpha / (p->die - cl.time);
1445 p->type = pt_decalfade;
1453 #ifndef WORKINGLQUAKE
1454 if (p->owner->model == p->ownermodel)
1456 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1457 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1464 while (cl.time > p->time2)
1467 particle(pt_static, PARTICLE_SPARK, 0x903010, 0xFFD030, tex_particle, false, PBLEND_ADD, p->scalex * 0.75, p->scaley * 0.75, p->alpha, p->alphafade, 9999, 0.5, 0, p->org[0], p->org[1], p->org[2], p->vel[0] * lhrandom(0.4, 0.6), p->vel[1] * lhrandom(0.4, 0.6), p->vel[2] * lhrandom(0.4, 0.6), 0, 0, 0, 0, 0, 0);
1471 Con_Printf("unknown particle type %i\n", p->type);
1477 cl_numparticles = maxparticle + 1;
1478 cl_freeparticle = 0;
1481 #define MAX_PARTICLETEXTURES 64
1482 // particletexture_t is a rectangle in the particlefonttexture
1485 rtexture_t *texture;
1486 float s1, t1, s2, t2;
1491 static int particlefonttexture;
1493 static rtexturepool_t *particletexturepool;
1494 static rtexture_t *particlefonttexture;
1496 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1498 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1500 static qbyte shadebubble(float dx, float dy, vec3_t light)
1504 dz = 1 - (dx*dx+dy*dy);
1505 if (dz > 0) // it does hit the sphere
1509 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1510 VectorNormalize(normal);
1511 dot = DotProduct(normal, light);
1512 if (dot > 0.5) // interior reflection
1513 f += ((dot * 2) - 1);
1514 else if (dot < -0.5) // exterior reflection
1515 f += ((dot * -2) - 1);
1517 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1518 VectorNormalize(normal);
1519 dot = DotProduct(normal, light);
1520 if (dot > 0.5) // interior reflection
1521 f += ((dot * 2) - 1);
1522 else if (dot < -0.5) // exterior reflection
1523 f += ((dot * -2) - 1);
1525 f += 16; // just to give it a haze so you can see the outline
1526 f = bound(0, f, 255);
1533 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1535 int basex, basey, y;
1536 basex = ((texnum >> 0) & 7) * 32;
1537 basey = ((texnum >> 3) & 7) * 32;
1538 particletexture[texnum].s1 = (basex + 1) / 256.0f;
1539 particletexture[texnum].t1 = (basey + 1) / 256.0f;
1540 particletexture[texnum].s2 = (basex + 31) / 256.0f;
1541 particletexture[texnum].t2 = (basey + 31) / 256.0f;
1542 for (y = 0;y < 32;y++)
1543 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 4);
1546 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1549 float cx, cy, dx, dy, f, iradius;
1551 cx = lhrandom(radius + 1, 30 - radius);
1552 cy = lhrandom(radius + 1, 30 - radius);
1553 iradius = 1.0f / radius;
1554 alpha *= (1.0f / 255.0f);
1555 for (y = 0;y < 32;y++)
1557 for (x = 0;x < 32;x++)
1561 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1564 d = data + (y * 32 + x) * 4;
1565 d[0] += f * (red - d[0]);
1566 d[1] += f * (green - d[1]);
1567 d[2] += f * (blue - d[2]);
1573 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1576 for (i = 0;i < 32*32;i++, data += 4)
1578 data[0] = bound(minr, data[0], maxr);
1579 data[1] = bound(ming, data[1], maxg);
1580 data[2] = bound(minb, data[2], maxb);
1584 void particletextureinvert(qbyte *data)
1587 for (i = 0;i < 32*32;i++, data += 4)
1589 data[0] = 255 - data[0];
1590 data[1] = 255 - data[1];
1591 data[2] = 255 - data[2];
1595 static void R_InitParticleTexture (void)
1597 int x, y, d, i, j, k, m;
1598 float dx, dy, radius, f, f2;
1599 qbyte data[32][32][4], noise1[64][64], noise2[64][64], data2[64][16][4];
1601 qbyte particletexturedata[256*256*4];
1603 // a note: decals need to modulate (multiply) the background color to
1604 // properly darken it (stain), and they need to be able to alpha fade,
1605 // this is a very difficult challenge because it means fading to white
1606 // (no change to background) rather than black (darkening everything
1607 // behind the whole decal polygon), and to accomplish this the texture is
1608 // inverted (dark red blood on white background becomes brilliant cyan
1609 // and white on black background) so we can alpha fade it to black, then
1610 // we invert it again during the blendfunc to make it work...
1612 memset(particletexturedata, 255, sizeof(particletexturedata));
1615 for (i = 0;i < 8;i++)
1617 memset(&data[0][0][0], 255, sizeof(data));
1620 fractalnoise(&noise1[0][0], 64, 4);
1621 fractalnoise(&noise2[0][0], 64, 8);
1623 for (y = 0;y < 32;y++)
1626 for (x = 0;x < 32;x++)
1629 d = (noise2[y][x] - 128) * 3 + 192;
1631 d = d * (256 - (int) (dx*dx+dy*dy)) / 256;
1632 d = (d * noise1[y][x]) >> 7;
1633 d = bound(0, d, 255);
1634 data[y][x][3] = (qbyte) d;
1641 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1645 for (i = 0;i < 16;i++)
1647 memset(&data[0][0][0], 255, sizeof(data));
1648 radius = i * 3.0f / 16.0f;
1649 f2 = 255.0f * ((15.0f - i) / 15.0f);
1650 for (y = 0;y < 32;y++)
1652 dy = (y - 16) * 0.25f;
1653 for (x = 0;x < 32;x++)
1655 dx = (x - 16) * 0.25f;
1656 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
1657 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1660 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1664 memset(&data[0][0][0], 255, sizeof(data));
1665 for (y = 0;y < 32;y++)
1668 for (x = 0;x < 32;x++)
1671 d = (256 - (dx*dx+dy*dy));
1672 d = bound(0, d, 255);
1673 data[y][x][3] = (qbyte) d;
1676 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1679 memset(&data[0][0][0], 255, sizeof(data));
1680 light[0] = 1;light[1] = 1;light[2] = 1;
1681 VectorNormalize(light);
1682 for (y = 0;y < 32;y++)
1683 for (x = 0;x < 32;x++)
1684 data[y][x][3] = shadebubble((x - 16) * (1.0 / 8.0), y < 24 ? (y - 24) * (1.0 / 24.0) : (y - 24) * (1.0 / 8.0), light);
1685 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1688 memset(&data[0][0][0], 255, sizeof(data));
1689 light[0] = 1;light[1] = 1;light[2] = 1;
1690 VectorNormalize(light);
1691 for (y = 0;y < 32;y++)
1692 for (x = 0;x < 32;x++)
1693 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
1694 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1697 for (i = 0;i < 8;i++)
1699 memset(&data[0][0][0], 255, sizeof(data));
1700 for (k = 0;k < 24;k++)
1701 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 160);
1702 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1703 particletextureinvert(&data[0][0][0]);
1704 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1708 for (i = 0;i < 8;i++)
1710 memset(&data[0][0][0], 255, sizeof(data));
1711 for (k = 0;k < 24;k++)
1712 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 96);
1713 for (j = 3;j < 7;j++)
1714 for (k = 0, m = rand() % 12;k < m;k++)
1715 particletextureblotch(&data[0][0][0], j, 96, 0, 0, 192);
1716 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1717 particletextureinvert(&data[0][0][0]);
1718 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1722 for (i = 0;i < 8;i++)
1724 memset(&data[0][0][0], 255, sizeof(data));
1725 for (k = 0;k < 12;k++)
1726 particletextureblotch(&data[0][0][0], 2, 0, 0, 0, 128);
1727 for (k = 0;k < 3;k++)
1728 particletextureblotch(&data[0][0][0], 14, 0, 0, 0, 160);
1729 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1730 particletextureinvert(&data[0][0][0]);
1731 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1735 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1736 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1737 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1739 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1740 if (!particlefonttexture)
1741 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1742 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1743 particletexture[i].texture = particlefonttexture;
1746 fractalnoise(&noise1[0][0], 64, 4);
1748 for (y = 0;y < 64;y++)
1750 for (x = 0;x < 16;x++)
1756 d = d * d * noise1[y][x] / (7 * 7);
1757 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1758 data2[y][x][3] = 255;
1762 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1763 if (!particletexture[tex_beam].texture)
1764 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1765 particletexture[tex_beam].s1 = 0;
1766 particletexture[tex_beam].t1 = 0;
1767 particletexture[tex_beam].s2 = 1;
1768 particletexture[tex_beam].t2 = 1;
1772 static void r_part_start(void)
1774 particletexturepool = R_AllocTexturePool();
1775 R_InitParticleTexture ();
1778 static void r_part_shutdown(void)
1780 R_FreeTexturePool(&particletexturepool);
1783 static void r_part_newmap(void)
1785 cl_numparticles = 0;
1786 cl_freeparticle = 0;
1789 void R_Particles_Init (void)
1791 Cvar_RegisterVariable(&r_drawparticles);
1792 #ifdef WORKINGLQUAKE
1795 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1799 #ifdef WORKINGLQUAKE
1800 void R_InitParticles(void)
1802 CL_Particles_Init();
1807 float particle_vertex3f[12], particle_texcoord2f[8];
1809 #ifdef WORKINGLQUAKE
1810 void R_DrawParticle(particle_t *p)
1813 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1815 const particle_t *p = calldata1;
1818 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1819 particletexture_t *tex;
1821 VectorCopy(p->org, org);
1823 tex = &particletexture[p->texnum];
1824 cr = p->color[0] * (1.0f / 255.0f);
1825 cg = p->color[1] * (1.0f / 255.0f);
1826 cb = p->color[2] * (1.0f / 255.0f);
1827 ca = p->alpha * (1.0f / 255.0f);
1828 if (p->blendmode == PBLEND_MOD)
1839 #ifndef WORKINGLQUAKE
1840 if (fogenabled && p->blendmode != PBLEND_MOD)
1842 VectorSubtract(org, r_vieworigin, fogvec);
1843 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1848 if (p->blendmode == 0)
1850 cr += fogcolor[0] * fog;
1851 cg += fogcolor[1] * fog;
1852 cb += fogcolor[2] * fog;
1856 R_Mesh_Matrix(&r_identitymatrix);
1858 memset(&m, 0, sizeof(m));
1859 m.tex[0] = R_GetTexture(tex->texture);
1860 m.pointer_texcoord[0] = particle_texcoord2f;
1861 m.pointer_vertex = particle_vertex3f;
1864 GL_Color(cr, cg, cb, ca);
1866 if (p->blendmode == 0)
1867 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1868 else if (p->blendmode == 1)
1869 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1871 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1872 GL_DepthMask(false);
1875 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1877 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1880 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1882 VectorNegate(p->vel2, v);
1883 VectorVectors(v, right, up);
1886 VectorVectors(p->vel2, right, up);
1887 VectorScale(right, p->scalex, right);
1888 VectorScale(up, p->scaley, up);
1892 VectorScale(r_viewleft, -p->scalex, right);
1893 VectorScale(r_viewup, p->scaley, up);
1895 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1896 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1897 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1898 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1899 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1900 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1901 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1902 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1903 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1904 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1905 particle_vertex3f[10] = org[1] + right[1] - up[1];
1906 particle_vertex3f[11] = org[2] + right[2] - up[2];
1907 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1908 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1909 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1910 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1912 else if (p->orientation == PARTICLE_SPARK)
1914 VectorMA(p->org, -p->scaley, p->vel, v);
1915 VectorMA(p->org, p->scaley, p->vel, up2);
1916 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1917 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1918 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1919 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1920 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1922 else if (p->orientation == PARTICLE_BEAM)
1924 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1925 VectorSubtract(p->vel2, p->org, up);
1926 VectorNormalizeFast(up);
1927 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1928 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1929 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1930 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1931 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1932 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1935 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1938 if (p->blendmode == 0)
1939 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1940 else if (p->blendmode == 1)
1941 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1943 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1944 glColor4f(cr, cg, cb, ca);
1946 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1947 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1948 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1949 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1952 R_Mesh_Draw(4, 2, polygonelements);
1956 void R_DrawParticles (void)
1959 float minparticledist;
1962 #ifdef WORKINGLQUAKE
1966 // LordHavoc: early out conditions
1967 if ((!cl_numparticles) || (!r_drawparticles.integer))
1970 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1972 #ifdef WORKINGLQUAKE
1973 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1975 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1977 // LordHavoc: only render if not too close
1978 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1979 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
1982 glDisable(GL_BLEND);
1983 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1985 // LordHavoc: only render if not too close
1986 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1991 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
1992 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);