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, vec_t gravityscale)
789 if (!cl_particles.integer) return;
791 if (cl_particles_sparks.integer)
794 count *= cl_particles_quality.value;
797 k = particlepalette[0x68 + (rand() & 7)];
798 particle(pt_static, PARTICLE_SPARK, k, k, tex_particle, false, PBLEND_ADD, 0.4f, 0.015f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, 9999, gravityscale, 0, org[0], org[1], org[2], lhrandom(-64, 64) + dir[0], lhrandom(-64, 64) + dir[1], lhrandom(0, 128) + dir[2], 0, 0, 0, 0, 0, 0);
803 void CL_Smoke (vec3_t org, vec3_t dir, int count)
808 if (!cl_particles.integer) return;
811 if (cl_particles_smoke.integer)
813 k = count * 0.25 * cl_particles_quality.value;
816 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
817 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
818 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
819 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
820 particle(pt_grow, PARTICLE_BILLBOARD, 0x101010, 0x202020, tex_smoke[rand()&7], true, PBLEND_ADD, 3, 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 9999, 0, 0, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 15, 0, 0, 0, 0, 0);
825 void CL_BulletMark (vec3_t org)
827 if (cl_stainmaps.integer)
828 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
829 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
832 void CL_PlasmaBurn (vec3_t org)
834 if (cl_stainmaps.integer)
835 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
836 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
839 static float bloodcount = 0;
840 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
844 // bloodcount is used to accumulate counts too small to cause a blood particle
845 if (!cl_particles.integer) return;
846 if (!cl_particles_blood.integer) return;
853 while(bloodcount > 0)
855 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
856 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
857 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
858 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
859 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);
860 bloodcount -= 16 / cl_particles_quality.value;
864 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
866 vec3_t org, vel, diff, center, velscale;
867 if (!cl_particles.integer) return;
868 if (!cl_particles_bloodshowers.integer) return;
869 if (!cl_particles_blood.integer) return;
871 VectorSubtract(maxs, mins, diff);
872 center[0] = (mins[0] + maxs[0]) * 0.5;
873 center[1] = (mins[1] + maxs[1]) * 0.5;
874 center[2] = (mins[2] + maxs[2]) * 0.5;
875 velscale[0] = velspeed * 2.0 / diff[0];
876 velscale[1] = velspeed * 2.0 / diff[1];
877 velscale[2] = velspeed * 2.0 / diff[2];
879 bloodcount += count * 5.0f;
880 while (bloodcount > 0)
882 org[0] = lhrandom(mins[0], maxs[0]);
883 org[1] = lhrandom(mins[1], maxs[1]);
884 org[2] = lhrandom(mins[2], maxs[2]);
885 vel[0] = (org[0] - center[0]) * velscale[0];
886 vel[1] = (org[1] - center[1]) * velscale[1];
887 vel[2] = (org[2] - center[2]) * velscale[2];
888 bloodcount -= 16 / cl_particles_quality.value;
889 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);
893 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
897 if (!cl_particles.integer) return;
898 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
899 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
900 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
902 count *= cl_particles_quality.value;
905 k = particlepalette[colorbase + (rand()&3)];
906 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);
910 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
913 float t, z, minz, maxz;
914 if (!cl_particles.integer) return;
915 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
916 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
917 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
918 if (dir[2] < 0) // falling
920 t = (maxs[2] - mins[2]) / -dir[2];
925 t = (maxs[2] - mins[2]) / dir[2];
928 if (t < 0 || t > 2) // sanity check
931 minz = z - fabs(dir[2]) * 0.1;
932 maxz = z + fabs(dir[2]) * 0.1;
933 minz = bound(mins[2], minz, maxs[2]);
934 maxz = bound(mins[2], maxz, maxs[2]);
936 count *= cl_particles_quality.value;
941 count *= 4; // ick, this should be in the mod or maps?
945 k = particlepalette[colorbase + (rand()&3)];
946 if (gamemode == GAME_GOODVSBAD2)
948 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);
952 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);
959 k = particlepalette[colorbase + (rand()&3)];
960 if (gamemode == GAME_GOODVSBAD2)
962 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);
966 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);
971 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
975 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
980 if (!cl_particles.integer) return;
982 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
983 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
984 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
986 center[0] = (mins[0] + maxs[0]) * 0.5f;
987 center[1] = (mins[1] + maxs[1]) * 0.5f;
988 center[2] = (mins[2] + maxs[2]) * 0.5f;
990 count *= cl_particles_quality.value;
993 k = particlepalette[224 + (rand()&15)];
994 o[0] = lhrandom(mins[0], maxs[0]);
995 o[1] = lhrandom(mins[1], maxs[1]);
996 o[2] = lhrandom(mins[2], maxs[2]);
997 VectorSubtract(o, center, v);
998 VectorNormalizeFast(v);
999 VectorScale(v, 100, v);
1000 v[2] += sv_gravity.value * 0.15f;
1001 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);
1005 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
1009 if (!cl_particles.integer) return;
1010 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1011 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1012 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1014 count *= cl_particles_quality.value;
1017 k = particlepalette[224 + (rand()&15)];
1018 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);
1020 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);
1024 void CL_Flames (vec3_t org, vec3_t vel, int count)
1027 if (!cl_particles.integer) return;
1029 count *= cl_particles_quality.value;
1032 k = particlepalette[224 + (rand()&15)];
1033 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);
1045 void CL_LavaSplash (vec3_t origin)
1047 float i, j, inc, vel;
1050 if (!cl_particles.integer) return;
1052 inc = 32 / cl_particles_quality.value;
1053 for (i = -128;i < 128;i += inc)
1055 for (j = -128;j < 128;j += inc)
1057 dir[0] = j + lhrandom(0, 8);
1058 dir[1] = i + lhrandom(0, 8);
1060 org[0] = origin[0] + dir[0];
1061 org[1] = origin[1] + dir[1];
1062 org[2] = origin[2] + lhrandom(0, 64);
1063 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1064 if (gamemode == GAME_GOODVSBAD2)
1066 k = particlepalette[0 + (rand()&255)];
1067 l = particlepalette[0 + (rand()&255)];
1068 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);
1072 k = l = particlepalette[224 + (rand()&7)];
1073 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);
1086 void R_TeleportSplash (vec3_t org)
1089 if (!cl_particles.integer) return;
1091 inc = 8 / cl_particles_quality.value;
1092 for (i = -16;i < 16;i += inc)
1093 for (j = -16;j < 16;j += inc)
1094 for (k = -24;k < 32;k += inc)
1095 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);
1099 #ifdef WORKINGLQUAKE
1100 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1102 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1105 vec3_t vec, dir, vel, pos;
1106 float len, dec, speed, qd;
1107 int contents, smoke, blood, bubbles;
1109 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1112 VectorSubtract(end, start, dir);
1113 VectorNormalize(dir);
1115 VectorSubtract (end, start, vec);
1116 #ifdef WORKINGLQUAKE
1117 len = VectorNormalize (vec);
1119 speed = 1.0f / cl.frametime;
1120 VectorSubtract(end, start, vel);
1122 len = VectorNormalizeLength (vec);
1123 dec = -ent->persistent.trail_time;
1124 ent->persistent.trail_time += len;
1125 if (ent->persistent.trail_time < 0.01f)
1128 // if we skip out, leave it reset
1129 ent->persistent.trail_time = 0.0f;
1131 speed = ent->state_current.time - ent->state_previous.time;
1133 speed = 1.0f / speed;
1134 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1135 color = particlepalette[color];
1137 VectorScale(vel, speed, vel);
1139 // advance into this frame to reach the first puff location
1140 VectorMA(start, dec, vec, pos);
1143 contents = CL_PointQ1Contents(pos);
1144 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1147 smoke = cl_particles.integer && cl_particles_smoke.integer;
1148 blood = cl_particles.integer && cl_particles_blood.integer;
1149 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1150 qd = 1.0f / cl_particles_quality.value;
1156 case 0: // rocket trail
1160 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);
1161 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);
1164 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);
1167 case 1: // grenade trail
1168 // FIXME: make it gradually stop smoking
1171 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);
1176 case 4: // slight blood
1179 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);
1182 case 3: // green tracer
1186 if (gamemode == GAME_GOODVSBAD2)
1187 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);
1189 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);
1193 case 5: // flame tracer
1196 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);
1199 case 6: // voor trail
1203 if (gamemode == GAME_GOODVSBAD2)
1204 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);
1205 else if (gamemode == GAME_PRYDON)
1206 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);
1208 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);
1211 #ifndef WORKINGLQUAKE
1212 case 7: // Nehahra smoke tracer
1215 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);
1217 case 8: // Nexuiz plasma trail
1220 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);
1222 case 9: // glow trail
1225 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);
1230 // advance to next time and position
1232 VectorMA (pos, dec, vec, pos);
1234 #ifndef WORKINGLQUAKE
1235 ent->persistent.trail_time = len;
1239 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1241 int tempcolor2, cr, cg, cb;
1245 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1246 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);
1249 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1252 if (!cl_particles.integer) return;
1255 if (cl_particles_smoke.integer)
1256 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1257 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);
1260 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1263 if (!cl_particles.integer) return;
1265 if (cl_stainmaps.integer)
1266 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1267 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1270 if (cl_particles_smoke.integer)
1271 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1272 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);
1275 if (cl_particles_sparks.integer)
1276 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1277 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);
1285 void CL_MoveParticles (void)
1288 int i, maxparticle, j, a, content;
1289 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1290 #ifdef WORKINGLQUAKE
1293 entity_render_t *hitent;
1296 // LordHavoc: early out condition
1297 if (!cl_numparticles)
1299 cl_freeparticle = 0;
1303 #ifdef WORKINGLQUAKE
1304 frametime = cl.frametime;
1306 frametime = cl.time - cl.oldtime;
1308 gravity = frametime * sv_gravity.value;
1309 dvel = 1+4*frametime;
1310 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1314 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1320 VectorCopy(p->org, p->oldorg);
1321 VectorMA(p->org, frametime, p->vel, p->org);
1322 VectorCopy(p->org, org);
1325 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1327 VectorCopy(v, p->org);
1330 // assume it's blood (lame, but...)
1331 #ifndef WORKINGLQUAKE
1332 if (cl_stainmaps.integer)
1333 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));
1335 if (!cl_decals.integer)
1342 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1343 // convert from a blood particle to a blood decal
1344 p->texnum = tex_blooddecal[rand()&7];
1345 #ifndef WORKINGLQUAKE
1347 p->ownermodel = hitent->model;
1348 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1349 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1350 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1352 p->time2 = cl.time + cl_decals_time.value;
1353 p->die = p->time2 + cl_decals_fadetime.value;
1355 VectorCopy(normal, p->vel2);
1356 VectorClear(p->vel);
1357 VectorAdd(p->org, normal, p->org);
1366 dist = DotProduct(p->vel, normal) * -p->bounce;
1367 VectorMA(p->vel, dist, normal, p->vel);
1368 if (DotProduct(p->vel, p->vel) < 0.03)
1369 VectorClear(p->vel);
1374 p->vel[2] -= p->gravity * gravity;
1376 p->alpha -= p->alphafade * frametime;
1378 if (p->alpha <= 0 || cl.time > p->die)
1386 f = p->friction * frametime;
1388 content = CL_PointQ1Contents(p->org);
1389 if (content != CONTENTS_EMPTY)
1392 VectorScale(p->vel, f, p->vel);
1395 if (p->type != pt_static)
1401 content = CL_PointQ1Contents(p->org);
1403 if (a != CONTENTS_EMPTY)
1405 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1407 p->scalex += frametime * 8;
1408 p->scaley += frametime * 8;
1409 //p->alpha -= bloodwaterfade;
1415 p->vel[2] -= gravity;
1419 content = CL_PointQ1Contents(p->org);
1420 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1427 if (cl.time > p->time2)
1430 p->time2 = cl.time + (rand() & 3) * 0.1;
1431 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1432 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1433 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1436 content = CL_PointQ1Contents(p->org);
1438 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1442 p->scalex += frametime * p->time2;
1443 p->scaley += frametime * p->time2;
1446 #ifndef WORKINGLQUAKE
1447 if (p->owner->model == p->ownermodel)
1449 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1450 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1451 if (cl.time > p->time2)
1453 p->alphafade = p->alpha / (p->die - cl.time);
1454 p->type = pt_decalfade;
1462 #ifndef WORKINGLQUAKE
1463 if (p->owner->model == p->ownermodel)
1465 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1466 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1473 while (cl.time > p->time2)
1476 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);
1480 Con_Printf("unknown particle type %i\n", p->type);
1486 cl_numparticles = maxparticle + 1;
1487 cl_freeparticle = 0;
1490 #define MAX_PARTICLETEXTURES 64
1491 // particletexture_t is a rectangle in the particlefonttexture
1494 rtexture_t *texture;
1495 float s1, t1, s2, t2;
1500 static int particlefonttexture;
1502 static rtexturepool_t *particletexturepool;
1503 static rtexture_t *particlefonttexture;
1505 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1507 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1509 #define PARTICLETEXTURESIZE 64
1510 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1512 static qbyte shadebubble(float dx, float dy, vec3_t light)
1516 dz = 1 - (dx*dx+dy*dy);
1517 if (dz > 0) // it does hit the sphere
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 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1530 VectorNormalize(normal);
1531 dot = DotProduct(normal, light);
1532 if (dot > 0.5) // interior reflection
1533 f += ((dot * 2) - 1);
1534 else if (dot < -0.5) // exterior reflection
1535 f += ((dot * -2) - 1);
1537 f += 16; // just to give it a haze so you can see the outline
1538 f = bound(0, f, 255);
1545 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1547 int basex, basey, y;
1548 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1549 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1550 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1551 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1552 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1553 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1554 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1555 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1558 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1561 float cx, cy, dx, dy, f, iradius;
1563 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1564 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1565 iradius = 1.0f / radius;
1566 alpha *= (1.0f / 255.0f);
1567 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1569 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1573 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1576 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1577 d[0] += f * (red - d[0]);
1578 d[1] += f * (green - d[1]);
1579 d[2] += f * (blue - d[2]);
1585 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1588 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1590 data[0] = bound(minr, data[0], maxr);
1591 data[1] = bound(ming, data[1], maxg);
1592 data[2] = bound(minb, data[2], maxb);
1596 void particletextureinvert(qbyte *data)
1599 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1601 data[0] = 255 - data[0];
1602 data[1] = 255 - data[1];
1603 data[2] = 255 - data[2];
1607 static void R_InitParticleTexture (void)
1609 int x, y, d, i, j, k, m;
1610 float dx, dy, radius, f, f2;
1611 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise3[64][64], data2[64][16][4];
1613 qbyte *particletexturedata;
1615 // a note: decals need to modulate (multiply) the background color to
1616 // properly darken it (stain), and they need to be able to alpha fade,
1617 // this is a very difficult challenge because it means fading to white
1618 // (no change to background) rather than black (darkening everything
1619 // behind the whole decal polygon), and to accomplish this the texture is
1620 // inverted (dark red blood on white background becomes brilliant cyan
1621 // and white on black background) so we can alpha fade it to black, then
1622 // we invert it again during the blendfunc to make it work...
1624 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1625 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1628 for (i = 0;i < 8;i++)
1630 memset(&data[0][0][0], 255, sizeof(data));
1633 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1634 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1636 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1638 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1639 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1641 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1642 d = (noise2[y][x] - 128) * 3 + 192;
1644 d = d * (1-(dx*dx+dy*dy));
1645 d = (d * noise1[y][x]) >> 7;
1646 d = bound(0, d, 255);
1647 data[y][x][3] = (qbyte) d;
1654 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1658 for (i = 0;i < 16;i++)
1660 memset(&data[0][0][0], 255, sizeof(data));
1661 radius = i * 3.0f / 4.0f / 16.0f;
1662 f2 = 255.0f * ((15.0f - i) / 15.0f);
1663 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1665 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1666 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1668 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1669 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1670 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1673 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1677 memset(&data[0][0][0], 255, sizeof(data));
1678 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1680 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1681 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1683 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1684 d = 256 * (1 - (dx*dx+dy*dy));
1685 d = bound(0, d, 255);
1686 data[y][x][3] = (qbyte) d;
1689 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1692 memset(&data[0][0][0], 255, sizeof(data));
1693 light[0] = 1;light[1] = 1;light[2] = 1;
1694 VectorNormalize(light);
1695 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1697 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1698 // stretch upper half of bubble by +50% and shrink lower half by -50%
1699 // (this gives an elongated teardrop shape)
1701 dy = (dy - 0.5f) * 2.0f;
1703 dy = (dy - 0.5f) / 1.5f;
1704 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1706 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1707 // shrink bubble width to half
1709 data[y][x][3] = shadebubble(dx, dy, light);
1712 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1715 memset(&data[0][0][0], 255, sizeof(data));
1716 light[0] = 1;light[1] = 1;light[2] = 1;
1717 VectorNormalize(light);
1718 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1720 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1721 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1723 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1724 data[y][x][3] = shadebubble(dx, dy, light);
1727 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1730 for (i = 0;i < 8;i++)
1732 memset(&data[0][0][0], 255, sizeof(data));
1733 for (k = 0;k < 24;k++)
1734 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1735 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1736 particletextureinvert(&data[0][0][0]);
1737 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1741 for (i = 0;i < 8;i++)
1743 memset(&data[0][0][0], 255, sizeof(data));
1745 for (j = 1;j < 10;j++)
1746 for (k = min(j, m - 1);k < m;k++)
1747 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1748 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1749 particletextureinvert(&data[0][0][0]);
1750 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1754 for (i = 0;i < 8;i++)
1756 memset(&data[0][0][0], 255, sizeof(data));
1757 for (k = 0;k < 12;k++)
1758 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1759 for (k = 0;k < 3;k++)
1760 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1761 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1762 particletextureinvert(&data[0][0][0]);
1763 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1767 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1768 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1769 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1773 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1776 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1777 if (!particlefonttexture)
1778 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1779 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1780 particletexture[i].texture = particlefonttexture;
1783 fractalnoise(&noise3[0][0], 64, 4);
1785 for (y = 0;y < 64;y++)
1787 dy = (y - 0.5f*64) / (64*0.5f+1);
1788 for (x = 0;x < 16;x++)
1790 dx = (x - 0.5f*16) / (16*0.5f+1);
1791 d = (1 - (dx*dx)) * noise3[y][x];
1792 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1793 data2[y][x][3] = 255;
1797 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1798 if (!particletexture[tex_beam].texture)
1799 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1800 particletexture[tex_beam].s1 = 0;
1801 particletexture[tex_beam].t1 = 0;
1802 particletexture[tex_beam].s2 = 1;
1803 particletexture[tex_beam].t2 = 1;
1805 Mem_Free(particletexturedata);
1808 static void r_part_start(void)
1810 particletexturepool = R_AllocTexturePool();
1811 R_InitParticleTexture ();
1814 static void r_part_shutdown(void)
1816 R_FreeTexturePool(&particletexturepool);
1819 static void r_part_newmap(void)
1821 cl_numparticles = 0;
1822 cl_freeparticle = 0;
1825 void R_Particles_Init (void)
1827 Cvar_RegisterVariable(&r_drawparticles);
1828 #ifdef WORKINGLQUAKE
1831 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1835 #ifdef WORKINGLQUAKE
1836 void R_InitParticles(void)
1838 CL_Particles_Init();
1843 float particle_vertex3f[12], particle_texcoord2f[8];
1845 #ifdef WORKINGLQUAKE
1846 void R_DrawParticle(particle_t *p)
1849 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1851 const particle_t *p = calldata1;
1854 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1855 particletexture_t *tex;
1857 VectorCopy(p->org, org);
1859 tex = &particletexture[p->texnum];
1860 cr = p->color[0] * (1.0f / 255.0f);
1861 cg = p->color[1] * (1.0f / 255.0f);
1862 cb = p->color[2] * (1.0f / 255.0f);
1863 ca = p->alpha * (1.0f / 255.0f);
1864 if (p->blendmode == PBLEND_MOD)
1875 #ifndef WORKINGLQUAKE
1876 if (fogenabled && p->blendmode != PBLEND_MOD)
1878 VectorSubtract(org, r_vieworigin, fogvec);
1879 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1884 if (p->blendmode == 0)
1886 cr += fogcolor[0] * fog;
1887 cg += fogcolor[1] * fog;
1888 cb += fogcolor[2] * fog;
1892 R_Mesh_Matrix(&r_identitymatrix);
1894 memset(&m, 0, sizeof(m));
1895 m.tex[0] = R_GetTexture(tex->texture);
1896 m.pointer_texcoord[0] = particle_texcoord2f;
1897 m.pointer_vertex = particle_vertex3f;
1900 GL_Color(cr, cg, cb, ca);
1902 if (p->blendmode == 0)
1903 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1904 else if (p->blendmode == 1)
1905 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1907 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1908 GL_DepthMask(false);
1911 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1913 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1916 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1918 VectorNegate(p->vel2, v);
1919 VectorVectors(v, right, up);
1922 VectorVectors(p->vel2, right, up);
1923 VectorScale(right, p->scalex, right);
1924 VectorScale(up, p->scaley, up);
1928 VectorScale(r_viewleft, -p->scalex, right);
1929 VectorScale(r_viewup, p->scaley, up);
1931 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1932 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1933 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1934 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1935 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1936 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1937 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1938 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1939 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1940 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1941 particle_vertex3f[10] = org[1] + right[1] - up[1];
1942 particle_vertex3f[11] = org[2] + right[2] - up[2];
1943 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1944 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1945 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1946 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1948 else if (p->orientation == PARTICLE_SPARK)
1950 VectorMA(p->org, -p->scaley, p->vel, v);
1951 VectorMA(p->org, p->scaley, p->vel, up2);
1952 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1953 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1954 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1955 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1956 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1958 else if (p->orientation == PARTICLE_BEAM)
1960 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1961 VectorSubtract(p->vel2, p->org, up);
1962 VectorNormalizeFast(up);
1963 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1964 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1965 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1966 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1967 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1968 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1971 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1974 if (p->blendmode == 0)
1975 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1976 else if (p->blendmode == 1)
1977 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1979 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1980 glColor4f(cr, cg, cb, ca);
1982 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1983 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1984 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1985 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1988 R_Mesh_Draw(4, 2, polygonelements);
1992 void R_DrawParticles (void)
1995 float minparticledist;
1998 #ifdef WORKINGLQUAKE
2002 // LordHavoc: early out conditions
2003 if ((!cl_numparticles) || (!r_drawparticles.integer))
2006 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2008 #ifdef WORKINGLQUAKE
2009 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2011 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2013 // LordHavoc: only render if not too close
2014 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2015 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2018 glDisable(GL_BLEND);
2019 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2021 // LordHavoc: only render if not too close
2022 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2027 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2028 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);