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 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
331 i = COM_CheckParm ("-particles");
333 if (i && i < com_argc - 1)
335 cl_maxparticles = (int)(atoi(com_argv[i+1]));
336 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
337 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
340 cl_maxparticles = MAX_PARTICLES;
342 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
344 Cvar_RegisterVariable (&cl_particles);
345 Cvar_RegisterVariable (&cl_particles_quality);
346 Cvar_RegisterVariable (&cl_particles_size);
347 Cvar_RegisterVariable (&cl_particles_bloodshowers);
348 Cvar_RegisterVariable (&cl_particles_blood);
349 Cvar_RegisterVariable (&cl_particles_blood_alpha);
350 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
351 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
352 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
353 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
354 Cvar_RegisterVariable (&cl_particles_explosions_shell);
355 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
356 Cvar_RegisterVariable (&cl_particles_smoke);
357 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
358 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
359 Cvar_RegisterVariable (&cl_particles_sparks);
360 Cvar_RegisterVariable (&cl_particles_bubbles);
361 Cvar_RegisterVariable (&cl_decals);
362 Cvar_RegisterVariable (&cl_decals_time);
363 Cvar_RegisterVariable (&cl_decals_fadetime);
366 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
368 cl_part_mempool = Mem_AllocPool("CL_Part", 0, NULL);
369 particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
371 CL_Particles_Clear();
374 // list of all 26 parameters:
375 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
376 // porientation - PARTICLE_ enum values (PARTICLE_BILLBOARD, PARTICLE_SPARK, etc)
377 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
378 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
379 // plight - no longer used (this used to turn on particle lighting)
380 // pblendmode - PBLEND_ enum values (PBLEND_ALPHA, PBLEND_ADD, etc)
381 // pscalex,pscaley - width and height of particle (according to orientation), these are normally the same except when making sparks and beams
382 // palpha - opacity of particle as 0-255 (can be more than 255)
383 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
384 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
385 // pgravity - how much effect gravity has on the particle (0-1)
386 // 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
387 // px,py,pz - starting origin of particle
388 // pvx,pvy,pvz - starting velocity of particle
389 // ptime2 - extra time parameter for certain particle types (pt_decal delayed fades and pt_rain snowflutter use this)
390 // pvx2,pvy2,pvz2 - for PARTICLE_ORIENTED_DOUBLESIDED this is the surface normal of the orientation (forward vector), pt_rain uses this for snow fluttering
391 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
392 // 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
393 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)
396 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
397 ptempcolor = (pcolor1);
398 ptempcolor2 = (pcolor2);
399 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
400 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
401 pcb2 = (ptempcolor2) & 0xFF;
402 if (ptempcolor != ptempcolor2)
404 pcr1 = ((ptempcolor) >> 16) & 0xFF;
405 pcg1 = ((ptempcolor) >> 8) & 0xFF;
406 pcb1 = (ptempcolor) & 0xFF;
407 ptempcolor = rand() & 0xFF;
408 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
409 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
410 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
412 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
413 if (cl_freeparticle >= cl_maxparticles)
415 part = &particles[cl_freeparticle++];
416 if (cl_numparticles < cl_freeparticle)
417 cl_numparticles = cl_freeparticle;
418 memset(part, 0, sizeof(*part));
419 part->type = (ptype);
420 part->color[0] = pcr2;
421 part->color[1] = pcg2;
422 part->color[2] = pcb2;
423 part->color[3] = 0xFF;
424 part->orientation = porientation;
426 part->blendmode = pblendmode;
427 part->scalex = (pscalex);
428 part->scaley = (pscaley);
429 part->alpha = (palpha);
430 part->alphafade = (palphafade);
431 part->die = cl.time + (ptime);
432 part->gravity = (pgravity);
433 part->bounce = (pbounce);
437 part->vel[0] = (pvx);
438 part->vel[1] = (pvy);
439 part->vel[2] = (pvz);
440 part->time2 = (ptime2);
441 part->vel2[0] = (pvx2);
442 part->vel2[1] = (pvy2);
443 part->vel2[2] = (pvz2);
444 part->friction = (pfriction);
445 part->pressure = (ppressure);
449 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
452 if (!cl_decals.integer)
454 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);
455 #ifndef WORKINGLQUAKE
459 p->ownermodel = p->owner->model;
460 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
461 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
462 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
467 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
470 float bestfrac, bestorg[3], bestnormal[3];
471 float frac, v[3], normal[3], org2[3];
473 void *besthitent = NULL, *hitent;
475 entity_render_t *besthitent = NULL, *hitent;
478 for (i = 0;i < 32;i++)
481 VectorMA(org, maxdist, org2, org2);
482 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
487 VectorCopy(v, bestorg);
488 VectorCopy(normal, bestnormal);
492 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
500 void CL_EntityParticles (entity_t *ent)
504 float sp, sy, cp, cy;
508 static vec3_t avelocities[NUMVERTEXNORMALS];
509 if (!cl_particles.integer) return;
514 if (!avelocities[0][0])
515 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
516 avelocities[0][i] = (rand()&255) * 0.01;
518 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
520 angle = cl.time * avelocities[i][0];
523 angle = cl.time * avelocities[i][1];
532 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);
534 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);
540 void CL_ReadPointFile_f (void)
544 char *pointfile = NULL, *pointfilepos, *t, tchar;
545 char name[MAX_OSPATH];
550 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
551 strlcat (name, ".pts", sizeof (name));
553 pointfile = COM_LoadTempFile (name);
555 pointfile = FS_LoadFile(name, tempmempool, true);
559 Con_Printf("Could not open %s\n", name);
563 Con_Printf("Reading %s...\n", name);
566 pointfilepos = pointfile;
567 while (*pointfilepos)
569 while (*pointfilepos == '\n' || *pointfilepos == '\r')
574 while (*t && *t != '\n' && *t != '\r')
578 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
584 VectorCopy(org, leakorg);
587 if (cl_numparticles < cl_maxparticles - 3)
590 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);
593 #ifndef WORKINGLQUAKE
596 VectorCopy(leakorg, org);
597 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
599 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);
600 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);
601 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);
606 CL_ParseParticleEffect
608 Parse an effect out of the server message
611 void CL_ParseParticleEffect (void)
614 int i, count, msgcount, color;
616 MSG_ReadVector(org, cl.protocol);
617 for (i=0 ; i<3 ; i++)
618 dir[i] = MSG_ReadChar () * (1.0/16);
619 msgcount = MSG_ReadByte ();
620 color = MSG_ReadByte ();
627 if (cl_particles_blood_bloodhack.integer)
632 CL_BloodPuff(org, dir, count / 2);
638 CL_BloodPuff(org, dir, count / 2);
642 CL_RunParticleEffect (org, dir, color, count);
651 void CL_ParticleExplosion (vec3_t org)
656 if (cl_stainmaps.integer)
657 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
658 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
660 i = CL_PointQ1Contents(org);
661 if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
663 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
664 for (i = 0;i < 128 * cl_particles_quality.value;i++)
665 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);
669 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
671 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
673 for (i = 0;i < 32;i++)
678 v2[0] = lhrandom(-48, 48);
679 v2[1] = lhrandom(-48, 48);
680 v2[2] = lhrandom(-48, 48);
682 for (k = 0;k < 16;k++)
684 v[0] = org[0] + lhrandom(-48, 48);
685 v[1] = org[1] + lhrandom(-48, 48);
686 v[2] = org[2] + lhrandom(-48, 48);
687 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
690 VectorSubtract(v2, org, v2);
692 VectorScale(v2, 2.0f, v2);
693 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);
698 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
699 for (i = 0;i < 128 * cl_particles_quality.value;i++)
700 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);
702 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
703 for (i = 0;i < 64 * cl_particles_quality.value;i++)
704 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);
706 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
707 for (i = 0;i < 256 * cl_particles_quality.value;i++)
708 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);
712 if (cl_particles_explosions_shell.integer)
718 CL_ParticleExplosion2
722 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
728 if (!cl_particles.integer) return;
730 for (i = 0;i < 512 * cl_particles_quality.value;i++)
732 VectorRandom (offset);
733 VectorScale (offset, 192, vel);
734 VectorScale (offset, 8, offset);
735 k = particlepalette[colorStart + (i % colorLength)];
736 pscale = lhrandom(0.5, 1.5);
737 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);
747 void CL_BlobExplosion (vec3_t org)
749 CL_ParticleExplosion(org);
758 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
764 CL_ParticleExplosion(org);
767 if (!cl_particles.integer) return;
768 count *= cl_particles_quality.value;
771 k = particlepalette[color + (rand()&7)];
772 if (gamemode == GAME_GOODVSBAD2)
773 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);
775 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);
779 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
785 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
790 if (cl_stainmaps.integer)
791 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
792 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
794 if (!cl_particles.integer) return;
796 if (cl_particles_bulletimpacts.integer)
799 if (cl_particles_smoke.integer)
801 k = count * 0.25 * cl_particles_quality.value;
804 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
805 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
806 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
807 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
808 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);
812 if (cl_particles_sparks.integer)
815 count *= cl_particles_quality.value;
818 k = particlepalette[0x68 + (rand() & 7)];
819 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);
825 void CL_PlasmaBurn (vec3_t org)
827 if (cl_stainmaps.integer)
828 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
829 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
832 static float bloodcount = 0;
833 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
837 // bloodcount is used to accumulate counts too small to cause a blood particle
838 if (!cl_particles.integer) return;
839 if (!cl_particles_blood.integer) return;
846 while(bloodcount > 0)
848 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
849 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
850 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
851 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
852 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);
853 bloodcount -= 16 / cl_particles_quality.value;
857 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
859 vec3_t org, vel, diff, center, velscale;
860 if (!cl_particles.integer) return;
861 if (!cl_particles_bloodshowers.integer) return;
862 if (!cl_particles_blood.integer) return;
864 VectorSubtract(maxs, mins, diff);
865 center[0] = (mins[0] + maxs[0]) * 0.5;
866 center[1] = (mins[1] + maxs[1]) * 0.5;
867 center[2] = (mins[2] + maxs[2]) * 0.5;
868 velscale[0] = velspeed * 2.0 / diff[0];
869 velscale[1] = velspeed * 2.0 / diff[1];
870 velscale[2] = velspeed * 2.0 / diff[2];
872 bloodcount += count * 5.0f;
873 while (bloodcount > 0)
875 org[0] = lhrandom(mins[0], maxs[0]);
876 org[1] = lhrandom(mins[1], maxs[1]);
877 org[2] = lhrandom(mins[2], maxs[2]);
878 vel[0] = (org[0] - center[0]) * velscale[0];
879 vel[1] = (org[1] - center[1]) * velscale[1];
880 vel[2] = (org[2] - center[2]) * velscale[2];
881 bloodcount -= 16 / cl_particles_quality.value;
882 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);
886 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
890 if (!cl_particles.integer) return;
891 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
892 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
893 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
895 count *= cl_particles_quality.value;
898 k = particlepalette[colorbase + (rand()&3)];
899 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);
903 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
906 float t, z, minz, maxz;
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;}
911 if (dir[2] < 0) // falling
913 t = (maxs[2] - mins[2]) / -dir[2];
918 t = (maxs[2] - mins[2]) / dir[2];
921 if (t < 0 || t > 2) // sanity check
924 minz = z - fabs(dir[2]) * 0.1;
925 maxz = z + fabs(dir[2]) * 0.1;
926 minz = bound(mins[2], minz, maxs[2]);
927 maxz = bound(mins[2], maxz, maxs[2]);
929 count *= cl_particles_quality.value;
934 count *= 4; // ick, this should be in the mod or maps?
938 k = particlepalette[colorbase + (rand()&3)];
939 if (gamemode == GAME_GOODVSBAD2)
941 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);
945 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);
952 k = particlepalette[colorbase + (rand()&3)];
953 if (gamemode == GAME_GOODVSBAD2)
955 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);
959 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);
964 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
968 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
973 if (!cl_particles.integer) return;
975 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
976 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
977 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
979 center[0] = (mins[0] + maxs[0]) * 0.5f;
980 center[1] = (mins[1] + maxs[1]) * 0.5f;
981 center[2] = (mins[2] + maxs[2]) * 0.5f;
983 count *= cl_particles_quality.value;
986 k = particlepalette[224 + (rand()&15)];
987 o[0] = lhrandom(mins[0], maxs[0]);
988 o[1] = lhrandom(mins[1], maxs[1]);
989 o[2] = lhrandom(mins[2], maxs[2]);
990 VectorSubtract(o, center, v);
991 VectorNormalizeFast(v);
992 VectorScale(v, 100, v);
993 v[2] += sv_gravity.value * 0.15f;
994 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);
998 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
1002 if (!cl_particles.integer) return;
1003 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1004 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1005 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1007 count *= cl_particles_quality.value;
1010 k = particlepalette[224 + (rand()&15)];
1011 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);
1013 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);
1017 void CL_Flames (vec3_t org, vec3_t vel, int count)
1020 if (!cl_particles.integer) return;
1022 count *= cl_particles_quality.value;
1025 k = particlepalette[224 + (rand()&15)];
1026 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);
1038 void CL_LavaSplash (vec3_t origin)
1040 float i, j, inc, vel;
1043 if (!cl_particles.integer) return;
1045 inc = 32 / cl_particles_quality.value;
1046 for (i = -128;i < 128;i += inc)
1048 for (j = -128;j < 128;j += inc)
1050 dir[0] = j + lhrandom(0, 8);
1051 dir[1] = i + lhrandom(0, 8);
1053 org[0] = origin[0] + dir[0];
1054 org[1] = origin[1] + dir[1];
1055 org[2] = origin[2] + lhrandom(0, 64);
1056 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1057 if (gamemode == GAME_GOODVSBAD2)
1059 k = particlepalette[0 + (rand()&255)];
1060 l = particlepalette[0 + (rand()&255)];
1061 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);
1065 k = l = particlepalette[224 + (rand()&7)];
1066 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);
1079 void R_TeleportSplash (vec3_t org)
1082 if (!cl_particles.integer) return;
1084 inc = 8 / cl_particles_quality.value;
1085 for (i = -16;i < 16;i += inc)
1086 for (j = -16;j < 16;j += inc)
1087 for (k = -24;k < 32;k += inc)
1088 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);
1092 #ifdef WORKINGLQUAKE
1093 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1095 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1098 vec3_t vec, dir, vel, pos;
1099 float len, dec, speed, qd;
1100 int contents, smoke, blood, bubbles;
1102 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1105 VectorSubtract(end, start, dir);
1106 VectorNormalize(dir);
1108 VectorSubtract (end, start, vec);
1109 #ifdef WORKINGLQUAKE
1110 len = VectorNormalize (vec);
1112 speed = 1.0f / cl.frametime;
1113 VectorSubtract(end, start, vel);
1115 len = VectorNormalizeLength (vec);
1116 dec = -ent->persistent.trail_time;
1117 ent->persistent.trail_time += len;
1118 if (ent->persistent.trail_time < 0.01f)
1121 // if we skip out, leave it reset
1122 ent->persistent.trail_time = 0.0f;
1124 speed = ent->state_current.time - ent->state_previous.time;
1126 speed = 1.0f / speed;
1127 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1128 color = particlepalette[color];
1130 VectorScale(vel, speed, vel);
1132 // advance into this frame to reach the first puff location
1133 VectorMA(start, dec, vec, pos);
1136 contents = CL_PointQ1Contents(pos);
1137 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1140 smoke = cl_particles.integer && cl_particles_smoke.integer;
1141 blood = cl_particles.integer && cl_particles_blood.integer;
1142 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1143 qd = 1.0f / cl_particles_quality.value;
1149 case 0: // rocket trail
1153 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);
1154 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);
1157 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);
1160 case 1: // grenade trail
1161 // FIXME: make it gradually stop smoking
1164 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);
1169 case 4: // slight blood
1172 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);
1175 case 3: // green tracer
1179 if (gamemode == GAME_GOODVSBAD2)
1180 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);
1182 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);
1186 case 5: // flame tracer
1189 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);
1192 case 6: // voor trail
1196 if (gamemode == GAME_GOODVSBAD2)
1197 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);
1198 else if (gamemode == GAME_PRYDON)
1199 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);
1201 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);
1204 #ifndef WORKINGLQUAKE
1205 case 7: // Nehahra smoke tracer
1208 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);
1210 case 8: // Nexuiz plasma trail
1213 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);
1215 case 9: // glow trail
1218 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);
1223 // advance to next time and position
1225 VectorMA (pos, dec, vec, pos);
1227 #ifndef WORKINGLQUAKE
1228 ent->persistent.trail_time = len;
1232 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1234 int tempcolor2, cr, cg, cb;
1238 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1239 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);
1242 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1245 if (!cl_particles.integer) return;
1248 if (cl_particles_smoke.integer)
1249 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1250 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);
1253 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1256 if (!cl_particles.integer) return;
1258 if (cl_stainmaps.integer)
1259 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1260 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1263 if (cl_particles_smoke.integer)
1264 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1265 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);
1268 if (cl_particles_sparks.integer)
1269 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1270 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);
1278 void CL_MoveParticles (void)
1281 int i, maxparticle, j, a, content;
1282 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1283 #ifdef WORKINGLQUAKE
1286 entity_render_t *hitent;
1289 // LordHavoc: early out condition
1290 if (!cl_numparticles)
1292 cl_freeparticle = 0;
1296 #ifdef WORKINGLQUAKE
1297 frametime = cl.frametime;
1299 frametime = cl.time - cl.oldtime;
1301 gravity = frametime * sv_gravity.value;
1302 dvel = 1+4*frametime;
1303 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1307 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1313 VectorCopy(p->org, p->oldorg);
1314 VectorMA(p->org, frametime, p->vel, p->org);
1315 VectorCopy(p->org, org);
1318 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1320 VectorCopy(v, p->org);
1323 // assume it's blood (lame, but...)
1324 #ifndef WORKINGLQUAKE
1325 if (cl_stainmaps.integer)
1326 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));
1328 if (!cl_decals.integer)
1335 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1336 // convert from a blood particle to a blood decal
1337 p->texnum = tex_blooddecal[rand()&7];
1338 #ifndef WORKINGLQUAKE
1340 p->ownermodel = hitent->model;
1341 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1342 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1343 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1345 p->time2 = cl.time + cl_decals_time.value;
1346 p->die = p->time2 + cl_decals_fadetime.value;
1348 VectorCopy(normal, p->vel2);
1349 VectorClear(p->vel);
1350 VectorAdd(p->org, normal, p->org);
1359 dist = DotProduct(p->vel, normal) * -p->bounce;
1360 VectorMA(p->vel, dist, normal, p->vel);
1361 if (DotProduct(p->vel, p->vel) < 0.03)
1362 VectorClear(p->vel);
1367 p->vel[2] -= p->gravity * gravity;
1369 p->alpha -= p->alphafade * frametime;
1371 if (p->alpha <= 0 || cl.time > p->die)
1379 f = p->friction * frametime;
1381 content = CL_PointQ1Contents(p->org);
1382 if (content != CONTENTS_EMPTY)
1385 VectorScale(p->vel, f, p->vel);
1388 if (p->type != pt_static)
1394 content = CL_PointQ1Contents(p->org);
1396 if (a != CONTENTS_EMPTY)
1398 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1400 p->scalex += frametime * 8;
1401 p->scaley += frametime * 8;
1402 //p->alpha -= bloodwaterfade;
1408 p->vel[2] -= gravity;
1412 content = CL_PointQ1Contents(p->org);
1413 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1420 if (cl.time > p->time2)
1423 p->time2 = cl.time + (rand() & 3) * 0.1;
1424 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1425 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1426 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1429 content = CL_PointQ1Contents(p->org);
1431 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1435 p->scalex += frametime * p->time2;
1436 p->scaley += frametime * p->time2;
1439 #ifndef WORKINGLQUAKE
1440 if (p->owner->model == p->ownermodel)
1442 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1443 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1444 if (cl.time > p->time2)
1446 p->alphafade = p->alpha / (p->die - cl.time);
1447 p->type = pt_decalfade;
1455 #ifndef WORKINGLQUAKE
1456 if (p->owner->model == p->ownermodel)
1458 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1459 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1466 while (cl.time > p->time2)
1469 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);
1473 Con_Printf("unknown particle type %i\n", p->type);
1479 cl_numparticles = maxparticle + 1;
1480 cl_freeparticle = 0;
1483 #define MAX_PARTICLETEXTURES 64
1484 // particletexture_t is a rectangle in the particlefonttexture
1487 rtexture_t *texture;
1488 float s1, t1, s2, t2;
1493 static int particlefonttexture;
1495 static rtexturepool_t *particletexturepool;
1496 static rtexture_t *particlefonttexture;
1498 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1500 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1502 #define PARTICLETEXTURESIZE 32
1503 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1505 static qbyte shadebubble(float dx, float dy, vec3_t light)
1509 dz = 1 - (dx*dx+dy*dy);
1510 if (dz > 0) // it does hit the sphere
1514 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1515 VectorNormalize(normal);
1516 dot = DotProduct(normal, light);
1517 if (dot > 0.5) // interior reflection
1518 f += ((dot * 2) - 1);
1519 else if (dot < -0.5) // exterior reflection
1520 f += ((dot * -2) - 1);
1522 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1523 VectorNormalize(normal);
1524 dot = DotProduct(normal, light);
1525 if (dot > 0.5) // interior reflection
1526 f += ((dot * 2) - 1);
1527 else if (dot < -0.5) // exterior reflection
1528 f += ((dot * -2) - 1);
1530 f += 16; // just to give it a haze so you can see the outline
1531 f = bound(0, f, 255);
1538 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1540 int basex, basey, y;
1541 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1542 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1543 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1544 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1545 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1546 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1547 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1548 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1551 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1554 float cx, cy, dx, dy, f, iradius;
1556 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1557 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1558 iradius = 1.0f / radius;
1559 alpha *= (1.0f / 255.0f);
1560 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1562 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1566 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1569 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1570 d[0] += f * (red - d[0]);
1571 d[1] += f * (green - d[1]);
1572 d[2] += f * (blue - d[2]);
1578 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1581 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1583 data[0] = bound(minr, data[0], maxr);
1584 data[1] = bound(ming, data[1], maxg);
1585 data[2] = bound(minb, data[2], maxb);
1589 void particletextureinvert(qbyte *data)
1592 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1594 data[0] = 255 - data[0];
1595 data[1] = 255 - data[1];
1596 data[2] = 255 - data[2];
1600 static void R_InitParticleTexture (void)
1602 int x, y, d, i, j, k, m;
1603 float dx, dy, radius, f, f2;
1604 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise3[64][64], data2[64][16][4];
1606 qbyte *particletexturedata;
1608 // a note: decals need to modulate (multiply) the background color to
1609 // properly darken it (stain), and they need to be able to alpha fade,
1610 // this is a very difficult challenge because it means fading to white
1611 // (no change to background) rather than black (darkening everything
1612 // behind the whole decal polygon), and to accomplish this the texture is
1613 // inverted (dark red blood on white background becomes brilliant cyan
1614 // and white on black background) so we can alpha fade it to black, then
1615 // we invert it again during the blendfunc to make it work...
1617 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1618 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1621 for (i = 0;i < 8;i++)
1623 memset(&data[0][0][0], 255, sizeof(data));
1626 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1627 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1629 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1631 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1632 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1634 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1635 d = (noise2[y][x] - 128) * 3 + 192;
1637 d = d * (1-(dx*dx+dy*dy));
1638 d = (d * noise1[y][x]) >> 7;
1639 d = bound(0, d, 255);
1640 data[y][x][3] = (qbyte) d;
1647 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1651 for (i = 0;i < 16;i++)
1653 memset(&data[0][0][0], 255, sizeof(data));
1654 radius = i * 3.0f / 4.0f / 16.0f;
1655 f2 = 255.0f * ((15.0f - i) / 15.0f);
1656 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1658 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1659 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1661 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1662 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1663 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1666 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1670 memset(&data[0][0][0], 255, sizeof(data));
1671 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1673 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1674 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1676 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1677 d = 256 * (1 - (dx*dx+dy*dy));
1678 d = bound(0, d, 255);
1679 data[y][x][3] = (qbyte) d;
1682 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1685 memset(&data[0][0][0], 255, sizeof(data));
1686 light[0] = 1;light[1] = 1;light[2] = 1;
1687 VectorNormalize(light);
1688 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1690 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1691 // stretch upper half of bubble by +50% and shrink lower half by -50%
1692 // (this gives an elongated teardrop shape)
1694 dy = (dy - 0.5f) * 2.0f;
1696 dy = (dy - 0.5f) / 1.5f;
1697 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1699 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1700 // shrink bubble width to half
1702 data[y][x][3] = shadebubble(dx, dy, light);
1705 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1708 memset(&data[0][0][0], 255, sizeof(data));
1709 light[0] = 1;light[1] = 1;light[2] = 1;
1710 VectorNormalize(light);
1711 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1713 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1714 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1716 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1717 data[y][x][3] = shadebubble(dx, dy, light);
1720 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1723 for (i = 0;i < 8;i++)
1725 memset(&data[0][0][0], 255, sizeof(data));
1726 for (k = 0;k < 24;k++)
1727 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1728 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1729 particletextureinvert(&data[0][0][0]);
1730 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1734 for (i = 0;i < 8;i++)
1736 memset(&data[0][0][0], 255, sizeof(data));
1738 for (j = 1;j < 10;j++)
1739 for (k = min(j, m - 1);k < m;k++)
1740 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1741 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1742 particletextureinvert(&data[0][0][0]);
1743 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1747 for (i = 0;i < 8;i++)
1749 memset(&data[0][0][0], 255, sizeof(data));
1750 for (k = 0;k < 12;k++)
1751 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1752 for (k = 0;k < 3;k++)
1753 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1754 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1755 particletextureinvert(&data[0][0][0]);
1756 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1760 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1761 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1762 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1764 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1765 if (!particlefonttexture)
1766 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1767 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1768 particletexture[i].texture = particlefonttexture;
1771 fractalnoise(&noise3[0][0], 64, 4);
1773 for (y = 0;y < 64;y++)
1775 dy = (y - 0.5f*64) / (64*0.5f+1);
1776 for (x = 0;x < 16;x++)
1778 dx = (x - 0.5f*16) / (16*0.5f+1);
1779 d = (1 - (dx*dx)) * noise3[y][x];
1780 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1781 data2[y][x][3] = 255;
1785 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1786 if (!particletexture[tex_beam].texture)
1787 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1788 particletexture[tex_beam].s1 = 0;
1789 particletexture[tex_beam].t1 = 0;
1790 particletexture[tex_beam].s2 = 1;
1791 particletexture[tex_beam].t2 = 1;
1793 Mem_Free(particletexturedata);
1796 static void r_part_start(void)
1798 particletexturepool = R_AllocTexturePool();
1799 R_InitParticleTexture ();
1802 static void r_part_shutdown(void)
1804 R_FreeTexturePool(&particletexturepool);
1807 static void r_part_newmap(void)
1809 cl_numparticles = 0;
1810 cl_freeparticle = 0;
1813 void R_Particles_Init (void)
1815 Cvar_RegisterVariable(&r_drawparticles);
1816 #ifdef WORKINGLQUAKE
1819 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1823 #ifdef WORKINGLQUAKE
1824 void R_InitParticles(void)
1826 CL_Particles_Init();
1831 float particle_vertex3f[12], particle_texcoord2f[8];
1833 #ifdef WORKINGLQUAKE
1834 void R_DrawParticle(particle_t *p)
1837 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1839 const particle_t *p = calldata1;
1842 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1843 particletexture_t *tex;
1845 VectorCopy(p->org, org);
1847 tex = &particletexture[p->texnum];
1848 cr = p->color[0] * (1.0f / 255.0f);
1849 cg = p->color[1] * (1.0f / 255.0f);
1850 cb = p->color[2] * (1.0f / 255.0f);
1851 ca = p->alpha * (1.0f / 255.0f);
1852 if (p->blendmode == PBLEND_MOD)
1863 #ifndef WORKINGLQUAKE
1864 if (fogenabled && p->blendmode != PBLEND_MOD)
1866 VectorSubtract(org, r_vieworigin, fogvec);
1867 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1872 if (p->blendmode == 0)
1874 cr += fogcolor[0] * fog;
1875 cg += fogcolor[1] * fog;
1876 cb += fogcolor[2] * fog;
1880 R_Mesh_Matrix(&r_identitymatrix);
1882 memset(&m, 0, sizeof(m));
1883 m.tex[0] = R_GetTexture(tex->texture);
1884 m.pointer_texcoord[0] = particle_texcoord2f;
1885 m.pointer_vertex = particle_vertex3f;
1888 GL_Color(cr, cg, cb, ca);
1890 if (p->blendmode == 0)
1891 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1892 else if (p->blendmode == 1)
1893 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1895 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1896 GL_DepthMask(false);
1899 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1901 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1904 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1906 VectorNegate(p->vel2, v);
1907 VectorVectors(v, right, up);
1910 VectorVectors(p->vel2, right, up);
1911 VectorScale(right, p->scalex, right);
1912 VectorScale(up, p->scaley, up);
1916 VectorScale(r_viewleft, -p->scalex, right);
1917 VectorScale(r_viewup, p->scaley, up);
1919 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1920 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1921 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1922 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1923 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1924 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1925 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1926 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1927 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1928 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1929 particle_vertex3f[10] = org[1] + right[1] - up[1];
1930 particle_vertex3f[11] = org[2] + right[2] - up[2];
1931 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1932 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1933 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1934 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1936 else if (p->orientation == PARTICLE_SPARK)
1938 VectorMA(p->org, -p->scaley, p->vel, v);
1939 VectorMA(p->org, p->scaley, p->vel, up2);
1940 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1941 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1942 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1943 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1944 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1946 else if (p->orientation == PARTICLE_BEAM)
1948 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1949 VectorSubtract(p->vel2, p->org, up);
1950 VectorNormalizeFast(up);
1951 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1952 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1953 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1954 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1955 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1956 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1959 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1962 if (p->blendmode == 0)
1963 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1964 else if (p->blendmode == 1)
1965 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1967 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1968 glColor4f(cr, cg, cb, ca);
1970 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1971 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1972 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1973 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1976 R_Mesh_Draw(4, 2, polygonelements);
1980 void R_DrawParticles (void)
1983 float minparticledist;
1986 #ifdef WORKINGLQUAKE
1990 // LordHavoc: early out conditions
1991 if ((!cl_numparticles) || (!r_drawparticles.integer))
1994 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1996 #ifdef WORKINGLQUAKE
1997 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1999 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2001 // LordHavoc: only render if not too close
2002 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2003 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2006 glDisable(GL_BLEND);
2007 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2009 // LordHavoc: only render if not too close
2010 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2015 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2016 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);