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);
1129 VectorScale(vel, speed, vel);
1131 // advance into this frame to reach the first puff location
1132 VectorMA(start, dec, vec, pos);
1135 contents = CL_PointQ1Contents(pos);
1136 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1139 smoke = cl_particles.integer && cl_particles_smoke.integer;
1140 blood = cl_particles.integer && cl_particles_blood.integer;
1141 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1142 qd = 1.0f / cl_particles_quality.value;
1148 case 0: // rocket trail
1152 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);
1153 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);
1156 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);
1159 case 1: // grenade trail
1160 // FIXME: make it gradually stop smoking
1163 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);
1168 case 4: // slight blood
1171 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);
1174 case 3: // green tracer
1178 if (gamemode == GAME_GOODVSBAD2)
1179 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);
1181 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);
1185 case 5: // flame tracer
1188 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);
1191 case 6: // voor trail
1195 if (gamemode == GAME_GOODVSBAD2)
1196 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);
1197 else if (gamemode == GAME_PRYDON)
1198 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);
1200 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);
1203 #ifndef WORKINGLQUAKE
1204 case 7: // Nehahra smoke tracer
1207 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);
1209 case 8: // Nexuiz plasma trail
1212 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);
1214 case 9: // glow trail
1217 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);
1222 // advance to next time and position
1224 VectorMA (pos, dec, vec, pos);
1226 #ifndef WORKINGLQUAKE
1227 ent->persistent.trail_time = len;
1231 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1233 int tempcolor2, cr, cg, cb;
1237 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1238 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);
1241 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1244 if (!cl_particles.integer) return;
1247 if (cl_particles_smoke.integer)
1248 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1249 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);
1252 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1255 if (!cl_particles.integer) return;
1257 if (cl_stainmaps.integer)
1258 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1259 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1262 if (cl_particles_smoke.integer)
1263 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1264 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);
1267 if (cl_particles_sparks.integer)
1268 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1269 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);
1277 void CL_MoveParticles (void)
1280 int i, maxparticle, j, a, content;
1281 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1282 #ifdef WORKINGLQUAKE
1285 entity_render_t *hitent;
1288 // LordHavoc: early out condition
1289 if (!cl_numparticles)
1291 cl_freeparticle = 0;
1295 #ifdef WORKINGLQUAKE
1296 frametime = cl.frametime;
1298 frametime = cl.time - cl.oldtime;
1300 gravity = frametime * sv_gravity.value;
1301 dvel = 1+4*frametime;
1302 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1306 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1312 VectorCopy(p->org, p->oldorg);
1313 VectorMA(p->org, frametime, p->vel, p->org);
1314 VectorCopy(p->org, org);
1317 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1319 VectorCopy(v, p->org);
1322 // assume it's blood (lame, but...)
1323 #ifndef WORKINGLQUAKE
1324 if (cl_stainmaps.integer)
1325 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));
1327 if (!cl_decals.integer)
1334 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1335 // convert from a blood particle to a blood decal
1336 p->texnum = tex_blooddecal[rand()&7];
1337 #ifndef WORKINGLQUAKE
1339 p->ownermodel = hitent->model;
1340 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1341 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1342 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1344 p->time2 = cl.time + cl_decals_time.value;
1345 p->die = p->time2 + cl_decals_fadetime.value;
1347 VectorCopy(normal, p->vel2);
1348 VectorClear(p->vel);
1349 VectorAdd(p->org, normal, p->org);
1358 dist = DotProduct(p->vel, normal) * -p->bounce;
1359 VectorMA(p->vel, dist, normal, p->vel);
1360 if (DotProduct(p->vel, p->vel) < 0.03)
1361 VectorClear(p->vel);
1366 p->vel[2] -= p->gravity * gravity;
1368 p->alpha -= p->alphafade * frametime;
1370 if (p->alpha <= 0 || cl.time > p->die)
1378 f = p->friction * frametime;
1380 content = CL_PointQ1Contents(p->org);
1381 if (content != CONTENTS_EMPTY)
1384 VectorScale(p->vel, f, p->vel);
1387 if (p->type != pt_static)
1393 content = CL_PointQ1Contents(p->org);
1395 if (a != CONTENTS_EMPTY)
1397 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1399 p->scalex += frametime * 8;
1400 p->scaley += frametime * 8;
1401 //p->alpha -= bloodwaterfade;
1407 p->vel[2] -= gravity;
1411 content = CL_PointQ1Contents(p->org);
1412 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1419 if (cl.time > p->time2)
1422 p->time2 = cl.time + (rand() & 3) * 0.1;
1423 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1424 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1425 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1428 content = CL_PointQ1Contents(p->org);
1430 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1434 p->scalex += frametime * p->time2;
1435 p->scaley += frametime * p->time2;
1438 #ifndef WORKINGLQUAKE
1439 if (p->owner->model == p->ownermodel)
1441 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1442 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1443 if (cl.time > p->time2)
1445 p->alphafade = p->alpha / (p->die - cl.time);
1446 p->type = pt_decalfade;
1454 #ifndef WORKINGLQUAKE
1455 if (p->owner->model == p->ownermodel)
1457 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1458 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1465 while (cl.time > p->time2)
1468 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);
1472 Con_Printf("unknown particle type %i\n", p->type);
1478 cl_numparticles = maxparticle + 1;
1479 cl_freeparticle = 0;
1482 #define MAX_PARTICLETEXTURES 64
1483 // particletexture_t is a rectangle in the particlefonttexture
1486 rtexture_t *texture;
1487 float s1, t1, s2, t2;
1492 static int particlefonttexture;
1494 static rtexturepool_t *particletexturepool;
1495 static rtexture_t *particlefonttexture;
1497 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1499 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1501 #define PARTICLETEXTURESIZE 32
1502 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1504 static qbyte shadebubble(float dx, float dy, vec3_t light)
1508 dz = 1 - (dx*dx+dy*dy);
1509 if (dz > 0) // it does hit the sphere
1513 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1514 VectorNormalize(normal);
1515 dot = DotProduct(normal, light);
1516 if (dot > 0.5) // interior reflection
1517 f += ((dot * 2) - 1);
1518 else if (dot < -0.5) // exterior reflection
1519 f += ((dot * -2) - 1);
1521 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1522 VectorNormalize(normal);
1523 dot = DotProduct(normal, light);
1524 if (dot > 0.5) // interior reflection
1525 f += ((dot * 2) - 1);
1526 else if (dot < -0.5) // exterior reflection
1527 f += ((dot * -2) - 1);
1529 f += 16; // just to give it a haze so you can see the outline
1530 f = bound(0, f, 255);
1537 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1539 int basex, basey, y;
1540 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1541 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1542 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1543 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1544 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1545 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1546 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1547 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1550 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1553 float cx, cy, dx, dy, f, iradius;
1555 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1556 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1557 iradius = 1.0f / radius;
1558 alpha *= (1.0f / 255.0f);
1559 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1561 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1565 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1568 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1569 d[0] += f * (red - d[0]);
1570 d[1] += f * (green - d[1]);
1571 d[2] += f * (blue - d[2]);
1577 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1580 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1582 data[0] = bound(minr, data[0], maxr);
1583 data[1] = bound(ming, data[1], maxg);
1584 data[2] = bound(minb, data[2], maxb);
1588 void particletextureinvert(qbyte *data)
1591 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1593 data[0] = 255 - data[0];
1594 data[1] = 255 - data[1];
1595 data[2] = 255 - data[2];
1599 static void R_InitParticleTexture (void)
1601 int x, y, d, i, j, k, m;
1602 float dx, dy, radius, f, f2;
1603 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise3[64][64], data2[64][16][4];
1605 qbyte *particletexturedata;
1607 // a note: decals need to modulate (multiply) the background color to
1608 // properly darken it (stain), and they need to be able to alpha fade,
1609 // this is a very difficult challenge because it means fading to white
1610 // (no change to background) rather than black (darkening everything
1611 // behind the whole decal polygon), and to accomplish this the texture is
1612 // inverted (dark red blood on white background becomes brilliant cyan
1613 // and white on black background) so we can alpha fade it to black, then
1614 // we invert it again during the blendfunc to make it work...
1616 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1617 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1620 for (i = 0;i < 8;i++)
1622 memset(&data[0][0][0], 255, sizeof(data));
1625 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1626 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1628 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1630 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1631 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1633 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1634 d = (noise2[y][x] - 128) * 3 + 192;
1636 d = d * (1-(dx*dx+dy*dy));
1637 d = (d * noise1[y][x]) >> 7;
1638 d = bound(0, d, 255);
1639 data[y][x][3] = (qbyte) d;
1646 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1650 for (i = 0;i < 16;i++)
1652 memset(&data[0][0][0], 255, sizeof(data));
1653 radius = i * 3.0f / 4.0f / 16.0f;
1654 f2 = 255.0f * ((15.0f - i) / 15.0f);
1655 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1657 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1658 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1660 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1661 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1662 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1665 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1669 memset(&data[0][0][0], 255, sizeof(data));
1670 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1672 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1673 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1675 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1676 d = 256 * (1 - (dx*dx+dy*dy));
1677 d = bound(0, d, 255);
1678 data[y][x][3] = (qbyte) d;
1681 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1684 memset(&data[0][0][0], 255, sizeof(data));
1685 light[0] = 1;light[1] = 1;light[2] = 1;
1686 VectorNormalize(light);
1687 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1689 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1690 // stretch upper half of bubble by +50% and shrink lower half by -50%
1691 // (this gives an elongated teardrop shape)
1693 dy = (dy - 0.5f) * 2.0f;
1695 dy = (dy - 0.5f) / 1.5f;
1696 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1698 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1699 // shrink bubble width to half
1701 data[y][x][3] = shadebubble(dx, dy, light);
1704 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1707 memset(&data[0][0][0], 255, sizeof(data));
1708 light[0] = 1;light[1] = 1;light[2] = 1;
1709 VectorNormalize(light);
1710 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1712 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1713 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1715 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1716 data[y][x][3] = shadebubble(dx, dy, light);
1719 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1722 for (i = 0;i < 8;i++)
1724 memset(&data[0][0][0], 255, sizeof(data));
1725 for (k = 0;k < 24;k++)
1726 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1727 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1728 particletextureinvert(&data[0][0][0]);
1729 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1733 for (i = 0;i < 8;i++)
1735 memset(&data[0][0][0], 255, sizeof(data));
1737 for (j = 1;j < 10;j++)
1738 for (k = min(j, m - 1);k < m;k++)
1739 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1740 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1741 particletextureinvert(&data[0][0][0]);
1742 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1746 for (i = 0;i < 8;i++)
1748 memset(&data[0][0][0], 255, sizeof(data));
1749 for (k = 0;k < 12;k++)
1750 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1751 for (k = 0;k < 3;k++)
1752 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1753 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1754 particletextureinvert(&data[0][0][0]);
1755 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1759 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1760 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1761 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1763 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1764 if (!particlefonttexture)
1765 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1766 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1767 particletexture[i].texture = particlefonttexture;
1770 fractalnoise(&noise3[0][0], 64, 4);
1772 for (y = 0;y < 64;y++)
1774 dy = (y - 0.5f*64) / (64*0.5f+1);
1775 for (x = 0;x < 16;x++)
1777 dx = (x - 0.5f*16) / (16*0.5f+1);
1778 d = (1 - (dx*dx)) * noise3[y][x];
1779 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1780 data2[y][x][3] = 255;
1784 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1785 if (!particletexture[tex_beam].texture)
1786 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1787 particletexture[tex_beam].s1 = 0;
1788 particletexture[tex_beam].t1 = 0;
1789 particletexture[tex_beam].s2 = 1;
1790 particletexture[tex_beam].t2 = 1;
1792 Mem_Free(particletexturedata);
1795 static void r_part_start(void)
1797 particletexturepool = R_AllocTexturePool();
1798 R_InitParticleTexture ();
1801 static void r_part_shutdown(void)
1803 R_FreeTexturePool(&particletexturepool);
1806 static void r_part_newmap(void)
1808 cl_numparticles = 0;
1809 cl_freeparticle = 0;
1812 void R_Particles_Init (void)
1814 Cvar_RegisterVariable(&r_drawparticles);
1815 #ifdef WORKINGLQUAKE
1818 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1822 #ifdef WORKINGLQUAKE
1823 void R_InitParticles(void)
1825 CL_Particles_Init();
1830 float particle_vertex3f[12], particle_texcoord2f[8];
1832 #ifdef WORKINGLQUAKE
1833 void R_DrawParticle(particle_t *p)
1836 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1838 const particle_t *p = calldata1;
1841 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1842 particletexture_t *tex;
1844 VectorCopy(p->org, org);
1846 tex = &particletexture[p->texnum];
1847 cr = p->color[0] * (1.0f / 255.0f);
1848 cg = p->color[1] * (1.0f / 255.0f);
1849 cb = p->color[2] * (1.0f / 255.0f);
1850 ca = p->alpha * (1.0f / 255.0f);
1851 if (p->blendmode == PBLEND_MOD)
1862 #ifndef WORKINGLQUAKE
1863 if (fogenabled && p->blendmode != PBLEND_MOD)
1865 VectorSubtract(org, r_vieworigin, fogvec);
1866 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1871 if (p->blendmode == 0)
1873 cr += fogcolor[0] * fog;
1874 cg += fogcolor[1] * fog;
1875 cb += fogcolor[2] * fog;
1879 R_Mesh_Matrix(&r_identitymatrix);
1881 memset(&m, 0, sizeof(m));
1882 m.tex[0] = R_GetTexture(tex->texture);
1883 m.pointer_texcoord[0] = particle_texcoord2f;
1884 m.pointer_vertex = particle_vertex3f;
1887 GL_Color(cr, cg, cb, ca);
1889 if (p->blendmode == 0)
1890 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1891 else if (p->blendmode == 1)
1892 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1894 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1895 GL_DepthMask(false);
1898 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1900 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1903 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1905 VectorNegate(p->vel2, v);
1906 VectorVectors(v, right, up);
1909 VectorVectors(p->vel2, right, up);
1910 VectorScale(right, p->scalex, right);
1911 VectorScale(up, p->scaley, up);
1915 VectorScale(r_viewleft, -p->scalex, right);
1916 VectorScale(r_viewup, p->scaley, up);
1918 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1919 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1920 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1921 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1922 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1923 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1924 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1925 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1926 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1927 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1928 particle_vertex3f[10] = org[1] + right[1] - up[1];
1929 particle_vertex3f[11] = org[2] + right[2] - up[2];
1930 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1931 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1932 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1933 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1935 else if (p->orientation == PARTICLE_SPARK)
1937 VectorMA(p->org, -p->scaley, p->vel, v);
1938 VectorMA(p->org, p->scaley, p->vel, up2);
1939 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1940 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1941 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1942 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1943 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1945 else if (p->orientation == PARTICLE_BEAM)
1947 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1948 VectorSubtract(p->vel2, p->org, up);
1949 VectorNormalizeFast(up);
1950 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1951 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1952 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1953 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1954 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1955 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1958 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1961 if (p->blendmode == 0)
1962 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1963 else if (p->blendmode == 1)
1964 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1966 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1967 glColor4f(cr, cg, cb, ca);
1969 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1970 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1971 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1972 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1975 R_Mesh_Draw(4, 2, polygonelements);
1979 void R_DrawParticles (void)
1982 float minparticledist;
1985 #ifdef WORKINGLQUAKE
1989 // LordHavoc: early out conditions
1990 if ((!cl_numparticles) || (!r_drawparticles.integer))
1993 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1995 #ifdef WORKINGLQUAKE
1996 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1998 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2000 // LordHavoc: only render if not too close
2001 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2002 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2005 glDisable(GL_BLEND);
2006 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2008 // LordHavoc: only render if not too close
2009 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2014 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2015 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);