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 #define CL_RocketTrail2 R_RocketTrail2
44 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
46 vec3_t right1, right2, diff, normal;
48 VectorSubtract (org2, org1, normal);
49 VectorNormalizeFast (normal);
51 // calculate 'right' vector for start
52 VectorSubtract (r_vieworigin, org1, diff);
53 VectorNormalizeFast (diff);
54 CrossProduct (normal, diff, right1);
56 // calculate 'right' vector for end
57 VectorSubtract (r_vieworigin, org2, diff);
58 VectorNormalizeFast (diff);
59 CrossProduct (normal, diff, right2);
61 vert[ 0] = org1[0] + width * right1[0];
62 vert[ 1] = org1[1] + width * right1[1];
63 vert[ 2] = org1[2] + width * right1[2];
64 vert[ 3] = org1[0] - width * right1[0];
65 vert[ 4] = org1[1] - width * right1[1];
66 vert[ 5] = org1[2] - width * right1[2];
67 vert[ 6] = org2[0] - width * right2[0];
68 vert[ 7] = org2[1] - width * right2[1];
69 vert[ 8] = org2[2] - width * right2[2];
70 vert[ 9] = org2[0] + width * right2[0];
71 vert[10] = org2[1] + width * right2[1];
72 vert[11] = org2[2] + width * right2[2];
74 void fractalnoise(qbyte *noise, int size, int startgrid)
76 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
78 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
80 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
81 if (size != (1 << sizepower))
82 Sys_Error("fractalnoise: size must be power of 2\n");
84 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
85 if (startgrid != (1 << gridpower))
86 Sys_Error("fractalnoise: grid must be power of 2\n");
88 startgrid = bound(0, startgrid, size);
90 amplitude = 0xFFFF; // this gets halved before use
91 noisebuf = malloc(size*size*sizeof(int));
92 memset(noisebuf, 0, size*size*sizeof(int));
94 for (g2 = startgrid;g2;g2 >>= 1)
96 // brownian motion (at every smaller level there is random behavior)
98 for (y = 0;y < size;y += g2)
99 for (x = 0;x < size;x += g2)
100 n(x,y) += (rand()&litude);
105 // subdivide, diamond-square algorithm (really this has little to do with squares)
107 for (y = 0;y < size;y += g2)
108 for (x = 0;x < size;x += g2)
109 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
111 for (y = 0;y < size;y += g2)
112 for (x = 0;x < size;x += g2)
114 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
115 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
119 // find range of noise values
121 for (y = 0;y < size;y++)
122 for (x = 0;x < size;x++)
124 if (n(x,y) < min) min = n(x,y);
125 if (n(x,y) > max) max = n(x,y);
129 // normalize noise and copy to output
130 for (y = 0;y < size;y++)
131 for (x = 0;x < size;x++)
132 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
136 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
140 right[0] = forward[2];
141 right[1] = -forward[0];
142 right[2] = forward[1];
144 d = DotProduct(forward, right);
145 right[0] -= d * forward[0];
146 right[1] -= d * forward[1];
147 right[2] -= d * forward[2];
148 VectorNormalizeFast(right);
149 CrossProduct(right, forward, up);
153 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
155 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int hitbmodels, void **hitent, int hitsupercontentsmask)
162 memset (&trace, 0, sizeof(trace));
164 VectorCopy (end, trace.endpos);
166 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
168 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
170 VectorCopy(trace.endpos, impact);
171 VectorCopy(trace.plane.normal, normal);
172 return trace.fraction;
175 #include "cl_collision.h"
179 #define MAX_PARTICLES 32768 // default max # of particles at one time
180 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
184 pt_dead, pt_static, pt_rain, pt_bubble, pt_blood, pt_grow, pt_decal, pt_decalfade, pt_ember
190 PARTICLE_BILLBOARD = 0,
192 PARTICLE_ORIENTED_DOUBLESIDED = 2,
205 typedef struct particle_s
216 float alpha; // 0-255
217 float alphafade; // how much alpha reduces per second
218 float time2; // used for various things (snow fluttering, for example)
219 float bounce; // how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide, 2 = keep bouncing forever, 1.5 is typical)
220 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
222 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
223 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
224 float pressure; // if non-zero, apply pressure to other particles
226 #ifndef WORKINGLQUAKE
227 entity_render_t *owner; // decal stuck to this entity
228 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
229 vec3_t relativeorigin; // decal at this location in entity's coordinate space
230 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
235 static int particlepalette[256] =
237 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
238 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
239 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
240 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
241 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
242 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
243 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
244 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
245 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
246 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
247 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
248 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
249 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
250 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
251 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
252 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
253 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
254 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
255 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
256 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
257 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
258 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
259 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
260 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
261 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
262 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
263 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
264 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
265 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
266 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
267 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
268 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
271 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
273 // texture numbers in particle font
274 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
275 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
276 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
277 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
278 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
279 static const int tex_particle = 63;
280 static const int tex_bubble = 62;
281 static const int tex_raindrop = 61;
282 static const int tex_beam = 60;
284 static int cl_maxparticles;
285 static int cl_numparticles;
286 static int cl_freeparticle;
287 static particle_t *particles;
289 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
290 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
291 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
292 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
293 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
294 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
295 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
296 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
297 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
298 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
299 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
300 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
301 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
302 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
303 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
304 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
305 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
306 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
307 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
308 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
310 #ifndef WORKINGLQUAKE
311 static mempool_t *cl_part_mempool;
314 void CL_Particles_Clear(void)
318 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
326 void CL_ReadPointFile_f (void);
327 void CL_Particles_Init (void)
331 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;
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, 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);
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);
1216 // advance to next time and position
1218 VectorMA (pos, dec, vec, pos);
1220 #ifndef WORKINGLQUAKE
1221 ent->persistent.trail_time = len;
1225 void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1229 if (!cl_particles.integer) return;
1230 if (!cl_particles_smoke.integer) return;
1232 VectorCopy(start, pos);
1233 VectorSubtract(end, start, vec);
1234 #ifdef WORKINGLQUAKE
1235 len = VectorNormalize(vec);
1237 len = VectorNormalizeLength(vec);
1239 color = particlepalette[color];
1240 dec = 3.0f / cl_particles_quality.value;
1243 particle(pt_static, PARTICLE_BILLBOARD, color, color, tex_particle, false, PBLEND_ALPHA, 5, 5, 128 / cl_particles_quality.value, 320 / cl_particles_quality.value, 9999, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1245 VectorMA(pos, dec, vec, pos);
1249 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1251 int tempcolor2, cr, cg, cb;
1255 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1256 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);
1259 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1262 if (!cl_particles.integer) return;
1265 if (cl_particles_smoke.integer)
1266 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1267 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);
1270 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1273 if (!cl_particles.integer) return;
1275 if (cl_stainmaps.integer)
1276 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1277 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1280 if (cl_particles_smoke.integer)
1281 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1282 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);
1285 if (cl_particles_sparks.integer)
1286 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1287 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);
1295 void CL_MoveParticles (void)
1298 int i, maxparticle, j, a, content;
1299 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1300 #ifdef WORKINGLQUAKE
1303 entity_render_t *hitent;
1306 // LordHavoc: early out condition
1307 if (!cl_numparticles)
1309 cl_freeparticle = 0;
1313 #ifdef WORKINGLQUAKE
1314 frametime = cl.frametime;
1316 frametime = cl.time - cl.oldtime;
1318 gravity = frametime * sv_gravity.value;
1319 dvel = 1+4*frametime;
1320 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1324 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1330 VectorCopy(p->org, p->oldorg);
1331 VectorMA(p->org, frametime, p->vel, p->org);
1332 VectorCopy(p->org, org);
1335 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1337 VectorCopy(v, p->org);
1340 // assume it's blood (lame, but...)
1341 #ifndef WORKINGLQUAKE
1342 if (cl_stainmaps.integer)
1343 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));
1345 if (!cl_decals.integer)
1352 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1353 // convert from a blood particle to a blood decal
1354 p->texnum = tex_blooddecal[rand()&7];
1355 #ifndef WORKINGLQUAKE
1357 p->ownermodel = hitent->model;
1358 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1359 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1360 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1362 p->time2 = cl.time + cl_decals_time.value;
1363 p->die = p->time2 + cl_decals_fadetime.value;
1365 VectorCopy(normal, p->vel2);
1366 VectorClear(p->vel);
1367 VectorAdd(p->org, normal, p->org);
1376 dist = DotProduct(p->vel, normal) * -p->bounce;
1377 VectorMA(p->vel, dist, normal, p->vel);
1378 if (DotProduct(p->vel, p->vel) < 0.03)
1379 VectorClear(p->vel);
1384 p->vel[2] -= p->gravity * gravity;
1386 p->alpha -= p->alphafade * frametime;
1388 if (p->alpha <= 0 || cl.time > p->die)
1396 f = p->friction * frametime;
1398 content = CL_PointQ1Contents(p->org);
1399 if (content != CONTENTS_EMPTY)
1402 VectorScale(p->vel, f, p->vel);
1405 if (p->type != pt_static)
1411 content = CL_PointQ1Contents(p->org);
1413 if (a != CONTENTS_EMPTY)
1415 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1417 p->scalex += frametime * 8;
1418 p->scaley += frametime * 8;
1419 //p->alpha -= bloodwaterfade;
1425 p->vel[2] -= gravity;
1429 content = CL_PointQ1Contents(p->org);
1430 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1437 if (cl.time > p->time2)
1440 p->time2 = cl.time + (rand() & 3) * 0.1;
1441 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1442 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1443 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1446 content = CL_PointQ1Contents(p->org);
1448 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1452 p->scalex += frametime * p->time2;
1453 p->scaley += frametime * p->time2;
1456 #ifndef WORKINGLQUAKE
1457 if (p->owner->model == p->ownermodel)
1459 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1460 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1461 if (cl.time > p->time2)
1463 p->alphafade = p->alpha / (p->die - cl.time);
1464 p->type = pt_decalfade;
1472 #ifndef WORKINGLQUAKE
1473 if (p->owner->model == p->ownermodel)
1475 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1476 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1483 while (cl.time > p->time2)
1486 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);
1490 Con_Printf("unknown particle type %i\n", p->type);
1496 cl_numparticles = maxparticle + 1;
1497 cl_freeparticle = 0;
1500 #define MAX_PARTICLETEXTURES 64
1501 // particletexture_t is a rectangle in the particlefonttexture
1504 rtexture_t *texture;
1505 float s1, t1, s2, t2;
1510 static int particlefonttexture;
1512 static rtexturepool_t *particletexturepool;
1513 static rtexture_t *particlefonttexture;
1515 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1517 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1519 static qbyte shadebubble(float dx, float dy, vec3_t light)
1523 dz = 1 - (dx*dx+dy*dy);
1524 if (dz > 0) // it does hit the sphere
1528 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1529 VectorNormalize(normal);
1530 dot = DotProduct(normal, light);
1531 if (dot > 0.5) // interior reflection
1532 f += ((dot * 2) - 1);
1533 else if (dot < -0.5) // exterior reflection
1534 f += ((dot * -2) - 1);
1536 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1537 VectorNormalize(normal);
1538 dot = DotProduct(normal, light);
1539 if (dot > 0.5) // interior reflection
1540 f += ((dot * 2) - 1);
1541 else if (dot < -0.5) // exterior reflection
1542 f += ((dot * -2) - 1);
1544 f += 16; // just to give it a haze so you can see the outline
1545 f = bound(0, f, 255);
1552 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1554 int basex, basey, y;
1555 basex = ((texnum >> 0) & 7) * 32;
1556 basey = ((texnum >> 3) & 7) * 32;
1557 particletexture[texnum].s1 = (basex + 1) / 256.0f;
1558 particletexture[texnum].t1 = (basey + 1) / 256.0f;
1559 particletexture[texnum].s2 = (basex + 31) / 256.0f;
1560 particletexture[texnum].t2 = (basey + 31) / 256.0f;
1561 for (y = 0;y < 32;y++)
1562 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 4);
1565 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1568 float cx, cy, dx, dy, f, iradius;
1570 cx = lhrandom(radius + 1, 30 - radius);
1571 cy = lhrandom(radius + 1, 30 - radius);
1572 iradius = 1.0f / radius;
1573 alpha *= (1.0f / 255.0f);
1574 for (y = 0;y < 32;y++)
1576 for (x = 0;x < 32;x++)
1580 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1583 d = data + (y * 32 + x) * 4;
1584 d[0] += f * (red - d[0]);
1585 d[1] += f * (green - d[1]);
1586 d[2] += f * (blue - d[2]);
1592 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1595 for (i = 0;i < 32*32;i++, data += 4)
1597 data[0] = bound(minr, data[0], maxr);
1598 data[1] = bound(ming, data[1], maxg);
1599 data[2] = bound(minb, data[2], maxb);
1603 void particletextureinvert(qbyte *data)
1606 for (i = 0;i < 32*32;i++, data += 4)
1608 data[0] = 255 - data[0];
1609 data[1] = 255 - data[1];
1610 data[2] = 255 - data[2];
1614 static void R_InitParticleTexture (void)
1616 int x, y, d, i, j, k, m;
1617 float dx, dy, radius, f, f2;
1618 qbyte data[32][32][4], noise1[64][64], noise2[64][64], data2[64][16][4];
1620 qbyte particletexturedata[256*256*4];
1622 // a note: decals need to modulate (multiply) the background color to
1623 // properly darken it (stain), and they need to be able to alpha fade,
1624 // this is a very difficult challenge because it means fading to white
1625 // (no change to background) rather than black (darkening everything
1626 // behind the whole decal polygon), and to accomplish this the texture is
1627 // inverted (dark red blood on white background becomes brilliant cyan
1628 // and white on black background) so we can alpha fade it to black, then
1629 // we invert it again during the blendfunc to make it work...
1631 memset(particletexturedata, 255, sizeof(particletexturedata));
1634 for (i = 0;i < 8;i++)
1636 memset(&data[0][0][0], 255, sizeof(data));
1639 fractalnoise(&noise1[0][0], 64, 4);
1640 fractalnoise(&noise2[0][0], 64, 8);
1642 for (y = 0;y < 32;y++)
1645 for (x = 0;x < 32;x++)
1648 d = (noise2[y][x] - 128) * 3 + 192;
1650 d = d * (256 - (int) (dx*dx+dy*dy)) / 256;
1651 d = (d * noise1[y][x]) >> 7;
1652 d = bound(0, d, 255);
1653 data[y][x][3] = (qbyte) d;
1660 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1664 for (i = 0;i < 16;i++)
1666 memset(&data[0][0][0], 255, sizeof(data));
1667 radius = i * 3.0f / 16.0f;
1668 f2 = 255.0f * ((15.0f - i) / 15.0f);
1669 for (y = 0;y < 32;y++)
1671 dy = (y - 16) * 0.25f;
1672 for (x = 0;x < 32;x++)
1674 dx = (x - 16) * 0.25f;
1675 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
1676 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1679 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1683 memset(&data[0][0][0], 255, sizeof(data));
1684 for (y = 0;y < 32;y++)
1687 for (x = 0;x < 32;x++)
1690 d = (256 - (dx*dx+dy*dy));
1691 d = bound(0, d, 255);
1692 data[y][x][3] = (qbyte) d;
1695 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1698 memset(&data[0][0][0], 255, sizeof(data));
1699 light[0] = 1;light[1] = 1;light[2] = 1;
1700 VectorNormalize(light);
1701 for (y = 0;y < 32;y++)
1702 for (x = 0;x < 32;x++)
1703 data[y][x][3] = shadebubble((x - 16) * (1.0 / 8.0), y < 24 ? (y - 24) * (1.0 / 24.0) : (y - 24) * (1.0 / 8.0), 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 < 32;y++)
1711 for (x = 0;x < 32;x++)
1712 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
1713 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1716 for (i = 0;i < 8;i++)
1718 memset(&data[0][0][0], 255, sizeof(data));
1719 for (k = 0;k < 24;k++)
1720 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 160);
1721 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1722 particletextureinvert(&data[0][0][0]);
1723 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1727 for (i = 0;i < 8;i++)
1729 memset(&data[0][0][0], 255, sizeof(data));
1730 for (k = 0;k < 24;k++)
1731 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 96);
1732 for (j = 3;j < 7;j++)
1733 for (k = 0, m = rand() % 12;k < m;k++)
1734 particletextureblotch(&data[0][0][0], j, 96, 0, 0, 192);
1735 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1736 particletextureinvert(&data[0][0][0]);
1737 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1741 for (i = 0;i < 8;i++)
1743 memset(&data[0][0][0], 255, sizeof(data));
1744 for (k = 0;k < 12;k++)
1745 particletextureblotch(&data[0][0][0], 2, 0, 0, 0, 128);
1746 for (k = 0;k < 3;k++)
1747 particletextureblotch(&data[0][0][0], 14, 0, 0, 0, 160);
1748 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1749 particletextureinvert(&data[0][0][0]);
1750 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1754 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1755 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1756 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1758 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1759 if (!particlefonttexture)
1760 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1761 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1762 particletexture[i].texture = particlefonttexture;
1765 fractalnoise(&noise1[0][0], 64, 4);
1767 for (y = 0;y < 64;y++)
1769 for (x = 0;x < 16;x++)
1775 d = d * d * noise1[y][x] / (7 * 7);
1776 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1777 data2[y][x][3] = 255;
1781 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1782 if (!particletexture[tex_beam].texture)
1783 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1784 particletexture[tex_beam].s1 = 0;
1785 particletexture[tex_beam].t1 = 0;
1786 particletexture[tex_beam].s2 = 1;
1787 particletexture[tex_beam].t2 = 1;
1791 static void r_part_start(void)
1793 particletexturepool = R_AllocTexturePool();
1794 R_InitParticleTexture ();
1797 static void r_part_shutdown(void)
1799 R_FreeTexturePool(&particletexturepool);
1802 static void r_part_newmap(void)
1804 cl_numparticles = 0;
1805 cl_freeparticle = 0;
1808 void R_Particles_Init (void)
1810 Cvar_RegisterVariable(&r_drawparticles);
1811 #ifdef WORKINGLQUAKE
1814 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1818 #ifdef WORKINGLQUAKE
1819 void R_InitParticles(void)
1821 CL_Particles_Init();
1826 float particle_vertex3f[12], particle_texcoord2f[8];
1828 #ifdef WORKINGLQUAKE
1829 void R_DrawParticle(particle_t *p)
1832 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1834 const particle_t *p = calldata1;
1837 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1838 particletexture_t *tex;
1840 VectorCopy(p->org, org);
1842 tex = &particletexture[p->texnum];
1843 cr = p->color[0] * (1.0f / 255.0f);
1844 cg = p->color[1] * (1.0f / 255.0f);
1845 cb = p->color[2] * (1.0f / 255.0f);
1846 ca = p->alpha * (1.0f / 255.0f);
1847 if (p->blendmode == PBLEND_MOD)
1858 #ifndef WORKINGLQUAKE
1859 if (fogenabled && p->blendmode != PBLEND_MOD)
1861 VectorSubtract(org, r_vieworigin, fogvec);
1862 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1867 if (p->blendmode == 0)
1869 cr += fogcolor[0] * fog;
1870 cg += fogcolor[1] * fog;
1871 cb += fogcolor[2] * fog;
1875 R_Mesh_Matrix(&r_identitymatrix);
1877 memset(&m, 0, sizeof(m));
1878 m.tex[0] = R_GetTexture(tex->texture);
1879 m.pointer_texcoord[0] = particle_texcoord2f;
1880 m.pointer_vertex = particle_vertex3f;
1883 GL_Color(cr, cg, cb, ca);
1885 if (p->blendmode == 0)
1886 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1887 else if (p->blendmode == 1)
1888 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1890 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1891 GL_DepthMask(false);
1894 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1896 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1899 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1901 VectorNegate(p->vel2, v);
1902 VectorVectors(v, right, up);
1905 VectorVectors(p->vel2, right, up);
1906 VectorScale(right, p->scalex, right);
1907 VectorScale(up, p->scaley, up);
1911 VectorScale(r_viewleft, -p->scalex, right);
1912 VectorScale(r_viewup, p->scaley, up);
1914 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1915 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1916 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1917 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1918 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1919 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1920 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1921 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1922 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1923 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1924 particle_vertex3f[10] = org[1] + right[1] - up[1];
1925 particle_vertex3f[11] = org[2] + right[2] - up[2];
1926 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1927 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1928 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1929 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1931 else if (p->orientation == PARTICLE_SPARK)
1933 VectorMA(p->org, -p->scaley, p->vel, v);
1934 VectorMA(p->org, p->scaley, p->vel, up2);
1935 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1936 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1937 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1938 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1939 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1941 else if (p->orientation == PARTICLE_BEAM)
1943 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1944 VectorSubtract(p->vel2, p->org, up);
1945 VectorNormalizeFast(up);
1946 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1947 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1948 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1949 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1950 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1951 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1954 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1957 if (p->blendmode == 0)
1958 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1959 else if (p->blendmode == 1)
1960 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1962 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1963 glColor4f(cr, cg, cb, ca);
1965 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1966 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1967 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1968 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1971 R_Mesh_Draw(4, 2, polygonelements);
1975 void R_DrawParticles (void)
1978 float minparticledist;
1981 #ifdef WORKINGLQUAKE
1985 // LordHavoc: early out conditions
1986 if ((!cl_numparticles) || (!r_drawparticles.integer))
1989 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1991 #ifdef WORKINGLQUAKE
1992 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1994 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1996 // LordHavoc: only render if not too close
1997 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1998 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2001 glDisable(GL_BLEND);
2002 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2004 // LordHavoc: only render if not too close
2005 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2010 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2011 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);