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_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);
1353 p->die = p->time2 + cl_decals_time.value + 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 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (p->alpha / cl_decals_fadetime.value) : 0;
1447 #ifndef WORKINGLQUAKE
1448 if (p->owner->model == p->ownermodel)
1450 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1451 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1458 while (cl.time > p->time2)
1461 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);
1465 Con_Printf("unknown particle type %i\n", p->type);
1471 cl_numparticles = maxparticle + 1;
1472 cl_freeparticle = 0;
1475 #define MAX_PARTICLETEXTURES 64
1476 // particletexture_t is a rectangle in the particlefonttexture
1479 rtexture_t *texture;
1480 float s1, t1, s2, t2;
1485 static int particlefonttexture;
1487 static rtexturepool_t *particletexturepool;
1488 static rtexture_t *particlefonttexture;
1490 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1492 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1494 #define PARTICLETEXTURESIZE 64
1495 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1497 static qbyte shadebubble(float dx, float dy, vec3_t light)
1501 dz = 1 - (dx*dx+dy*dy);
1502 if (dz > 0) // it does hit the sphere
1506 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1507 VectorNormalize(normal);
1508 dot = DotProduct(normal, light);
1509 if (dot > 0.5) // interior reflection
1510 f += ((dot * 2) - 1);
1511 else if (dot < -0.5) // exterior reflection
1512 f += ((dot * -2) - 1);
1514 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1515 VectorNormalize(normal);
1516 dot = DotProduct(normal, light);
1517 if (dot > 0.5) // interior reflection
1518 f += ((dot * 2) - 1);
1519 else if (dot < -0.5) // exterior reflection
1520 f += ((dot * -2) - 1);
1522 f += 16; // just to give it a haze so you can see the outline
1523 f = bound(0, f, 255);
1530 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1532 int basex, basey, y;
1533 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1534 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1535 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1536 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1537 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1538 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1539 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1540 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1543 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1546 float cx, cy, dx, dy, f, iradius;
1548 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1549 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1550 iradius = 1.0f / radius;
1551 alpha *= (1.0f / 255.0f);
1552 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1554 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1558 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1561 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1562 d[0] += f * (red - d[0]);
1563 d[1] += f * (green - d[1]);
1564 d[2] += f * (blue - d[2]);
1570 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1573 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1575 data[0] = bound(minr, data[0], maxr);
1576 data[1] = bound(ming, data[1], maxg);
1577 data[2] = bound(minb, data[2], maxb);
1581 void particletextureinvert(qbyte *data)
1584 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1586 data[0] = 255 - data[0];
1587 data[1] = 255 - data[1];
1588 data[2] = 255 - data[2];
1592 static void R_InitParticleTexture (void)
1594 int x, y, d, i, j, k, m;
1595 float dx, dy, radius, f, f2;
1596 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise3[64][64], data2[64][16][4];
1598 qbyte *particletexturedata;
1600 // a note: decals need to modulate (multiply) the background color to
1601 // properly darken it (stain), and they need to be able to alpha fade,
1602 // this is a very difficult challenge because it means fading to white
1603 // (no change to background) rather than black (darkening everything
1604 // behind the whole decal polygon), and to accomplish this the texture is
1605 // inverted (dark red blood on white background becomes brilliant cyan
1606 // and white on black background) so we can alpha fade it to black, then
1607 // we invert it again during the blendfunc to make it work...
1609 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1610 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1613 for (i = 0;i < 8;i++)
1615 memset(&data[0][0][0], 255, sizeof(data));
1618 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1619 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1621 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1623 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1624 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1626 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1627 d = (noise2[y][x] - 128) * 3 + 192;
1629 d = d * (1-(dx*dx+dy*dy));
1630 d = (d * noise1[y][x]) >> 7;
1631 d = bound(0, d, 255);
1632 data[y][x][3] = (qbyte) d;
1639 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1643 for (i = 0;i < 16;i++)
1645 memset(&data[0][0][0], 255, sizeof(data));
1646 radius = i * 3.0f / 4.0f / 16.0f;
1647 f2 = 255.0f * ((15.0f - i) / 15.0f);
1648 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1650 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1651 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1653 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1654 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1655 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1658 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1662 memset(&data[0][0][0], 255, sizeof(data));
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 d = 256 * (1 - (dx*dx+dy*dy));
1670 d = bound(0, d, 255);
1671 data[y][x][3] = (qbyte) d;
1674 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1677 memset(&data[0][0][0], 255, sizeof(data));
1678 light[0] = 1;light[1] = 1;light[2] = 1;
1679 VectorNormalize(light);
1680 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1682 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1683 // stretch upper half of bubble by +50% and shrink lower half by -50%
1684 // (this gives an elongated teardrop shape)
1686 dy = (dy - 0.5f) * 2.0f;
1688 dy = (dy - 0.5f) / 1.5f;
1689 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1691 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1692 // shrink bubble width to half
1694 data[y][x][3] = shadebubble(dx, dy, light);
1697 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1700 memset(&data[0][0][0], 255, sizeof(data));
1701 light[0] = 1;light[1] = 1;light[2] = 1;
1702 VectorNormalize(light);
1703 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1705 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1706 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1708 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f+1);
1709 data[y][x][3] = shadebubble(dx, dy, light);
1712 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1715 for (i = 0;i < 8;i++)
1717 memset(&data[0][0][0], 255, sizeof(data));
1718 for (k = 0;k < 24;k++)
1719 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1720 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1721 particletextureinvert(&data[0][0][0]);
1722 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1726 for (i = 0;i < 8;i++)
1728 memset(&data[0][0][0], 255, sizeof(data));
1730 for (j = 1;j < 10;j++)
1731 for (k = min(j, m - 1);k < m;k++)
1732 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1733 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1734 particletextureinvert(&data[0][0][0]);
1735 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1739 for (i = 0;i < 8;i++)
1741 memset(&data[0][0][0], 255, sizeof(data));
1742 for (k = 0;k < 12;k++)
1743 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1744 for (k = 0;k < 3;k++)
1745 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1746 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1747 particletextureinvert(&data[0][0][0]);
1748 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1752 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1753 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1754 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1758 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1761 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1762 if (!particlefonttexture)
1763 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1764 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1765 particletexture[i].texture = particlefonttexture;
1768 fractalnoise(&noise3[0][0], 64, 4);
1770 for (y = 0;y < 64;y++)
1772 dy = (y - 0.5f*64) / (64*0.5f+1);
1773 for (x = 0;x < 16;x++)
1775 dx = (x - 0.5f*16) / (16*0.5f+1);
1776 d = (1 - (dx*dx)) * noise3[y][x];
1777 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1778 data2[y][x][3] = 255;
1782 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1783 if (!particletexture[tex_beam].texture)
1784 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1785 particletexture[tex_beam].s1 = 0;
1786 particletexture[tex_beam].t1 = 0;
1787 particletexture[tex_beam].s2 = 1;
1788 particletexture[tex_beam].t2 = 1;
1790 Mem_Free(particletexturedata);
1793 static void r_part_start(void)
1795 particletexturepool = R_AllocTexturePool();
1796 R_InitParticleTexture ();
1799 static void r_part_shutdown(void)
1801 R_FreeTexturePool(&particletexturepool);
1804 static void r_part_newmap(void)
1806 cl_numparticles = 0;
1807 cl_freeparticle = 0;
1810 void R_Particles_Init (void)
1812 Cvar_RegisterVariable(&r_drawparticles);
1813 #ifdef WORKINGLQUAKE
1816 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1820 #ifdef WORKINGLQUAKE
1821 void R_InitParticles(void)
1823 CL_Particles_Init();
1828 float particle_vertex3f[12], particle_texcoord2f[8];
1830 #ifdef WORKINGLQUAKE
1831 void R_DrawParticle(particle_t *p)
1834 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1836 const particle_t *p = calldata1;
1839 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1840 particletexture_t *tex;
1842 VectorCopy(p->org, org);
1844 tex = &particletexture[p->texnum];
1845 cr = p->color[0] * (1.0f / 255.0f);
1846 cg = p->color[1] * (1.0f / 255.0f);
1847 cb = p->color[2] * (1.0f / 255.0f);
1848 ca = p->alpha * (1.0f / 255.0f);
1849 if (p->blendmode == PBLEND_MOD)
1860 #ifndef WORKINGLQUAKE
1861 if (fogenabled && p->blendmode != PBLEND_MOD)
1863 VectorSubtract(org, r_vieworigin, fogvec);
1864 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1869 if (p->blendmode == 0)
1871 cr += fogcolor[0] * fog;
1872 cg += fogcolor[1] * fog;
1873 cb += fogcolor[2] * fog;
1877 R_Mesh_Matrix(&r_identitymatrix);
1879 memset(&m, 0, sizeof(m));
1880 m.tex[0] = R_GetTexture(tex->texture);
1881 m.pointer_texcoord[0] = particle_texcoord2f;
1882 m.pointer_vertex = particle_vertex3f;
1885 GL_Color(cr, cg, cb, ca);
1887 if (p->blendmode == 0)
1888 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1889 else if (p->blendmode == 1)
1890 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1892 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1893 GL_DepthMask(false);
1896 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1898 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1901 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1903 VectorNegate(p->vel2, v);
1904 VectorVectors(v, right, up);
1907 VectorVectors(p->vel2, right, up);
1908 VectorScale(right, p->scalex, right);
1909 VectorScale(up, p->scaley, up);
1913 VectorScale(r_viewleft, -p->scalex, right);
1914 VectorScale(r_viewup, p->scaley, up);
1916 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1917 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1918 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1919 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1920 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1921 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1922 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1923 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1924 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1925 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1926 particle_vertex3f[10] = org[1] + right[1] - up[1];
1927 particle_vertex3f[11] = org[2] + right[2] - up[2];
1928 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1929 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1930 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1931 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1933 else if (p->orientation == PARTICLE_SPARK)
1935 VectorMA(p->org, -p->scaley, p->vel, v);
1936 VectorMA(p->org, p->scaley, p->vel, up2);
1937 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1938 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1939 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1940 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1941 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1943 else if (p->orientation == PARTICLE_BEAM)
1945 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1946 VectorSubtract(p->vel2, p->org, up);
1947 VectorNormalizeFast(up);
1948 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1949 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1950 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1951 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1952 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1953 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1956 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1959 if (p->blendmode == 0)
1960 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1961 else if (p->blendmode == 1)
1962 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1964 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1965 glColor4f(cr, cg, cb, ca);
1967 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1968 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1969 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1970 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1973 R_Mesh_Draw(4, 2, polygonelements);
1977 void R_DrawParticles (void)
1980 float minparticledist;
1983 #ifdef WORKINGLQUAKE
1987 // LordHavoc: early out conditions
1988 if ((!cl_numparticles) || (!r_drawparticles.integer))
1991 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1993 #ifdef WORKINGLQUAKE
1994 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1996 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1998 // LordHavoc: only render if not too close
1999 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2000 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2003 glDisable(GL_BLEND);
2004 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2006 // LordHavoc: only render if not too close
2007 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2012 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2014 if (p->type == pt_decal)
2015 R_DrawParticleCallback(p, 0);
2017 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);