2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
25 #define NUMVERTEXNORMALS 162
26 siextern float r_avertexnormals[NUMVERTEXNORMALS][3];
27 #define m_bytenormals r_avertexnormals
28 #define VectorNormalizeFast VectorNormalize
29 #define CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
30 typedef unsigned char qbyte;
31 #define cl_stainmaps.integer 0
32 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
35 #define CL_EntityParticles R_EntityParticles
36 #define CL_ReadPointFile_f R_ReadPointFile_f
37 #define CL_ParseParticleEffect R_ParseParticleEffect
38 #define CL_ParticleExplosion R_ParticleExplosion
39 #define CL_ParticleExplosion2 R_ParticleExplosion2
40 #define CL_TeleportSplash R_TeleportSplash
41 #define CL_BlobExplosion R_BlobExplosion
42 #define CL_RunParticleEffect R_RunParticleEffect
43 #define CL_LavaSplash R_LavaSplash
44 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
46 vec3_t right1, right2, diff, normal;
48 VectorSubtract (org2, org1, normal);
49 VectorNormalizeFast (normal);
51 // calculate 'right' vector for start
52 VectorSubtract (r_vieworigin, org1, diff);
53 VectorNormalizeFast (diff);
54 CrossProduct (normal, diff, right1);
56 // calculate 'right' vector for end
57 VectorSubtract (r_vieworigin, org2, diff);
58 VectorNormalizeFast (diff);
59 CrossProduct (normal, diff, right2);
61 vert[ 0] = org1[0] + width * right1[0];
62 vert[ 1] = org1[1] + width * right1[1];
63 vert[ 2] = org1[2] + width * right1[2];
64 vert[ 3] = org1[0] - width * right1[0];
65 vert[ 4] = org1[1] - width * right1[1];
66 vert[ 5] = org1[2] - width * right1[2];
67 vert[ 6] = org2[0] - width * right2[0];
68 vert[ 7] = org2[1] - width * right2[1];
69 vert[ 8] = org2[2] - width * right2[2];
70 vert[ 9] = org2[0] + width * right2[0];
71 vert[10] = org2[1] + width * right2[1];
72 vert[11] = org2[2] + width * right2[2];
74 void fractalnoise(qbyte *noise, int size, int startgrid)
76 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
78 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
80 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
81 if (size != (1 << sizepower))
82 Sys_Error("fractalnoise: size must be power of 2\n");
84 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
85 if (startgrid != (1 << gridpower))
86 Sys_Error("fractalnoise: grid must be power of 2\n");
88 startgrid = bound(0, startgrid, size);
90 amplitude = 0xFFFF; // this gets halved before use
91 noisebuf = malloc(size*size*sizeof(int));
92 memset(noisebuf, 0, size*size*sizeof(int));
94 for (g2 = startgrid;g2;g2 >>= 1)
96 // brownian motion (at every smaller level there is random behavior)
98 for (y = 0;y < size;y += g2)
99 for (x = 0;x < size;x += g2)
100 n(x,y) += (rand()&litude);
105 // subdivide, diamond-square algorithm (really this has little to do with squares)
107 for (y = 0;y < size;y += g2)
108 for (x = 0;x < size;x += g2)
109 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
111 for (y = 0;y < size;y += g2)
112 for (x = 0;x < size;x += g2)
114 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
115 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
119 // find range of noise values
121 for (y = 0;y < size;y++)
122 for (x = 0;x < size;x++)
124 if (n(x,y) < min) min = n(x,y);
125 if (n(x,y) > max) max = n(x,y);
129 // normalize noise and copy to output
130 for (y = 0;y < size;y++)
131 for (x = 0;x < size;x++)
132 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
136 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
140 right[0] = forward[2];
141 right[1] = -forward[0];
142 right[2] = forward[1];
144 d = DotProduct(forward, right);
145 right[0] -= d * forward[0];
146 right[1] -= d * forward[1];
147 right[2] -= d * forward[2];
148 VectorNormalizeFast(right);
149 CrossProduct(right, forward, up);
153 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
155 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int hitbmodels, void **hitent, int hitsupercontentsmask)
162 memset (&trace, 0, sizeof(trace));
164 VectorCopy (end, trace.endpos);
166 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
168 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
170 VectorCopy(trace.endpos, impact);
171 VectorCopy(trace.plane.normal, normal);
172 return trace.fraction;
175 #include "cl_collision.h"
179 #define MAX_PARTICLES 32768 // default max # of particles at one time
180 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
184 pt_dead, pt_static, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_grow, pt_decal, pt_ember
190 PARTICLE_BILLBOARD = 0,
192 PARTICLE_ORIENTED_DOUBLESIDED = 2,
205 typedef struct particle_s
216 float alpha; // 0-255
217 float alphafade; // how much alpha reduces per second
218 float time2; // used for various things (snow fluttering, for example)
219 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)
220 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
222 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
223 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
224 float pressure; // if non-zero, apply pressure to other particles
226 #ifndef WORKINGLQUAKE
227 entity_render_t *owner; // decal stuck to this entity
228 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
229 vec3_t relativeorigin; // decal at this location in entity's coordinate space
230 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
235 static int particlepalette[256] =
237 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
238 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
239 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
240 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
241 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
242 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
243 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
244 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
245 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
246 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
247 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
248 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
249 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
250 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
251 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
252 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
253 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
254 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
255 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
256 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
257 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
258 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
259 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
260 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
261 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
262 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
263 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
264 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
265 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
266 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
267 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
268 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
271 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
273 // texture numbers in particle font
274 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
275 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
276 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
277 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
278 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
279 static const int tex_particle = 63;
280 static const int tex_bubble = 62;
281 static const int tex_raindrop = 61;
282 static const int tex_beam = 60;
284 static int cl_maxparticles;
285 static int cl_numparticles;
286 static int cl_freeparticle;
287 static particle_t *particles;
289 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
290 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
291 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
292 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
293 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
294 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
295 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
296 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
297 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
298 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
299 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
300 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
301 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
302 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
303 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
304 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
305 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
306 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
307 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
308 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
310 #ifndef WORKINGLQUAKE
311 static mempool_t *cl_part_mempool;
314 void CL_Particles_Clear(void)
318 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
326 void CL_ReadPointFile_f (void);
327 void CL_Particles_Init (void)
331 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
332 i = COM_CheckParm ("-particles");
334 if (i && i < com_argc - 1)
336 cl_maxparticles = (int)(atoi(com_argv[i+1]));
337 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
338 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
341 cl_maxparticles = MAX_PARTICLES;
343 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
345 Cvar_RegisterVariable (&cl_particles);
346 Cvar_RegisterVariable (&cl_particles_quality);
347 Cvar_RegisterVariable (&cl_particles_size);
348 Cvar_RegisterVariable (&cl_particles_bloodshowers);
349 Cvar_RegisterVariable (&cl_particles_blood);
350 Cvar_RegisterVariable (&cl_particles_blood_alpha);
351 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
352 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
353 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
354 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
355 Cvar_RegisterVariable (&cl_particles_explosions_shell);
356 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
357 Cvar_RegisterVariable (&cl_particles_smoke);
358 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
359 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
360 Cvar_RegisterVariable (&cl_particles_sparks);
361 Cvar_RegisterVariable (&cl_particles_bubbles);
362 Cvar_RegisterVariable (&cl_decals);
363 Cvar_RegisterVariable (&cl_decals_time);
364 Cvar_RegisterVariable (&cl_decals_fadetime);
367 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
369 cl_part_mempool = Mem_AllocPool("CL_Part", 0, NULL);
370 particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
372 CL_Particles_Clear();
375 void CL_Particles_Shutdown (void)
378 // No clue what to do here...
380 Mem_FreePool (&cl_part_mempool);
384 // list of all 26 parameters:
385 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
386 // porientation - PARTICLE_ enum values (PARTICLE_BILLBOARD, PARTICLE_SPARK, etc)
387 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
388 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
389 // plight - no longer used (this used to turn on particle lighting)
390 // pblendmode - PBLEND_ enum values (PBLEND_ALPHA, PBLEND_ADD, etc)
391 // pscalex,pscaley - width and height of particle (according to orientation), these are normally the same except when making sparks and beams
392 // palpha - opacity of particle as 0-255 (can be more than 255)
393 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
394 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
395 // pgravity - how much effect gravity has on the particle (0-1)
396 // 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
397 // px,py,pz - starting origin of particle
398 // pvx,pvy,pvz - starting velocity of particle
399 // ptime2 - extra time parameter for certain particle types (pt_decal delayed fades and pt_rain snowflutter use this)
400 // pvx2,pvy2,pvz2 - for PARTICLE_ORIENTED_DOUBLESIDED this is the surface normal of the orientation (forward vector), pt_rain uses this for snow fluttering
401 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
402 // 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
403 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)
406 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
407 ptempcolor = (pcolor1);
408 ptempcolor2 = (pcolor2);
409 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
410 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
411 pcb2 = (ptempcolor2) & 0xFF;
412 if (ptempcolor != ptempcolor2)
414 pcr1 = ((ptempcolor) >> 16) & 0xFF;
415 pcg1 = ((ptempcolor) >> 8) & 0xFF;
416 pcb1 = (ptempcolor) & 0xFF;
417 ptempcolor = rand() & 0xFF;
418 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
419 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
420 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
422 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
423 if (cl_freeparticle >= cl_maxparticles)
425 part = &particles[cl_freeparticle++];
426 if (cl_numparticles < cl_freeparticle)
427 cl_numparticles = cl_freeparticle;
428 memset(part, 0, sizeof(*part));
429 part->type = (ptype);
430 part->color[0] = pcr2;
431 part->color[1] = pcg2;
432 part->color[2] = pcb2;
433 part->color[3] = 0xFF;
434 part->orientation = porientation;
436 part->blendmode = pblendmode;
437 part->scalex = (pscalex);
438 part->scaley = (pscaley);
439 part->alpha = (palpha);
440 part->alphafade = (palphafade);
441 part->die = cl.time + (ptime);
442 part->gravity = (pgravity);
443 part->bounce = (pbounce);
447 part->vel[0] = (pvx);
448 part->vel[1] = (pvy);
449 part->vel[2] = (pvz);
450 part->time2 = (ptime2);
451 part->vel2[0] = (pvx2);
452 part->vel2[1] = (pvy2);
453 part->vel2[2] = (pvz2);
454 part->friction = (pfriction);
455 part->pressure = (ppressure);
459 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
462 if (!cl_decals.integer)
464 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);
465 #ifndef WORKINGLQUAKE
469 p->ownermodel = p->owner->model;
470 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
471 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
472 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
477 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
480 float bestfrac, bestorg[3], bestnormal[3];
481 float frac, v[3], normal[3], org2[3];
483 void *besthitent = NULL, *hitent;
485 entity_render_t *besthitent = NULL, *hitent;
488 for (i = 0;i < 32;i++)
491 VectorMA(org, maxdist, org2, org2);
492 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
497 VectorCopy(v, bestorg);
498 VectorCopy(normal, bestnormal);
502 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
510 void CL_EntityParticles (entity_t *ent)
514 float sp, sy, cp, cy;
518 static vec3_t avelocities[NUMVERTEXNORMALS];
519 if (!cl_particles.integer) return;
524 if (!avelocities[0][0])
525 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
526 avelocities[0][i] = (rand()&255) * 0.01;
528 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
530 angle = cl.time * avelocities[i][0];
533 angle = cl.time * avelocities[i][1];
542 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);
544 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);
550 void CL_ReadPointFile_f (void)
554 char *pointfile = NULL, *pointfilepos, *t, tchar;
555 char name[MAX_OSPATH];
560 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
561 strlcat (name, ".pts", sizeof (name));
563 pointfile = COM_LoadTempFile (name);
565 pointfile = FS_LoadFile(name, tempmempool, true);
569 Con_Printf("Could not open %s\n", name);
573 Con_Printf("Reading %s...\n", name);
576 pointfilepos = pointfile;
577 while (*pointfilepos)
579 while (*pointfilepos == '\n' || *pointfilepos == '\r')
584 while (*t && *t != '\n' && *t != '\r')
588 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
594 VectorCopy(org, leakorg);
597 if (cl_numparticles < cl_maxparticles - 3)
600 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);
603 #ifndef WORKINGLQUAKE
606 VectorCopy(leakorg, org);
607 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
609 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);
610 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);
611 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);
616 CL_ParseParticleEffect
618 Parse an effect out of the server message
621 void CL_ParseParticleEffect (void)
624 int i, count, msgcount, color;
626 MSG_ReadVector(org, cl.protocol);
627 for (i=0 ; i<3 ; i++)
628 dir[i] = MSG_ReadChar () * (1.0/16);
629 msgcount = MSG_ReadByte ();
630 color = MSG_ReadByte ();
637 if (cl_particles_blood_bloodhack.integer)
642 CL_BloodPuff(org, dir, count / 2);
648 CL_BloodPuff(org, dir, count / 2);
652 CL_RunParticleEffect (org, dir, color, count);
661 void CL_ParticleExplosion (vec3_t org)
666 if (cl_stainmaps.integer)
667 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
668 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
670 i = CL_PointSuperContents(org);
671 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
673 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
674 for (i = 0;i < 128 * cl_particles_quality.value;i++)
675 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);
679 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
681 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
683 for (i = 0;i < 32;i++)
688 v2[0] = lhrandom(-48, 48);
689 v2[1] = lhrandom(-48, 48);
690 v2[2] = lhrandom(-48, 48);
692 for (k = 0;k < 16;k++)
694 v[0] = org[0] + lhrandom(-48, 48);
695 v[1] = org[1] + lhrandom(-48, 48);
696 v[2] = org[2] + lhrandom(-48, 48);
697 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
700 VectorSubtract(v2, org, v2);
702 VectorScale(v2, 2.0f, v2);
703 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);
708 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
709 for (i = 0;i < 128 * cl_particles_quality.value;i++)
710 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);
712 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
713 for (i = 0;i < 64 * cl_particles_quality.value;i++)
714 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);
716 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
717 for (i = 0;i < 256 * cl_particles_quality.value;i++)
718 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);
722 if (cl_particles_explosions_shell.integer)
728 CL_ParticleExplosion2
732 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
738 if (!cl_particles.integer) return;
740 for (i = 0;i < 512 * cl_particles_quality.value;i++)
742 VectorRandom (offset);
743 VectorScale (offset, 192, vel);
744 VectorScale (offset, 8, offset);
745 k = particlepalette[colorStart + (i % colorLength)];
746 pscale = lhrandom(0.5, 1.5);
747 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);
757 void CL_BlobExplosion (vec3_t org)
759 CL_ParticleExplosion(org);
768 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
774 CL_ParticleExplosion(org);
777 if (!cl_particles.integer) return;
778 count *= cl_particles_quality.value;
781 k = particlepalette[color + (rand()&7)];
782 if (gamemode == GAME_GOODVSBAD2)
783 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);
785 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);
789 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
795 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
799 if (!cl_particles.integer) return;
801 if (cl_particles_sparks.integer)
804 count *= cl_particles_quality.value;
807 k = particlepalette[0x68 + (rand() & 7)];
808 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);
813 void CL_Smoke (vec3_t org, vec3_t dir, int count)
818 if (!cl_particles.integer) return;
821 if (cl_particles_smoke.integer)
823 k = count * 0.25 * cl_particles_quality.value;
826 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
827 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
828 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
829 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
830 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);
835 void CL_BulletMark (vec3_t org)
837 if (cl_stainmaps.integer)
838 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
839 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
842 void CL_PlasmaBurn (vec3_t org)
844 if (cl_stainmaps.integer)
845 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
846 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
849 static float bloodcount = 0;
850 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
854 // bloodcount is used to accumulate counts too small to cause a blood particle
855 if (!cl_particles.integer) return;
856 if (!cl_particles_blood.integer) return;
863 while(bloodcount > 0)
865 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
866 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
867 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
868 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
869 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);
870 bloodcount -= 16 / cl_particles_quality.value;
874 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
876 vec3_t org, vel, diff, center, velscale;
877 if (!cl_particles.integer) return;
878 if (!cl_particles_bloodshowers.integer) return;
879 if (!cl_particles_blood.integer) return;
881 VectorSubtract(maxs, mins, diff);
882 center[0] = (mins[0] + maxs[0]) * 0.5;
883 center[1] = (mins[1] + maxs[1]) * 0.5;
884 center[2] = (mins[2] + maxs[2]) * 0.5;
885 velscale[0] = velspeed * 2.0 / diff[0];
886 velscale[1] = velspeed * 2.0 / diff[1];
887 velscale[2] = velspeed * 2.0 / diff[2];
889 bloodcount += count * 5.0f;
890 while (bloodcount > 0)
892 org[0] = lhrandom(mins[0], maxs[0]);
893 org[1] = lhrandom(mins[1], maxs[1]);
894 org[2] = lhrandom(mins[2], maxs[2]);
895 vel[0] = (org[0] - center[0]) * velscale[0];
896 vel[1] = (org[1] - center[1]) * velscale[1];
897 vel[2] = (org[2] - center[2]) * velscale[2];
898 bloodcount -= 16 / cl_particles_quality.value;
899 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);
903 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
907 if (!cl_particles.integer) return;
908 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
909 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
910 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
912 count *= cl_particles_quality.value;
915 k = particlepalette[colorbase + (rand()&3)];
916 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);
920 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
923 float t, z, minz, maxz;
924 if (!cl_particles.integer) return;
925 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
926 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
927 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
928 if (dir[2] < 0) // falling
930 t = (maxs[2] - mins[2]) / -dir[2];
935 t = (maxs[2] - mins[2]) / dir[2];
938 if (t < 0 || t > 2) // sanity check
941 minz = z - fabs(dir[2]) * 0.1;
942 maxz = z + fabs(dir[2]) * 0.1;
943 minz = bound(mins[2], minz, maxs[2]);
944 maxz = bound(mins[2], maxz, maxs[2]);
946 count *= cl_particles_quality.value;
951 count *= 4; // ick, this should be in the mod or maps?
955 k = particlepalette[colorbase + (rand()&3)];
956 if (gamemode == GAME_GOODVSBAD2)
958 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);
962 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);
969 k = particlepalette[colorbase + (rand()&3)];
970 if (gamemode == GAME_GOODVSBAD2)
972 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);
976 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);
981 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
985 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
990 if (!cl_particles.integer) return;
992 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
993 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
994 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
996 center[0] = (mins[0] + maxs[0]) * 0.5f;
997 center[1] = (mins[1] + maxs[1]) * 0.5f;
998 center[2] = (mins[2] + maxs[2]) * 0.5f;
1000 count *= cl_particles_quality.value;
1003 k = particlepalette[224 + (rand()&15)];
1004 o[0] = lhrandom(mins[0], maxs[0]);
1005 o[1] = lhrandom(mins[1], maxs[1]);
1006 o[2] = lhrandom(mins[2], maxs[2]);
1007 VectorSubtract(o, center, v);
1008 VectorNormalizeFast(v);
1009 VectorScale(v, 100, v);
1010 v[2] += sv_gravity.value * 0.15f;
1011 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);
1015 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
1019 if (!cl_particles.integer) return;
1020 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1021 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1022 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1024 count *= cl_particles_quality.value;
1027 k = particlepalette[224 + (rand()&15)];
1028 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);
1030 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);
1034 void CL_Flames (vec3_t org, vec3_t vel, int count)
1037 if (!cl_particles.integer) return;
1039 count *= cl_particles_quality.value;
1042 k = particlepalette[224 + (rand()&15)];
1043 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);
1055 void CL_LavaSplash (vec3_t origin)
1057 float i, j, inc, vel;
1060 if (!cl_particles.integer) return;
1062 inc = 32 / cl_particles_quality.value;
1063 for (i = -128;i < 128;i += inc)
1065 for (j = -128;j < 128;j += inc)
1067 dir[0] = j + lhrandom(0, 8);
1068 dir[1] = i + lhrandom(0, 8);
1070 org[0] = origin[0] + dir[0];
1071 org[1] = origin[1] + dir[1];
1072 org[2] = origin[2] + lhrandom(0, 64);
1073 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1074 if (gamemode == GAME_GOODVSBAD2)
1076 k = particlepalette[0 + (rand()&255)];
1077 l = particlepalette[0 + (rand()&255)];
1078 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);
1082 k = l = particlepalette[224 + (rand()&7)];
1083 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 CL_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 * 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);
1107 #ifdef WORKINGLQUAKE
1108 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1110 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1113 vec3_t vec, dir, vel, pos;
1114 float len, dec, speed, qd;
1115 int smoke, blood, bubbles;
1116 #ifdef WORKINGLQUAKE
1120 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1123 VectorSubtract(end, start, dir);
1124 VectorNormalize(dir);
1126 VectorSubtract (end, start, vec);
1127 #ifdef WORKINGLQUAKE
1128 len = VectorNormalize (vec);
1130 speed = 1.0f / cl.frametime;
1131 VectorSubtract(end, start, vel);
1133 len = VectorNormalizeLength (vec);
1134 dec = -ent->persistent.trail_time;
1135 ent->persistent.trail_time += len;
1136 if (ent->persistent.trail_time < 0.01f)
1139 // if we skip out, leave it reset
1140 ent->persistent.trail_time = 0.0f;
1142 speed = ent->state_current.time - ent->state_previous.time;
1144 speed = 1.0f / speed;
1145 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1146 color = particlepalette[color];
1148 VectorScale(vel, speed, vel);
1150 // advance into this frame to reach the first puff location
1151 VectorMA(start, dec, vec, pos);
1154 smoke = cl_particles.integer && cl_particles_smoke.integer;
1155 blood = cl_particles.integer && cl_particles_blood.integer;
1156 #ifdef WORKINGLQUAKE
1157 contents = CL_PointQ1Contents(pos);
1158 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1160 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1162 qd = 1.0f / cl_particles_quality.value;
1168 case 0: // rocket trail
1172 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);
1173 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);
1176 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);
1179 case 1: // grenade trail
1180 // FIXME: make it gradually stop smoking
1183 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);
1188 case 4: // slight blood
1191 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);
1194 case 3: // green tracer
1198 if (gamemode == GAME_GOODVSBAD2)
1199 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);
1201 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);
1205 case 5: // flame tracer
1208 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);
1211 case 6: // voor trail
1215 if (gamemode == GAME_GOODVSBAD2)
1216 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);
1217 else if (gamemode == GAME_PRYDON)
1218 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);
1220 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);
1223 #ifndef WORKINGLQUAKE
1224 case 7: // Nehahra smoke tracer
1227 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);
1229 case 8: // Nexuiz plasma trail
1232 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);
1234 case 9: // glow trail
1237 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);
1242 // advance to next time and position
1244 VectorMA (pos, dec, vec, pos);
1246 #ifndef WORKINGLQUAKE
1247 ent->persistent.trail_time = len;
1251 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1253 int tempcolor2, cr, cg, cb;
1257 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1258 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);
1261 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1264 if (!cl_particles.integer) return;
1267 if (cl_particles_smoke.integer)
1268 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1269 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);
1272 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1275 if (!cl_particles.integer) return;
1277 if (cl_stainmaps.integer)
1278 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1279 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1282 if (cl_particles_smoke.integer)
1283 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1284 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);
1287 if (cl_particles_sparks.integer)
1288 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1289 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);
1297 void CL_MoveParticles (void)
1300 int i, maxparticle, j, a, content;
1301 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1302 #ifdef WORKINGLQUAKE
1305 entity_render_t *hitent;
1308 // LordHavoc: early out condition
1309 if (!cl_numparticles)
1311 cl_freeparticle = 0;
1315 #ifdef WORKINGLQUAKE
1316 frametime = cl.frametime;
1318 frametime = cl.time - cl.oldtime;
1320 gravity = frametime * sv_gravity.value;
1321 dvel = 1+4*frametime;
1322 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1326 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1332 VectorCopy(p->org, p->oldorg);
1333 VectorMA(p->org, frametime, p->vel, p->org);
1334 VectorCopy(p->org, org);
1337 if (p->type == pt_rain)
1339 // raindrop - splash on solid/water/slime/lava
1340 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK) < 1)
1342 VectorCopy(v, p->org);
1344 p->type = pt_raindecal;
1345 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1346 // convert from a raindrop particle to a rainsplash decal
1347 p->texnum = tex_rainsplash[0];
1349 p->die = p->time2 + 0.4;
1350 p->alphafade = p->alpha / 0.4;
1351 VectorCopy(normal, p->vel2);
1352 VectorClear(p->vel);
1353 VectorAdd(p->org, normal, p->org);
1361 else if (p->type == pt_blood)
1363 // blood - splash on solid
1364 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1366 VectorCopy(v, p->org);
1367 #ifndef WORKINGLQUAKE
1368 if (cl_stainmaps.integer)
1369 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));
1371 if (!cl_decals.integer)
1378 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1379 // convert from a blood particle to a blood decal
1380 p->texnum = tex_blooddecal[rand()&7];
1381 #ifndef WORKINGLQUAKE
1383 p->ownermodel = hitent->model;
1384 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1385 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1386 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1389 p->die = p->time2 + cl_decals_time.value + cl_decals_fadetime.value;
1391 VectorCopy(normal, p->vel2);
1392 VectorClear(p->vel);
1393 VectorAdd(p->org, normal, p->org);
1403 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1405 VectorCopy(v, p->org);
1413 dist = DotProduct(p->vel, normal) * -p->bounce;
1414 VectorMA(p->vel, dist, normal, p->vel);
1415 if (DotProduct(p->vel, p->vel) < 0.03)
1416 VectorClear(p->vel);
1422 p->vel[2] -= p->gravity * gravity;
1424 p->alpha -= p->alphafade * frametime;
1426 if (p->alpha <= 0 || cl.time > p->die)
1434 f = p->friction * frametime;
1435 #ifdef WORKINGLQUAKE
1436 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1438 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1442 VectorScale(p->vel, f, p->vel);
1445 if (p->type != pt_static)
1450 #ifdef WORKINGLQUAKE
1451 a = CL_PointQ1Contents(p->org);
1452 if (a <= CONTENTS_WATER)
1454 a = CL_PointSuperContents(p->org);
1455 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1458 p->scalex += frametime * 8;
1459 p->scaley += frametime * 8;
1460 //p->alpha -= bloodwaterfade;
1463 p->vel[2] -= gravity;
1464 #ifdef WORKINGLQUAKE
1465 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1467 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1472 #ifdef WORKINGLQUAKE
1473 a = CL_PointQ1Contents(p->org);
1474 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1476 a = CL_PointSuperContents(p->org);
1477 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1485 #ifdef WORKINGLQUAKE
1486 a = CL_PointQ1Contents(p->org);
1487 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1489 a = CL_PointSuperContents(p->org);
1490 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1495 if (cl.time > p->time2)
1498 p->time2 = cl.time + (rand() & 3) * 0.1;
1499 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1500 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1501 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1503 #ifdef WORKINGLQUAKE
1504 a = CL_PointQ1Contents(p->org);
1505 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1507 a = CL_PointSuperContents(p->org);
1508 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1513 p->scalex += frametime * p->time2;
1514 p->scaley += frametime * p->time2;
1517 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (p->alpha / cl_decals_fadetime.value) : 0;
1518 #ifndef WORKINGLQUAKE
1519 if (p->owner->model == p->ownermodel)
1521 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1522 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1529 a = max(0, (cl.time - p->time2) * 40);
1531 p->texnum = tex_rainsplash[a];
1536 while (cl.time > p->time2)
1539 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);
1543 Con_Printf("unknown particle type %i\n", p->type);
1549 cl_numparticles = maxparticle + 1;
1550 cl_freeparticle = 0;
1553 #define MAX_PARTICLETEXTURES 64
1554 // particletexture_t is a rectangle in the particlefonttexture
1557 rtexture_t *texture;
1558 float s1, t1, s2, t2;
1563 static int particlefonttexture;
1565 static rtexturepool_t *particletexturepool;
1566 static rtexture_t *particlefonttexture;
1568 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1570 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1572 #define PARTICLETEXTURESIZE 64
1573 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1575 static qbyte shadebubble(float dx, float dy, vec3_t light)
1579 dz = 1 - (dx*dx+dy*dy);
1580 if (dz > 0) // it does hit the sphere
1584 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1585 VectorNormalize(normal);
1586 dot = DotProduct(normal, light);
1587 if (dot > 0.5) // interior reflection
1588 f += ((dot * 2) - 1);
1589 else if (dot < -0.5) // exterior reflection
1590 f += ((dot * -2) - 1);
1592 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1593 VectorNormalize(normal);
1594 dot = DotProduct(normal, light);
1595 if (dot > 0.5) // interior reflection
1596 f += ((dot * 2) - 1);
1597 else if (dot < -0.5) // exterior reflection
1598 f += ((dot * -2) - 1);
1600 f += 16; // just to give it a haze so you can see the outline
1601 f = bound(0, f, 255);
1608 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1610 int basex, basey, y;
1611 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1612 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1613 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1614 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1615 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1616 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1617 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1618 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1621 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1624 float cx, cy, dx, dy, f, iradius;
1626 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1627 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1628 iradius = 1.0f / radius;
1629 alpha *= (1.0f / 255.0f);
1630 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1632 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1636 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1639 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1640 d[0] += f * (red - d[0]);
1641 d[1] += f * (green - d[1]);
1642 d[2] += f * (blue - d[2]);
1648 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1651 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1653 data[0] = bound(minr, data[0], maxr);
1654 data[1] = bound(ming, data[1], maxg);
1655 data[2] = bound(minb, data[2], maxb);
1659 void particletextureinvert(qbyte *data)
1662 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1664 data[0] = 255 - data[0];
1665 data[1] = 255 - data[1];
1666 data[2] = 255 - data[2];
1670 static void R_InitParticleTexture (void)
1672 int x, y, d, i, j, k, m;
1673 float dx, dy, radius, f, f2;
1674 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise3[64][64], data2[64][16][4];
1676 qbyte *particletexturedata;
1678 // a note: decals need to modulate (multiply) the background color to
1679 // properly darken it (stain), and they need to be able to alpha fade,
1680 // this is a very difficult challenge because it means fading to white
1681 // (no change to background) rather than black (darkening everything
1682 // behind the whole decal polygon), and to accomplish this the texture is
1683 // inverted (dark red blood on white background becomes brilliant cyan
1684 // and white on black background) so we can alpha fade it to black, then
1685 // we invert it again during the blendfunc to make it work...
1687 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1688 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1691 for (i = 0;i < 8;i++)
1693 memset(&data[0][0][0], 255, sizeof(data));
1696 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1697 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1699 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1701 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1702 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1704 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1705 d = (noise2[y][x] - 128) * 3 + 192;
1707 d = d * (1-(dx*dx+dy*dy));
1708 d = (d * noise1[y][x]) >> 7;
1709 d = bound(0, d, 255);
1710 data[y][x][3] = (qbyte) d;
1717 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1721 for (i = 0;i < 16;i++)
1723 memset(&data[0][0][0], 255, sizeof(data));
1724 radius = i * 3.0f / 4.0f / 16.0f;
1725 f2 = 255.0f * ((15.0f - i) / 15.0f);
1726 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1728 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1729 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1731 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1732 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1733 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1736 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1740 memset(&data[0][0][0], 255, sizeof(data));
1741 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1743 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1744 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1746 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1747 d = 256 * (1 - (dx*dx+dy*dy));
1748 d = bound(0, d, 255);
1749 data[y][x][3] = (qbyte) d;
1752 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1755 memset(&data[0][0][0], 255, sizeof(data));
1756 light[0] = 1;light[1] = 1;light[2] = 1;
1757 VectorNormalize(light);
1758 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1760 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1761 // stretch upper half of bubble by +50% and shrink lower half by -50%
1762 // (this gives an elongated teardrop shape)
1764 dy = (dy - 0.5f) * 2.0f;
1766 dy = (dy - 0.5f) / 1.5f;
1767 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1769 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1770 // shrink bubble width to half
1772 data[y][x][3] = shadebubble(dx, dy, light);
1775 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1778 memset(&data[0][0][0], 255, sizeof(data));
1779 light[0] = 1;light[1] = 1;light[2] = 1;
1780 VectorNormalize(light);
1781 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1783 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1784 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1786 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1787 data[y][x][3] = shadebubble(dx, dy, light);
1790 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1793 for (i = 0;i < 8;i++)
1795 memset(&data[0][0][0], 255, sizeof(data));
1796 for (k = 0;k < 24;k++)
1797 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1798 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1799 particletextureinvert(&data[0][0][0]);
1800 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1804 for (i = 0;i < 8;i++)
1806 memset(&data[0][0][0], 255, sizeof(data));
1808 for (j = 1;j < 10;j++)
1809 for (k = min(j, m - 1);k < m;k++)
1810 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1811 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1812 particletextureinvert(&data[0][0][0]);
1813 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1817 for (i = 0;i < 8;i++)
1819 memset(&data[0][0][0], 255, sizeof(data));
1820 for (k = 0;k < 12;k++)
1821 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1822 for (k = 0;k < 3;k++)
1823 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1824 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1825 particletextureinvert(&data[0][0][0]);
1826 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1830 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1831 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1832 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1836 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1839 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1840 if (!particlefonttexture)
1841 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1842 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1843 particletexture[i].texture = particlefonttexture;
1846 fractalnoise(&noise3[0][0], 64, 4);
1848 for (y = 0;y < 64;y++)
1850 dy = (y - 0.5f*64) / (64*0.5f-1);
1851 for (x = 0;x < 16;x++)
1853 dx = (x - 0.5f*16) / (16*0.5f-2);
1854 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1855 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1856 data2[y][x][3] = 255;
1861 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1864 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1865 if (!particletexture[tex_beam].texture)
1866 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1867 particletexture[tex_beam].s1 = 0;
1868 particletexture[tex_beam].t1 = 0;
1869 particletexture[tex_beam].s2 = 1;
1870 particletexture[tex_beam].t2 = 1;
1872 Mem_Free(particletexturedata);
1875 static void r_part_start(void)
1877 particletexturepool = R_AllocTexturePool();
1878 R_InitParticleTexture ();
1881 static void r_part_shutdown(void)
1883 R_FreeTexturePool(&particletexturepool);
1886 static void r_part_newmap(void)
1888 cl_numparticles = 0;
1889 cl_freeparticle = 0;
1892 void R_Particles_Init (void)
1894 Cvar_RegisterVariable(&r_drawparticles);
1895 #ifdef WORKINGLQUAKE
1898 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1902 #ifdef WORKINGLQUAKE
1903 void R_InitParticles(void)
1905 CL_Particles_Init();
1910 float particle_vertex3f[12], particle_texcoord2f[8];
1912 #ifdef WORKINGLQUAKE
1913 void R_DrawParticle(particle_t *p)
1916 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1918 const particle_t *p = calldata1;
1921 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, scalex, scaley;
1922 particletexture_t *tex;
1924 VectorCopy(p->org, org);
1926 tex = &particletexture[p->texnum];
1927 cr = p->color[0] * (1.0f / 255.0f);
1928 cg = p->color[1] * (1.0f / 255.0f);
1929 cb = p->color[2] * (1.0f / 255.0f);
1930 ca = p->alpha * (1.0f / 255.0f);
1931 if (p->blendmode == PBLEND_MOD)
1942 #ifndef WORKINGLQUAKE
1943 if (fogenabled && p->blendmode != PBLEND_MOD)
1945 VectorSubtract(org, r_vieworigin, fogvec);
1946 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1951 if (p->blendmode == 0)
1953 cr += fogcolor[0] * fog;
1954 cg += fogcolor[1] * fog;
1955 cb += fogcolor[2] * fog;
1959 R_Mesh_Matrix(&r_identitymatrix);
1961 memset(&m, 0, sizeof(m));
1962 m.tex[0] = R_GetTexture(tex->texture);
1963 m.pointer_texcoord[0] = particle_texcoord2f;
1964 m.pointer_vertex = particle_vertex3f;
1967 GL_Color(cr, cg, cb, ca);
1969 if (p->blendmode == 0)
1970 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1971 else if (p->blendmode == 1)
1972 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1974 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1975 GL_DepthMask(false);
1978 scalex = p->scalex * cl_particles_size.value;
1979 scaley = p->scaley * cl_particles_size.value;
1980 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1982 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1985 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1987 VectorNegate(p->vel2, v);
1988 VectorVectors(v, right, up);
1991 VectorVectors(p->vel2, right, up);
1992 VectorScale(right, scalex, right);
1993 VectorScale(up, scaley, up);
1997 VectorScale(r_viewleft, -scalex, right);
1998 VectorScale(r_viewup, scaley, up);
2000 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
2001 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
2002 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
2003 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
2004 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
2005 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
2006 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
2007 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
2008 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
2009 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
2010 particle_vertex3f[10] = org[1] + right[1] - up[1];
2011 particle_vertex3f[11] = org[2] + right[2] - up[2];
2012 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2013 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2014 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2015 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2017 else if (p->orientation == PARTICLE_SPARK)
2019 VectorMA(p->org, -scaley, p->vel, v);
2020 VectorMA(p->org, scaley, p->vel, up2);
2021 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, scalex);
2022 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2023 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2024 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2025 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2027 else if (p->orientation == PARTICLE_BEAM)
2029 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, scalex);
2030 VectorSubtract(p->vel2, p->org, up);
2031 VectorNormalizeFast(up);
2032 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2033 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f);
2034 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2035 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2036 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2037 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2040 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
2043 if (p->blendmode == 0)
2044 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2045 else if (p->blendmode == 1)
2046 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2048 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2049 glColor4f(cr, cg, cb, ca);
2051 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2052 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2053 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2054 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2057 R_Mesh_Draw(4, 2, polygonelements);
2061 void R_DrawParticles (void)
2064 float minparticledist;
2067 #ifdef WORKINGLQUAKE
2071 // LordHavoc: early out conditions
2072 if ((!cl_numparticles) || (!r_drawparticles.integer))
2075 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2077 #ifdef WORKINGLQUAKE
2078 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2080 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2082 // LordHavoc: only render if not too close
2083 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2084 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2087 glDisable(GL_BLEND);
2088 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2090 // LordHavoc: only render if not too close
2091 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2096 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2098 if (p->type == pt_decal)
2099 R_DrawParticleCallback(p, 0);
2101 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);