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