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_raindecal, pt_snow, pt_bubble, pt_blood, pt_grow, pt_decal, 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 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
331 i = COM_CheckParm ("-particles");
333 if (i && i < com_argc - 1)
335 cl_maxparticles = (int)(atoi(com_argv[i+1]));
336 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
337 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
340 cl_maxparticles = MAX_PARTICLES;
342 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
344 Cvar_RegisterVariable (&cl_particles);
345 Cvar_RegisterVariable (&cl_particles_quality);
346 Cvar_RegisterVariable (&cl_particles_size);
347 Cvar_RegisterVariable (&cl_particles_bloodshowers);
348 Cvar_RegisterVariable (&cl_particles_blood);
349 Cvar_RegisterVariable (&cl_particles_blood_alpha);
350 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
351 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
352 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
353 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
354 Cvar_RegisterVariable (&cl_particles_explosions_shell);
355 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
356 Cvar_RegisterVariable (&cl_particles_smoke);
357 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
358 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
359 Cvar_RegisterVariable (&cl_particles_sparks);
360 Cvar_RegisterVariable (&cl_particles_bubbles);
361 Cvar_RegisterVariable (&cl_decals);
362 Cvar_RegisterVariable (&cl_decals_time);
363 Cvar_RegisterVariable (&cl_decals_fadetime);
366 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
368 cl_part_mempool = Mem_AllocPool("CL_Part", 0, NULL);
369 particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
371 CL_Particles_Clear();
374 void CL_Particles_Shutdown (void)
377 // No clue what to do here...
379 Mem_FreePool (&cl_part_mempool);
383 // list of all 26 parameters:
384 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
385 // porientation - PARTICLE_ enum values (PARTICLE_BILLBOARD, PARTICLE_SPARK, etc)
386 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
387 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
388 // plight - no longer used (this used to turn on particle lighting)
389 // pblendmode - PBLEND_ enum values (PBLEND_ALPHA, PBLEND_ADD, etc)
390 // pscalex,pscaley - width and height of particle (according to orientation), these are normally the same except when making sparks and beams
391 // palpha - opacity of particle as 0-255 (can be more than 255)
392 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
393 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
394 // pgravity - how much effect gravity has on the particle (0-1)
395 // 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
396 // px,py,pz - starting origin of particle
397 // pvx,pvy,pvz - starting velocity of particle
398 // ptime2 - extra time parameter for certain particle types (pt_decal delayed fades and pt_rain snowflutter use this)
399 // pvx2,pvy2,pvz2 - for PARTICLE_ORIENTED_DOUBLESIDED this is the surface normal of the orientation (forward vector), pt_rain uses this for snow fluttering
400 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
401 // 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
402 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)
405 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
406 ptempcolor = (pcolor1);
407 ptempcolor2 = (pcolor2);
408 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
409 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
410 pcb2 = (ptempcolor2) & 0xFF;
411 if (ptempcolor != ptempcolor2)
413 pcr1 = ((ptempcolor) >> 16) & 0xFF;
414 pcg1 = ((ptempcolor) >> 8) & 0xFF;
415 pcb1 = (ptempcolor) & 0xFF;
416 ptempcolor = rand() & 0xFF;
417 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
418 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
419 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
421 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
422 if (cl_freeparticle >= cl_maxparticles)
424 part = &particles[cl_freeparticle++];
425 if (cl_numparticles < cl_freeparticle)
426 cl_numparticles = cl_freeparticle;
427 memset(part, 0, sizeof(*part));
428 part->type = (ptype);
429 part->color[0] = pcr2;
430 part->color[1] = pcg2;
431 part->color[2] = pcb2;
432 part->color[3] = 0xFF;
433 part->orientation = porientation;
435 part->blendmode = pblendmode;
436 part->scalex = (pscalex);
437 part->scaley = (pscaley);
438 part->alpha = (palpha);
439 part->alphafade = (palphafade);
440 part->die = cl.time + (ptime);
441 part->gravity = (pgravity);
442 part->bounce = (pbounce);
446 part->vel[0] = (pvx);
447 part->vel[1] = (pvy);
448 part->vel[2] = (pvz);
449 part->time2 = (ptime2);
450 part->vel2[0] = (pvx2);
451 part->vel2[1] = (pvy2);
452 part->vel2[2] = (pvz2);
453 part->friction = (pfriction);
454 part->pressure = (ppressure);
458 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
461 if (!cl_decals.integer)
463 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);
464 #ifndef WORKINGLQUAKE
468 p->ownermodel = p->owner->model;
469 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
470 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
471 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
476 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
479 float bestfrac, bestorg[3], bestnormal[3];
480 float frac, v[3], normal[3], org2[3];
482 void *besthitent = NULL, *hitent;
484 entity_render_t *besthitent = NULL, *hitent;
487 for (i = 0;i < 32;i++)
490 VectorMA(org, maxdist, org2, org2);
491 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
496 VectorCopy(v, bestorg);
497 VectorCopy(normal, bestnormal);
501 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
509 void CL_EntityParticles (entity_t *ent)
513 float sp, sy, cp, cy;
517 static vec3_t avelocities[NUMVERTEXNORMALS];
518 if (!cl_particles.integer) return;
523 if (!avelocities[0][0])
524 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
525 avelocities[0][i] = (rand()&255) * 0.01;
527 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
529 angle = cl.time * avelocities[i][0];
532 angle = cl.time * avelocities[i][1];
541 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);
543 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);
549 void CL_ReadPointFile_f (void)
553 char *pointfile = NULL, *pointfilepos, *t, tchar;
554 char name[MAX_OSPATH];
559 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
560 strlcat (name, ".pts", sizeof (name));
562 pointfile = COM_LoadTempFile (name);
564 pointfile = FS_LoadFile(name, tempmempool, true);
568 Con_Printf("Could not open %s\n", name);
572 Con_Printf("Reading %s...\n", name);
575 pointfilepos = pointfile;
576 while (*pointfilepos)
578 while (*pointfilepos == '\n' || *pointfilepos == '\r')
583 while (*t && *t != '\n' && *t != '\r')
587 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
593 VectorCopy(org, leakorg);
596 if (cl_numparticles < cl_maxparticles - 3)
599 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);
602 #ifndef WORKINGLQUAKE
605 VectorCopy(leakorg, org);
606 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
608 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);
609 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);
610 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);
615 CL_ParseParticleEffect
617 Parse an effect out of the server message
620 void CL_ParseParticleEffect (void)
623 int i, count, msgcount, color;
625 MSG_ReadVector(org, cl.protocol);
626 for (i=0 ; i<3 ; i++)
627 dir[i] = MSG_ReadChar () * (1.0/16);
628 msgcount = MSG_ReadByte ();
629 color = MSG_ReadByte ();
636 if (cl_particles_blood_bloodhack.integer)
641 CL_BloodPuff(org, dir, count / 2);
647 CL_BloodPuff(org, dir, count / 2);
651 CL_RunParticleEffect (org, dir, color, count);
660 void CL_ParticleExplosion (vec3_t org)
665 if (cl_stainmaps.integer)
666 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
667 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
669 i = CL_PointSuperContents(org);
670 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
672 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
673 for (i = 0;i < 128 * cl_particles_quality.value;i++)
674 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);
678 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
680 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
682 for (i = 0;i < 32;i++)
687 v2[0] = lhrandom(-48, 48);
688 v2[1] = lhrandom(-48, 48);
689 v2[2] = lhrandom(-48, 48);
691 for (k = 0;k < 16;k++)
693 v[0] = org[0] + lhrandom(-48, 48);
694 v[1] = org[1] + lhrandom(-48, 48);
695 v[2] = org[2] + lhrandom(-48, 48);
696 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
699 VectorSubtract(v2, org, v2);
701 VectorScale(v2, 2.0f, v2);
702 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);
707 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
708 for (i = 0;i < 128 * cl_particles_quality.value;i++)
709 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);
711 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
712 for (i = 0;i < 64 * cl_particles_quality.value;i++)
713 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);
715 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
716 for (i = 0;i < 256 * cl_particles_quality.value;i++)
717 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);
721 if (cl_particles_explosions_shell.integer)
727 CL_ParticleExplosion2
731 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
737 if (!cl_particles.integer) return;
739 for (i = 0;i < 512 * cl_particles_quality.value;i++)
741 VectorRandom (offset);
742 VectorScale (offset, 192, vel);
743 VectorScale (offset, 8, offset);
744 k = particlepalette[colorStart + (i % colorLength)];
745 pscale = lhrandom(0.5, 1.5);
746 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);
756 void CL_BlobExplosion (vec3_t org)
758 CL_ParticleExplosion(org);
767 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
773 CL_ParticleExplosion(org);
776 if (!cl_particles.integer) return;
777 count *= cl_particles_quality.value;
780 k = particlepalette[color + (rand()&7)];
781 if (gamemode == GAME_GOODVSBAD2)
782 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);
784 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);
788 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
794 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
798 if (!cl_particles.integer) return;
800 if (cl_particles_sparks.integer)
803 count *= cl_particles_quality.value;
806 k = particlepalette[0x68 + (rand() & 7)];
807 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, gravityscale, 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, 0);
812 void CL_Smoke (vec3_t org, vec3_t dir, int count)
817 if (!cl_particles.integer) return;
820 if (cl_particles_smoke.integer)
822 k = count * 0.25 * cl_particles_quality.value;
825 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
826 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
827 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
828 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
829 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, 0, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 15, 0, 0, 0, 0, 0);
834 void CL_BulletMark (vec3_t org)
836 if (cl_stainmaps.integer)
837 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
838 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
841 void CL_PlasmaBurn (vec3_t org)
843 if (cl_stainmaps.integer)
844 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
845 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
848 static float bloodcount = 0;
849 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
853 // bloodcount is used to accumulate counts too small to cause a blood particle
854 if (!cl_particles.integer) return;
855 if (!cl_particles_blood.integer) return;
862 while(bloodcount > 0)
864 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
865 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
866 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
867 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
868 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);
869 bloodcount -= 16 / cl_particles_quality.value;
873 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
875 vec3_t org, vel, diff, center, velscale;
876 if (!cl_particles.integer) return;
877 if (!cl_particles_bloodshowers.integer) return;
878 if (!cl_particles_blood.integer) return;
880 VectorSubtract(maxs, mins, diff);
881 center[0] = (mins[0] + maxs[0]) * 0.5;
882 center[1] = (mins[1] + maxs[1]) * 0.5;
883 center[2] = (mins[2] + maxs[2]) * 0.5;
884 velscale[0] = velspeed * 2.0 / diff[0];
885 velscale[1] = velspeed * 2.0 / diff[1];
886 velscale[2] = velspeed * 2.0 / diff[2];
888 bloodcount += count * 5.0f;
889 while (bloodcount > 0)
891 org[0] = lhrandom(mins[0], maxs[0]);
892 org[1] = lhrandom(mins[1], maxs[1]);
893 org[2] = lhrandom(mins[2], maxs[2]);
894 vel[0] = (org[0] - center[0]) * velscale[0];
895 vel[1] = (org[1] - center[1]) * velscale[1];
896 vel[2] = (org[2] - center[2]) * velscale[2];
897 bloodcount -= 16 / cl_particles_quality.value;
898 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);
902 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
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;}
911 count *= cl_particles_quality.value;
914 k = particlepalette[colorbase + (rand()&3)];
915 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);
919 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
922 float t, z, minz, maxz;
923 if (!cl_particles.integer) return;
924 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
925 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
926 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
927 if (dir[2] < 0) // falling
929 t = (maxs[2] - mins[2]) / -dir[2];
934 t = (maxs[2] - mins[2]) / dir[2];
937 if (t < 0 || t > 2) // sanity check
940 minz = z - fabs(dir[2]) * 0.1;
941 maxz = z + fabs(dir[2]) * 0.1;
942 minz = bound(mins[2], minz, maxs[2]);
943 maxz = bound(mins[2], maxz, maxs[2]);
945 count *= cl_particles_quality.value;
950 count *= 4; // ick, this should be in the mod or maps?
954 k = particlepalette[colorbase + (rand()&3)];
955 if (gamemode == GAME_GOODVSBAD2)
957 particle(pt_rain, PARTICLE_SPARK, k, k, tex_particle, true, PBLEND_ADD, 20, 20, lhrandom(8, 16) / cl_particles_quality.value, 0, t, 0, -1, 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);
961 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, -1, 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);
968 k = particlepalette[colorbase + (rand()&3)];
969 if (gamemode == GAME_GOODVSBAD2)
971 particle(pt_snow, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, 20, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, t, 0, -1, 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);
975 particle(pt_snow, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ADD, 1, 1, lhrandom(64, 128) / cl_particles_quality.value, 0, t, 0, -1, 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);
980 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
984 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
989 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 center[0] = (mins[0] + maxs[0]) * 0.5f;
996 center[1] = (mins[1] + maxs[1]) * 0.5f;
997 center[2] = (mins[2] + maxs[2]) * 0.5f;
999 count *= cl_particles_quality.value;
1002 k = particlepalette[224 + (rand()&15)];
1003 o[0] = lhrandom(mins[0], maxs[0]);
1004 o[1] = lhrandom(mins[1], maxs[1]);
1005 o[2] = lhrandom(mins[2], maxs[2]);
1006 VectorSubtract(o, center, v);
1007 VectorNormalizeFast(v);
1008 VectorScale(v, 100, v);
1009 v[2] += sv_gravity.value * 0.15f;
1010 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);
1014 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
1018 if (!cl_particles.integer) return;
1019 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1020 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1021 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1023 count *= cl_particles_quality.value;
1026 k = particlepalette[224 + (rand()&15)];
1027 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);
1029 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);
1033 void CL_Flames (vec3_t org, vec3_t vel, int count)
1036 if (!cl_particles.integer) return;
1038 count *= cl_particles_quality.value;
1041 k = particlepalette[224 + (rand()&15)];
1042 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);
1054 void CL_LavaSplash (vec3_t origin)
1056 float i, j, inc, vel;
1059 if (!cl_particles.integer) return;
1061 inc = 32 / cl_particles_quality.value;
1062 for (i = -128;i < 128;i += inc)
1064 for (j = -128;j < 128;j += inc)
1066 dir[0] = j + lhrandom(0, 8);
1067 dir[1] = i + lhrandom(0, 8);
1069 org[0] = origin[0] + dir[0];
1070 org[1] = origin[1] + dir[1];
1071 org[2] = origin[2] + lhrandom(0, 64);
1072 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1073 if (gamemode == GAME_GOODVSBAD2)
1075 k = particlepalette[0 + (rand()&255)];
1076 l = particlepalette[0 + (rand()&255)];
1077 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);
1081 k = l = particlepalette[224 + (rand()&7)];
1082 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);
1095 void R_TeleportSplash (vec3_t org)
1098 if (!cl_particles.integer) return;
1100 inc = 8 / cl_particles_quality.value;
1101 for (i = -16;i < 16;i += inc)
1102 for (j = -16;j < 16;j += inc)
1103 for (k = -24;k < 32;k += inc)
1104 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);
1108 #ifdef WORKINGLQUAKE
1109 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1111 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1114 vec3_t vec, dir, vel, pos;
1115 float len, dec, speed, qd;
1116 int smoke, blood, bubbles;
1117 #ifdef WORKINGLQUAKE
1121 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1124 VectorSubtract(end, start, dir);
1125 VectorNormalize(dir);
1127 VectorSubtract (end, start, vec);
1128 #ifdef WORKINGLQUAKE
1129 len = VectorNormalize (vec);
1131 speed = 1.0f / cl.frametime;
1132 VectorSubtract(end, start, vel);
1134 len = VectorNormalizeLength (vec);
1135 dec = -ent->persistent.trail_time;
1136 ent->persistent.trail_time += len;
1137 if (ent->persistent.trail_time < 0.01f)
1140 // if we skip out, leave it reset
1141 ent->persistent.trail_time = 0.0f;
1143 speed = ent->state_current.time - ent->state_previous.time;
1145 speed = 1.0f / speed;
1146 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1147 color = particlepalette[color];
1149 VectorScale(vel, speed, vel);
1151 // advance into this frame to reach the first puff location
1152 VectorMA(start, dec, vec, pos);
1155 smoke = cl_particles.integer && cl_particles_smoke.integer;
1156 blood = cl_particles.integer && cl_particles_blood.integer;
1157 #ifdef WORKINGLQUAKE
1158 contents = CL_PointQ1Contents(pos);
1159 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1161 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1163 qd = 1.0f / cl_particles_quality.value;
1169 case 0: // rocket trail
1173 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);
1174 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);
1177 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);
1180 case 1: // grenade trail
1181 // FIXME: make it gradually stop smoking
1184 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);
1189 case 4: // slight blood
1192 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);
1195 case 3: // green tracer
1199 if (gamemode == GAME_GOODVSBAD2)
1200 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);
1202 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);
1206 case 5: // flame tracer
1209 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);
1212 case 6: // voor trail
1216 if (gamemode == GAME_GOODVSBAD2)
1217 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);
1218 else if (gamemode == GAME_PRYDON)
1219 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);
1221 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);
1224 #ifndef WORKINGLQUAKE
1225 case 7: // Nehahra smoke tracer
1228 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);
1230 case 8: // Nexuiz plasma trail
1233 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);
1235 case 9: // glow trail
1238 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);
1243 // advance to next time and position
1245 VectorMA (pos, dec, vec, pos);
1247 #ifndef WORKINGLQUAKE
1248 ent->persistent.trail_time = len;
1252 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1254 int tempcolor2, cr, cg, cb;
1258 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1259 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);
1262 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1265 if (!cl_particles.integer) return;
1268 if (cl_particles_smoke.integer)
1269 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1270 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);
1273 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1276 if (!cl_particles.integer) return;
1278 if (cl_stainmaps.integer)
1279 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1280 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1283 if (cl_particles_smoke.integer)
1284 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1285 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);
1288 if (cl_particles_sparks.integer)
1289 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1290 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);
1298 void CL_MoveParticles (void)
1301 int i, maxparticle, j, a, content;
1302 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1303 #ifdef WORKINGLQUAKE
1306 entity_render_t *hitent;
1309 // LordHavoc: early out condition
1310 if (!cl_numparticles)
1312 cl_freeparticle = 0;
1316 #ifdef WORKINGLQUAKE
1317 frametime = cl.frametime;
1319 frametime = cl.time - cl.oldtime;
1321 gravity = frametime * sv_gravity.value;
1322 dvel = 1+4*frametime;
1323 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1327 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1333 VectorCopy(p->org, p->oldorg);
1334 VectorMA(p->org, frametime, p->vel, p->org);
1335 VectorCopy(p->org, org);
1338 if (p->type == pt_rain)
1340 // raindrop - splash on solid/water/slime/lava
1341 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK) < 1)
1343 VectorCopy(v, p->org);
1345 p->type = pt_raindecal;
1346 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1347 // convert from a raindrop particle to a rainsplash decal
1348 p->texnum = tex_rainsplash[0];
1350 p->die = p->time2 + 0.4;
1351 p->alphafade = p->alpha / 0.4;
1352 VectorCopy(normal, p->vel2);
1353 VectorClear(p->vel);
1354 VectorAdd(p->org, normal, p->org);
1362 else if (p->type == pt_blood)
1364 // blood - splash on solid
1365 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1367 VectorCopy(v, p->org);
1368 #ifndef WORKINGLQUAKE
1369 if (cl_stainmaps.integer)
1370 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));
1372 if (!cl_decals.integer)
1379 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1380 // convert from a blood particle to a blood decal
1381 p->texnum = tex_blooddecal[rand()&7];
1382 #ifndef WORKINGLQUAKE
1384 p->ownermodel = hitent->model;
1385 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1386 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1387 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1390 p->die = p->time2 + cl_decals_time.value + cl_decals_fadetime.value;
1392 VectorCopy(normal, p->vel2);
1393 VectorClear(p->vel);
1394 VectorAdd(p->org, normal, p->org);
1404 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1406 VectorCopy(v, p->org);
1414 dist = DotProduct(p->vel, normal) * -p->bounce;
1415 VectorMA(p->vel, dist, normal, p->vel);
1416 if (DotProduct(p->vel, p->vel) < 0.03)
1417 VectorClear(p->vel);
1423 p->vel[2] -= p->gravity * gravity;
1425 p->alpha -= p->alphafade * frametime;
1427 if (p->alpha <= 0 || cl.time > p->die)
1435 f = p->friction * frametime;
1436 #ifdef WORKINGLQUAKE
1437 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1439 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1443 VectorScale(p->vel, f, p->vel);
1446 if (p->type != pt_static)
1451 #ifdef WORKINGLQUAKE
1452 a = CL_PointQ1Contents(p->org);
1453 if (a <= CONTENTS_WATER)
1455 a = CL_PointSuperContents(p->org);
1456 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1459 p->scalex += frametime * 8;
1460 p->scaley += frametime * 8;
1461 //p->alpha -= bloodwaterfade;
1464 p->vel[2] -= gravity;
1465 #ifdef WORKINGLQUAKE
1466 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1468 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1473 #ifdef WORKINGLQUAKE
1474 a = CL_PointQ1Contents(p->org);
1475 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1477 a = CL_PointSuperContents(p->org);
1478 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1486 #ifdef WORKINGLQUAKE
1487 a = CL_PointQ1Contents(p->org);
1488 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1490 a = CL_PointSuperContents(p->org);
1491 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1496 if (cl.time > p->time2)
1499 p->time2 = cl.time + (rand() & 3) * 0.1;
1500 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1501 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1502 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1504 #ifdef WORKINGLQUAKE
1505 a = CL_PointQ1Contents(p->org);
1506 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1508 a = CL_PointSuperContents(p->org);
1509 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1514 p->scalex += frametime * p->time2;
1515 p->scaley += frametime * p->time2;
1518 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (p->alpha / cl_decals_fadetime.value) : 0;
1519 #ifndef WORKINGLQUAKE
1520 if (p->owner->model == p->ownermodel)
1522 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1523 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1530 a = max(0, (cl.time - p->time2) * 40);
1532 p->texnum = tex_rainsplash[a];
1537 while (cl.time > p->time2)
1540 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);
1544 Con_Printf("unknown particle type %i\n", p->type);
1550 cl_numparticles = maxparticle + 1;
1551 cl_freeparticle = 0;
1554 #define MAX_PARTICLETEXTURES 64
1555 // particletexture_t is a rectangle in the particlefonttexture
1558 rtexture_t *texture;
1559 float s1, t1, s2, t2;
1564 static int particlefonttexture;
1566 static rtexturepool_t *particletexturepool;
1567 static rtexture_t *particlefonttexture;
1569 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1571 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1573 #define PARTICLETEXTURESIZE 64
1574 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1576 static qbyte shadebubble(float dx, float dy, vec3_t light)
1580 dz = 1 - (dx*dx+dy*dy);
1581 if (dz > 0) // it does hit the sphere
1585 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1586 VectorNormalize(normal);
1587 dot = DotProduct(normal, light);
1588 if (dot > 0.5) // interior reflection
1589 f += ((dot * 2) - 1);
1590 else if (dot < -0.5) // exterior reflection
1591 f += ((dot * -2) - 1);
1593 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1594 VectorNormalize(normal);
1595 dot = DotProduct(normal, light);
1596 if (dot > 0.5) // interior reflection
1597 f += ((dot * 2) - 1);
1598 else if (dot < -0.5) // exterior reflection
1599 f += ((dot * -2) - 1);
1601 f += 16; // just to give it a haze so you can see the outline
1602 f = bound(0, f, 255);
1609 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1611 int basex, basey, y;
1612 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1613 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1614 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1615 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1616 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1617 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1618 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1619 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1622 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1625 float cx, cy, dx, dy, f, iradius;
1627 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1628 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1629 iradius = 1.0f / radius;
1630 alpha *= (1.0f / 255.0f);
1631 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1633 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1637 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1640 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1641 d[0] += f * (red - d[0]);
1642 d[1] += f * (green - d[1]);
1643 d[2] += f * (blue - d[2]);
1649 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1652 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1654 data[0] = bound(minr, data[0], maxr);
1655 data[1] = bound(ming, data[1], maxg);
1656 data[2] = bound(minb, data[2], maxb);
1660 void particletextureinvert(qbyte *data)
1663 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1665 data[0] = 255 - data[0];
1666 data[1] = 255 - data[1];
1667 data[2] = 255 - data[2];
1671 static void R_InitParticleTexture (void)
1673 int x, y, d, i, j, k, m;
1674 float dx, dy, radius, f, f2;
1675 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise3[64][64], data2[64][16][4];
1677 qbyte *particletexturedata;
1679 // a note: decals need to modulate (multiply) the background color to
1680 // properly darken it (stain), and they need to be able to alpha fade,
1681 // this is a very difficult challenge because it means fading to white
1682 // (no change to background) rather than black (darkening everything
1683 // behind the whole decal polygon), and to accomplish this the texture is
1684 // inverted (dark red blood on white background becomes brilliant cyan
1685 // and white on black background) so we can alpha fade it to black, then
1686 // we invert it again during the blendfunc to make it work...
1688 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1689 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1692 for (i = 0;i < 8;i++)
1694 memset(&data[0][0][0], 255, sizeof(data));
1697 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1698 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1700 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1702 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1703 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1705 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1706 d = (noise2[y][x] - 128) * 3 + 192;
1708 d = d * (1-(dx*dx+dy*dy));
1709 d = (d * noise1[y][x]) >> 7;
1710 d = bound(0, d, 255);
1711 data[y][x][3] = (qbyte) d;
1718 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1722 for (i = 0;i < 16;i++)
1724 memset(&data[0][0][0], 255, sizeof(data));
1725 radius = i * 3.0f / 4.0f / 16.0f;
1726 f2 = 255.0f * ((15.0f - i) / 15.0f);
1727 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1729 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1730 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1732 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1733 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1734 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1737 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1741 memset(&data[0][0][0], 255, sizeof(data));
1742 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1744 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1745 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1747 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1748 d = 256 * (1 - (dx*dx+dy*dy));
1749 d = bound(0, d, 255);
1750 data[y][x][3] = (qbyte) d;
1753 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1756 memset(&data[0][0][0], 255, sizeof(data));
1757 light[0] = 1;light[1] = 1;light[2] = 1;
1758 VectorNormalize(light);
1759 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1761 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1762 // stretch upper half of bubble by +50% and shrink lower half by -50%
1763 // (this gives an elongated teardrop shape)
1765 dy = (dy - 0.5f) * 2.0f;
1767 dy = (dy - 0.5f) / 1.5f;
1768 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1770 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1771 // shrink bubble width to half
1773 data[y][x][3] = shadebubble(dx, dy, light);
1776 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1779 memset(&data[0][0][0], 255, sizeof(data));
1780 light[0] = 1;light[1] = 1;light[2] = 1;
1781 VectorNormalize(light);
1782 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1784 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1785 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1787 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1788 data[y][x][3] = shadebubble(dx, dy, light);
1791 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1794 for (i = 0;i < 8;i++)
1796 memset(&data[0][0][0], 255, sizeof(data));
1797 for (k = 0;k < 24;k++)
1798 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1799 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1800 particletextureinvert(&data[0][0][0]);
1801 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1805 for (i = 0;i < 8;i++)
1807 memset(&data[0][0][0], 255, sizeof(data));
1809 for (j = 1;j < 10;j++)
1810 for (k = min(j, m - 1);k < m;k++)
1811 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1812 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1813 particletextureinvert(&data[0][0][0]);
1814 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1818 for (i = 0;i < 8;i++)
1820 memset(&data[0][0][0], 255, sizeof(data));
1821 for (k = 0;k < 12;k++)
1822 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1823 for (k = 0;k < 3;k++)
1824 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1825 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1826 particletextureinvert(&data[0][0][0]);
1827 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1831 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1832 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1833 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1837 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1840 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1841 if (!particlefonttexture)
1842 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1843 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1844 particletexture[i].texture = particlefonttexture;
1847 fractalnoise(&noise3[0][0], 64, 4);
1849 for (y = 0;y < 64;y++)
1851 dy = (y - 0.5f*64) / (64*0.5f-1);
1852 for (x = 0;x < 16;x++)
1854 dx = (x - 0.5f*16) / (16*0.5f-2);
1855 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1856 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1857 data2[y][x][3] = 255;
1862 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1865 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1866 if (!particletexture[tex_beam].texture)
1867 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1868 particletexture[tex_beam].s1 = 0;
1869 particletexture[tex_beam].t1 = 0;
1870 particletexture[tex_beam].s2 = 1;
1871 particletexture[tex_beam].t2 = 1;
1873 Mem_Free(particletexturedata);
1876 static void r_part_start(void)
1878 particletexturepool = R_AllocTexturePool();
1879 R_InitParticleTexture ();
1882 static void r_part_shutdown(void)
1884 R_FreeTexturePool(&particletexturepool);
1887 static void r_part_newmap(void)
1889 cl_numparticles = 0;
1890 cl_freeparticle = 0;
1893 void R_Particles_Init (void)
1895 Cvar_RegisterVariable(&r_drawparticles);
1896 #ifdef WORKINGLQUAKE
1899 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1903 #ifdef WORKINGLQUAKE
1904 void R_InitParticles(void)
1906 CL_Particles_Init();
1911 float particle_vertex3f[12], particle_texcoord2f[8];
1913 #ifdef WORKINGLQUAKE
1914 void R_DrawParticle(particle_t *p)
1917 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1919 const particle_t *p = calldata1;
1922 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, scalex, scaley;
1923 particletexture_t *tex;
1925 VectorCopy(p->org, org);
1927 tex = &particletexture[p->texnum];
1928 cr = p->color[0] * (1.0f / 255.0f);
1929 cg = p->color[1] * (1.0f / 255.0f);
1930 cb = p->color[2] * (1.0f / 255.0f);
1931 ca = p->alpha * (1.0f / 255.0f);
1932 if (p->blendmode == PBLEND_MOD)
1943 #ifndef WORKINGLQUAKE
1944 if (fogenabled && p->blendmode != PBLEND_MOD)
1946 VectorSubtract(org, r_vieworigin, fogvec);
1947 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1952 if (p->blendmode == 0)
1954 cr += fogcolor[0] * fog;
1955 cg += fogcolor[1] * fog;
1956 cb += fogcolor[2] * fog;
1960 R_Mesh_Matrix(&r_identitymatrix);
1962 memset(&m, 0, sizeof(m));
1963 m.tex[0] = R_GetTexture(tex->texture);
1964 m.pointer_texcoord[0] = particle_texcoord2f;
1965 m.pointer_vertex = particle_vertex3f;
1968 GL_Color(cr, cg, cb, ca);
1970 if (p->blendmode == 0)
1971 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1972 else if (p->blendmode == 1)
1973 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1975 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1976 GL_DepthMask(false);
1979 scalex = p->scalex * cl_particles_size.value;
1980 scaley = p->scaley * cl_particles_size.value;
1981 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1983 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1986 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1988 VectorNegate(p->vel2, v);
1989 VectorVectors(v, right, up);
1992 VectorVectors(p->vel2, right, up);
1993 VectorScale(right, scalex, right);
1994 VectorScale(up, scaley, up);
1998 VectorScale(r_viewleft, -scalex, right);
1999 VectorScale(r_viewup, scaley, up);
2001 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
2002 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
2003 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
2004 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
2005 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
2006 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
2007 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
2008 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
2009 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
2010 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
2011 particle_vertex3f[10] = org[1] + right[1] - up[1];
2012 particle_vertex3f[11] = org[2] + right[2] - up[2];
2013 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2014 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2015 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2016 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2018 else if (p->orientation == PARTICLE_SPARK)
2020 VectorMA(p->org, -scaley, p->vel, v);
2021 VectorMA(p->org, scaley, p->vel, up2);
2022 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, scalex);
2023 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2024 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2025 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2026 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2028 else if (p->orientation == PARTICLE_BEAM)
2030 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, scalex);
2031 VectorSubtract(p->vel2, p->org, up);
2032 VectorNormalizeFast(up);
2033 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2034 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f);
2035 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2036 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2037 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2038 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2041 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
2044 if (p->blendmode == 0)
2045 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2046 else if (p->blendmode == 1)
2047 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2049 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2050 glColor4f(cr, cg, cb, ca);
2052 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2053 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2054 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2055 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2058 R_Mesh_Draw(4, 2, polygonelements);
2062 void R_DrawParticles (void)
2065 float minparticledist;
2068 #ifdef WORKINGLQUAKE
2072 // LordHavoc: early out conditions
2073 if ((!cl_numparticles) || (!r_drawparticles.integer))
2076 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2078 #ifdef WORKINGLQUAKE
2079 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2081 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2083 // LordHavoc: only render if not too close
2084 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2085 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2088 glDisable(GL_BLEND);
2089 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2091 // LordHavoc: only render if not too close
2092 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2097 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2099 if (p->type == pt_decal)
2100 R_DrawParticleCallback(p, 0);
2102 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);