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_TeleportSplash R_TeleportSplash
41 #define CL_BlobExplosion R_BlobExplosion
42 #define CL_RunParticleEffect R_RunParticleEffect
43 #define CL_LavaSplash R_LavaSplash
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 PARTICLE_BILLBOARD = 0,
186 PARTICLE_ORIENTED_DOUBLESIDED = 2,
199 typedef struct particletype_s
202 porientation_t orientation;
208 pt_alphastatic, pt_static, pt_spark, pt_beam, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_grow, pt_decal, pt_entityparticle, pt_total
212 // must match ptype_t values
213 particletype_t particletype[pt_total] =
215 {PBLEND_ALPHA, PARTICLE_BILLBOARD}, //pt_alphastatic
216 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_static
217 {PBLEND_ADD, PARTICLE_SPARK}, //pt_spark
218 {PBLEND_ADD, PARTICLE_BEAM}, //pt_beam
219 {PBLEND_ADD, PARTICLE_SPARK}, //pt_rain
220 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED}, //pt_raindecal
221 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_snow
222 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_bubble
223 {PBLEND_MOD, PARTICLE_BILLBOARD}, //pt_blood
224 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_grow
225 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED}, //pt_decal
226 {PBLEND_ALPHA, PARTICLE_BILLBOARD}, //pt_entityparticle
229 typedef struct particle_s
231 particletype_t *type;
234 vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam
236 float alpha; // 0-255
237 float alphafade; // how much alpha reduces per second
238 float time2; // used for snow fluttering and decal fade
239 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)
240 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
241 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
243 #ifndef WORKINGLQUAKE
244 entity_render_t *owner; // decal stuck to this entity
245 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
246 vec3_t relativeorigin; // decal at this location in entity's coordinate space
247 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
252 static int particlepalette[256] =
254 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
255 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
256 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
257 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
258 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
259 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
260 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
261 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
262 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
263 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
264 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
265 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
266 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
267 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
268 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
269 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
270 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
271 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
272 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
273 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
274 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
275 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
276 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
277 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
278 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
279 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
280 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
281 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
282 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
283 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
284 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
285 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
288 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
290 // texture numbers in particle font
291 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
292 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
293 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
294 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
295 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
296 static const int tex_particle = 63;
297 static const int tex_bubble = 62;
298 static const int tex_raindrop = 61;
299 static const int tex_beam = 60;
301 static int cl_maxparticles;
302 static int cl_numparticles;
303 static int cl_freeparticle;
304 static particle_t *particles;
306 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
307 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
308 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
309 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
310 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
311 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
312 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
313 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
314 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
315 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
316 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
317 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
318 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
319 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
320 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
321 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
322 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
323 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
324 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
325 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
327 #ifndef WORKINGLQUAKE
328 static mempool_t *cl_part_mempool;
331 void CL_Particles_Clear(void)
335 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
343 void CL_ReadPointFile_f (void);
344 void CL_Particles_Init (void)
348 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
349 i = COM_CheckParm ("-particles");
351 if (i && i < com_argc - 1)
353 cl_maxparticles = (int)(atoi(com_argv[i+1]));
354 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
355 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
358 cl_maxparticles = MAX_PARTICLES;
360 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
362 Cvar_RegisterVariable (&cl_particles);
363 Cvar_RegisterVariable (&cl_particles_quality);
364 Cvar_RegisterVariable (&cl_particles_size);
365 Cvar_RegisterVariable (&cl_particles_bloodshowers);
366 Cvar_RegisterVariable (&cl_particles_blood);
367 Cvar_RegisterVariable (&cl_particles_blood_alpha);
368 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
369 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
370 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
371 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
372 Cvar_RegisterVariable (&cl_particles_explosions_shell);
373 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
374 Cvar_RegisterVariable (&cl_particles_smoke);
375 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
376 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
377 Cvar_RegisterVariable (&cl_particles_sparks);
378 Cvar_RegisterVariable (&cl_particles_bubbles);
379 Cvar_RegisterVariable (&cl_decals);
380 Cvar_RegisterVariable (&cl_decals_time);
381 Cvar_RegisterVariable (&cl_decals_fadetime);
384 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
386 cl_part_mempool = Mem_AllocPool("CL_Part", 0, NULL);
387 particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
389 CL_Particles_Clear();
392 void CL_Particles_Shutdown (void)
395 // No clue what to do here...
397 Mem_FreePool (&cl_part_mempool);
401 // list of all 26 parameters:
402 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
403 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
404 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
405 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
406 // palpha - opacity of particle as 0-255 (can be more than 255)
407 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
408 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
409 // pgravity - how much effect gravity has on the particle (0-1)
410 // 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
411 // px,py,pz - starting origin of particle
412 // pvx,pvy,pvz - starting velocity of particle
413 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
414 particle_t *particle(particletype_t *ptype, int pcolor1, int pcolor2, int ptex, float psize, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pfriction)
417 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
418 ptempcolor = (pcolor1);
419 ptempcolor2 = (pcolor2);
420 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
421 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
422 pcb2 = (ptempcolor2) & 0xFF;
423 if (ptempcolor != ptempcolor2)
425 pcr1 = ((ptempcolor) >> 16) & 0xFF;
426 pcg1 = ((ptempcolor) >> 8) & 0xFF;
427 pcb1 = (ptempcolor) & 0xFF;
428 ptempcolor = rand() & 0xFF;
429 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
430 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
431 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
433 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
434 if (cl_freeparticle >= cl_maxparticles)
436 part = &particles[cl_freeparticle++];
437 if (cl_numparticles < cl_freeparticle)
438 cl_numparticles = cl_freeparticle;
439 memset(part, 0, sizeof(*part));
440 part->type = (ptype);
441 part->color[0] = pcr2;
442 part->color[1] = pcg2;
443 part->color[2] = pcb2;
444 part->color[3] = 0xFF;
446 part->size = (psize);
447 part->alpha = (palpha);
448 part->alphafade = (palphafade);
449 part->gravity = (pgravity);
450 part->bounce = (pbounce);
454 part->vel[0] = (pvx);
455 part->vel[1] = (pvy);
456 part->vel[2] = (pvz);
458 part->friction = (pfriction);
462 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
465 if (!cl_decals.integer)
467 p = particle(particletype + pt_decal, color1, color2, texnum, size, alpha, 0, 0, 0, org[0] + normal[0], org[1] + normal[1], org[2] + normal[2], normal[0], normal[1], normal[2], 0);
471 #ifndef WORKINGLQUAKE
473 p->ownermodel = p->owner->model;
474 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
475 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
476 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
481 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
484 float bestfrac, bestorg[3], bestnormal[3];
485 float frac, v[3], normal[3], org2[3];
487 void *besthitent = NULL, *hitent;
489 entity_render_t *besthitent = NULL, *hitent;
492 for (i = 0;i < 32;i++)
495 VectorMA(org, maxdist, org2, org2);
496 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
501 VectorCopy(v, bestorg);
502 VectorCopy(normal, bestnormal);
506 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
514 void CL_EntityParticles (entity_t *ent)
518 float sp, sy, cp, cy;
522 static vec3_t avelocities[NUMVERTEXNORMALS];
523 if (!cl_particles.integer) return;
528 if (!avelocities[0][0])
529 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
530 avelocities[0][i] = (rand()&255) * 0.01;
532 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
534 angle = cl.time * avelocities[i][0];
537 angle = cl.time * avelocities[i][1];
546 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 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);
548 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 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);
554 void CL_ReadPointFile_f (void)
558 char *pointfile = NULL, *pointfilepos, *t, tchar;
559 char name[MAX_OSPATH];
564 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
565 strlcat (name, ".pts", sizeof (name));
567 pointfile = COM_LoadTempFile (name);
569 pointfile = FS_LoadFile(name, tempmempool, true);
573 Con_Printf("Could not open %s\n", name);
577 Con_Printf("Reading %s...\n", name);
580 pointfilepos = pointfile;
581 while (*pointfilepos)
583 while (*pointfilepos == '\n' || *pointfilepos == '\r')
588 while (*t && *t != '\n' && *t != '\r')
592 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
598 VectorCopy(org, leakorg);
601 if (cl_numparticles < cl_maxparticles - 3)
604 particle(particletype + pt_static, particlepalette[(-c)&15], particlepalette[(-c)&15], tex_particle, 2, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0);
607 #ifndef WORKINGLQUAKE
610 VectorCopy(leakorg, org);
611 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
613 particle(particletype + pt_beam, 0xFF0000, 0xFF0000, tex_beam, 64, 255, 0, 0, 0, org[0] - 4096, org[1], org[2], org[0] + 4096, org[1], org[2], 0);
614 particle(particletype + pt_beam, 0x00FF00, 0x00FF00, tex_beam, 64, 255, 0, 0, 0, org[0], org[1] - 4096, org[2], org[0], org[1] + 4096, org[2], 0);
615 particle(particletype + pt_beam, 0x0000FF, 0x0000FF, tex_beam, 64, 255, 0, 0, 0, org[0], org[1], org[2] - 4096, org[0], org[1], org[2] + 4096, 0);
620 CL_ParseParticleEffect
622 Parse an effect out of the server message
625 void CL_ParseParticleEffect (void)
628 int i, count, msgcount, color;
630 MSG_ReadVector(org, cl.protocol);
631 for (i=0 ; i<3 ; i++)
632 dir[i] = MSG_ReadChar () * (1.0/16);
633 msgcount = MSG_ReadByte ();
634 color = MSG_ReadByte ();
641 if (cl_particles_blood_bloodhack.integer)
646 CL_BloodPuff(org, dir, count / 2);
652 CL_BloodPuff(org, dir, count / 2);
656 CL_RunParticleEffect (org, dir, color, count);
665 void CL_ParticleExplosion (vec3_t org)
670 if (cl_stainmaps.integer)
671 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
672 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
674 i = CL_PointSuperContents(org);
675 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
677 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
678 for (i = 0;i < 128 * cl_particles_quality.value;i++)
679 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, (1.0f / cl_particles_quality.value) * lhrandom(128, 255), (1.0f / cl_particles_quality.value) * 128, -0.125, 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), (1.0 / 16.0));
683 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
685 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
687 for (i = 0;i < 32;i++)
692 v2[0] = lhrandom(-48, 48);
693 v2[1] = lhrandom(-48, 48);
694 v2[2] = lhrandom(-48, 48);
696 for (k = 0;k < 16;k++)
698 v[0] = org[0] + lhrandom(-48, 48);
699 v[1] = org[1] + lhrandom(-48, 48);
700 v[2] = org[2] + lhrandom(-48, 48);
701 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
704 VectorSubtract(v2, org, v2);
706 VectorScale(v2, 2.0f, v2);
707 particle(particletype + pt_static, 0xFFFFFF, 0xFFFFFF, tex_smoke[rand()&7], 12, 32, 64, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0);
711 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
712 for (i = 0;i < 128 * cl_particles_quality.value;i++)
713 particle(particletype + pt_spark, 0x903010, 0xFFD030, tex_particle, 1.0f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 512, 1, 0, org[0], org[1], org[2], lhrandom(-256, 256), lhrandom(-256, 256), lhrandom(-256, 256) + 80, 0.2);
716 if (cl_particles_explosions_shell.integer)
722 CL_ParticleExplosion2
726 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
732 if (!cl_particles.integer) return;
734 for (i = 0;i < 512 * cl_particles_quality.value;i++)
736 VectorRandom (offset);
737 VectorScale (offset, 192, vel);
738 VectorScale (offset, 8, offset);
739 k = particlepalette[colorStart + (i % colorLength)];
740 pscale = lhrandom(0.5, 1.5);
741 particle(particletype + pt_static, k, k, tex_particle, pscale, (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*512, 0, 0, org[0] + offset[0], org[1] + offset[1], org[2] + offset[2], vel[0], vel[1], vel[2], lhrandom(1.5, 3));
751 void CL_BlobExplosion (vec3_t org)
753 CL_ParticleExplosion(org);
762 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
768 CL_ParticleExplosion(org);
771 if (!cl_particles.integer) return;
772 count *= cl_particles_quality.value;
775 k = particlepalette[color + (rand()&7)];
776 if (gamemode == GAME_GOODVSBAD2)
777 particle(particletype + pt_alphastatic, k, k, tex_particle, 5, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 300, 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);
779 particle(particletype + pt_alphastatic, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 512, 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);
783 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
789 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
793 if (!cl_particles.integer) return;
795 if (cl_particles_sparks.integer)
798 count *= cl_particles_quality.value;
801 k = particlepalette[0x68 + (rand() & 7)];
802 particle(particletype + pt_spark, k, k, tex_particle, 0.4f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, gravityscale, 0, org[0], org[1], org[2], lhrandom(-64, 64) + dir[0], lhrandom(-64, 64) + dir[1], lhrandom(0, 128) + dir[2], 0);
807 void CL_Smoke (vec3_t org, vec3_t dir, int count)
812 if (!cl_particles.integer) return;
815 if (cl_particles_smoke.integer)
817 k = count * 0.25 * cl_particles_quality.value;
820 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
821 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
822 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
823 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
824 particle(particletype + pt_grow, 0x101010, 0x202020, tex_smoke[rand()&7], 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 0, 0, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
829 void CL_BulletMark (vec3_t org)
831 if (cl_stainmaps.integer)
832 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
833 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
836 void CL_PlasmaBurn (vec3_t org)
838 if (cl_stainmaps.integer)
839 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
840 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
843 static float bloodcount = 0;
844 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
848 // bloodcount is used to accumulate counts too small to cause a blood particle
849 if (!cl_particles.integer) return;
850 if (!cl_particles_blood.integer) return;
857 while(bloodcount > 0)
859 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
860 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
861 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
862 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
863 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, org3[0], org3[1], org3[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 1);
864 bloodcount -= 16 / cl_particles_quality.value;
868 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
870 vec3_t org, vel, diff, center, velscale;
871 if (!cl_particles.integer) return;
872 if (!cl_particles_bloodshowers.integer) return;
873 if (!cl_particles_blood.integer) return;
875 VectorSubtract(maxs, mins, diff);
876 center[0] = (mins[0] + maxs[0]) * 0.5;
877 center[1] = (mins[1] + maxs[1]) * 0.5;
878 center[2] = (mins[2] + maxs[2]) * 0.5;
879 velscale[0] = velspeed * 2.0 / diff[0];
880 velscale[1] = velspeed * 2.0 / diff[1];
881 velscale[2] = velspeed * 2.0 / diff[2];
883 bloodcount += count * 5.0f;
884 while (bloodcount > 0)
886 org[0] = lhrandom(mins[0], maxs[0]);
887 org[1] = lhrandom(mins[1], maxs[1]);
888 org[2] = lhrandom(mins[2], maxs[2]);
889 vel[0] = (org[0] - center[0]) * velscale[0];
890 vel[1] = (org[1] - center[1]) * velscale[1];
891 vel[2] = (org[2] - center[2]) * velscale[2];
892 bloodcount -= 16 / cl_particles_quality.value;
893 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 1);
897 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
901 if (!cl_particles.integer) return;
902 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
903 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
904 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
906 count *= cl_particles_quality.value;
909 k = particlepalette[colorbase + (rand()&3)];
910 particle(particletype + pt_alphastatic, k, k, tex_particle, 2, 255 / cl_particles_quality.value, (255 / cl_particles_quality.value) / 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);
914 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
917 float t, z, minz, maxz;
918 if (!cl_particles.integer) return;
919 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
920 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
921 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
922 if (dir[2] < 0) // falling
927 minz = z - fabs(dir[2]) * 0.1;
928 maxz = z + fabs(dir[2]) * 0.1;
929 minz = bound(mins[2], minz, maxs[2]);
930 maxz = bound(mins[2], maxz, maxs[2]);
932 count *= cl_particles_quality.value;
937 count *= 4; // ick, this should be in the mod or maps?
941 k = particlepalette[colorbase + (rand()&3)];
942 if (gamemode == GAME_GOODVSBAD2)
943 particle(particletype + pt_rain, k, k, tex_particle, 20, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
945 particle(particletype + pt_rain, k, k, tex_particle, 0.5, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
951 k = particlepalette[colorbase + (rand()&3)];
952 if (gamemode == GAME_GOODVSBAD2)
953 particle(particletype + pt_snow, k, k, tex_particle, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
955 particle(particletype + pt_snow, k, k, tex_particle, 1, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
959 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
963 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
968 if (!cl_particles.integer) return;
970 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
971 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
972 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
974 center[0] = (mins[0] + maxs[0]) * 0.5f;
975 center[1] = (mins[1] + maxs[1]) * 0.5f;
976 center[2] = (mins[2] + maxs[2]) * 0.5f;
978 count *= cl_particles_quality.value;
981 k = particlepalette[224 + (rand()&15)];
982 o[0] = lhrandom(mins[0], maxs[0]);
983 o[1] = lhrandom(mins[1], maxs[1]);
984 o[2] = lhrandom(mins[2], maxs[2]);
985 VectorSubtract(o, center, v);
986 VectorNormalizeFast(v);
987 VectorScale(v, 100, v);
988 v[2] += sv_gravity.value * 0.15f;
989 particle(particletype + pt_static, 0x903010, 0xFFD030, tex_particle, 1.5, lhrandom(64, 128) / cl_particles_quality.value, 128 / cl_particles_quality.value, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0.2);
993 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
997 if (!cl_particles.integer) return;
998 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
999 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1000 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1002 count *= cl_particles_quality.value;
1005 k = particlepalette[224 + (rand()&15)];
1006 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -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), 1);
1008 particle(particletype + pt_static, 0x303030, 0x606060, tex_smoke[rand()&7], 6, lhrandom(48, 96) / cl_particles_quality.value, 64 / cl_particles_quality.value, 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);
1012 void CL_Flames (vec3_t org, vec3_t vel, int count)
1015 if (!cl_particles.integer) return;
1017 count *= cl_particles_quality.value;
1020 k = particlepalette[224 + (rand()&15)];
1021 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 1);
1033 void CL_LavaSplash (vec3_t origin)
1035 float i, j, inc, vel;
1038 if (!cl_particles.integer) return;
1040 inc = 32 / cl_particles_quality.value;
1041 for (i = -128;i < 128;i += inc)
1043 for (j = -128;j < 128;j += inc)
1045 dir[0] = j + lhrandom(0, 8);
1046 dir[1] = i + lhrandom(0, 8);
1048 org[0] = origin[0] + dir[0];
1049 org[1] = origin[1] + dir[1];
1050 org[2] = origin[2] + lhrandom(0, 64);
1051 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1052 if (gamemode == GAME_GOODVSBAD2)
1054 k = particlepalette[0 + (rand()&255)];
1055 l = particlepalette[0 + (rand()&255)];
1056 particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 1, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0);
1060 k = l = particlepalette[224 + (rand()&7)];
1061 particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0);
1073 void CL_TeleportSplash (vec3_t org)
1076 if (!cl_particles.integer) return;
1078 inc = 8 / cl_particles_quality.value;
1079 for (i = -16;i < 16;i += inc)
1080 for (j = -16;j < 16;j += inc)
1081 for (k = -24;k < 32;k += inc)
1082 particle(particletype + pt_static, 0xA0A0A0, 0xFFFFFF, tex_particle, 10, inc * lhrandom(8, 16), inc * 32, 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), 1);
1085 #ifdef WORKINGLQUAKE
1086 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1088 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1091 vec3_t vec, dir, vel, pos;
1092 float len, dec, speed, qd;
1093 int smoke, blood, bubbles;
1094 #ifdef WORKINGLQUAKE
1098 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1101 VectorSubtract(end, start, dir);
1102 VectorNormalize(dir);
1104 VectorSubtract (end, start, vec);
1105 #ifdef WORKINGLQUAKE
1106 len = VectorNormalize (vec);
1108 speed = 1.0f / cl.frametime;
1109 VectorSubtract(end, start, vel);
1111 len = VectorNormalizeLength (vec);
1112 dec = -ent->persistent.trail_time;
1113 ent->persistent.trail_time += len;
1114 if (ent->persistent.trail_time < 0.01f)
1117 // if we skip out, leave it reset
1118 ent->persistent.trail_time = 0.0f;
1120 speed = ent->state_current.time - ent->state_previous.time;
1122 speed = 1.0f / speed;
1123 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1124 color = particlepalette[color];
1126 VectorScale(vel, speed, vel);
1128 // advance into this frame to reach the first puff location
1129 VectorMA(start, dec, vec, pos);
1132 smoke = cl_particles.integer && cl_particles_smoke.integer;
1133 blood = cl_particles.integer && cl_particles_blood.integer;
1134 #ifdef WORKINGLQUAKE
1135 contents = CL_PointQ1Contents(pos);
1136 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1138 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1140 qd = 1.0f / cl_particles_quality.value;
1146 case 0: // rocket trail
1150 particle(particletype + pt_grow, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*125, qd*cl_particles_smoke_alphafade.value*125, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1151 particle(particletype + pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*288, qd*cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], lhrandom(-20, 20), lhrandom(-20, 20), lhrandom(-20, 20), 0);
1154 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, qd*lhrandom(64, 255), qd*256, -0.25, 1.5, pos[0], pos[1], pos[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), (1.0 / 16.0));
1157 case 1: // grenade trail
1158 // FIXME: make it gradually stop smoking
1161 particle(particletype + pt_grow, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*100, qd*cl_particles_smoke_alphafade.value*100, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1166 case 4: // slight blood
1169 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 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), 1);
1172 case 3: // green tracer
1176 if (gamemode == GAME_GOODVSBAD2)
1177 particle(particletype + pt_static, 0x00002E, 0x000030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1179 particle(particletype + pt_static, 0x002000, 0x003000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1183 case 5: // flame tracer
1186 particle(particletype + pt_static, 0x301000, 0x502000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1189 case 6: // voor trail
1193 if (gamemode == GAME_GOODVSBAD2)
1194 particle(particletype + pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, qd*255, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1195 else if (gamemode == GAME_PRYDON)
1196 particle(particletype + pt_static, 0x103040, 0x204050, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1198 particle(particletype + pt_static, 0x502030, 0x502030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1201 #ifndef WORKINGLQUAKE
1202 case 7: // Nehahra smoke tracer
1205 particle(particletype + pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, qd*64, qd*320, 0, 0, pos[0], pos[1], pos[2], lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(0, 16), 0);
1207 case 8: // Nexuiz plasma trail
1210 particle(particletype + pt_static, 0x283880, 0x283880, tex_particle, 4, qd*255, qd*1024, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0);
1212 case 9: // glow trail
1215 particle(particletype + pt_alphastatic, color, color, tex_particle, 5, qd*128, qd*320, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0);
1220 // advance to next time and position
1222 VectorMA (pos, dec, vec, pos);
1224 #ifndef WORKINGLQUAKE
1225 ent->persistent.trail_time = len;
1229 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1231 int tempcolor2, cr, cg, cb;
1235 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1236 particle(particletype + pt_beam, tempcolor2, tempcolor2, tex_beam, radius, alpha * 255, alpha * 255 / lifetime, 0, 0, start[0], start[1], start[2], end[0], end[1], end[2], 0);
1239 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1242 if (!cl_particles.integer) return;
1245 if (cl_particles_smoke.integer)
1246 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1247 particle(particletype + pt_grow, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 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, 0);
1250 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1253 if (!cl_particles.integer) return;
1255 if (cl_stainmaps.integer)
1256 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1257 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1260 if (cl_particles_smoke.integer)
1261 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1262 particle(particletype + pt_grow, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 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), 0);
1265 if (cl_particles_sparks.integer)
1266 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1267 particle(particletype + pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, lhrandom(64, 255) / cl_particles_quality.value, 512 / cl_particles_quality.value, 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);
1275 void CL_MoveParticles (void)
1278 int i, maxparticle, j, a, content;
1279 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3], oldorg[3];
1280 #ifdef WORKINGLQUAKE
1283 entity_render_t *hitent;
1286 // LordHavoc: early out condition
1287 if (!cl_numparticles)
1289 cl_freeparticle = 0;
1293 #ifdef WORKINGLQUAKE
1294 frametime = cl.frametime;
1296 frametime = cl.time - cl.oldtime;
1298 gravity = frametime * sv_gravity.value;
1299 dvel = 1+4*frametime;
1300 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1304 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1311 p->alpha -= p->alphafade * frametime;
1319 if (p->type->orientation != PARTICLE_BEAM)
1321 VectorCopy(p->org, oldorg);
1322 VectorMA(p->org, frametime, p->vel, p->org);
1323 VectorCopy(p->org, org);
1326 if (p->type == particletype + pt_rain)
1328 // raindrop - splash on solid/water/slime/lava
1329 if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK) < 1)
1331 VectorCopy(v, p->org);
1333 p->type = particletype + pt_raindecal;
1334 // convert from a raindrop particle to a rainsplash decal
1335 p->texnum = tex_rainsplash[0];
1337 p->alphafade = p->alpha / 0.4;
1338 VectorCopy(normal, p->vel);
1339 VectorAdd(p->org, normal, p->org);
1346 else if (p->type == particletype + pt_blood)
1348 // blood - splash on solid
1349 if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1351 VectorCopy(v, p->org);
1352 #ifndef WORKINGLQUAKE
1353 if (cl_stainmaps.integer)
1354 R_Stain(v, 32, 32, 16, 16, p->alpha * p->size * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->size * (1.0f / 40.0f));
1356 if (!cl_decals.integer)
1362 p->type = particletype + pt_decal;
1363 // convert from a blood particle to a blood decal
1364 p->texnum = tex_blooddecal[rand()&7];
1365 #ifndef WORKINGLQUAKE
1367 p->ownermodel = hitent->model;
1368 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1369 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1370 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1374 VectorCopy(normal, p->vel);
1375 VectorAdd(p->org, normal, p->org);
1384 if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1386 VectorCopy(v, p->org);
1394 dist = DotProduct(p->vel, normal) * -p->bounce;
1395 VectorMA(p->vel, dist, normal, p->vel);
1396 if (DotProduct(p->vel, p->vel) < 0.03)
1397 VectorClear(p->vel);
1402 p->vel[2] -= p->gravity * gravity;
1406 f = p->friction * frametime;
1407 #ifdef WORKINGLQUAKE
1408 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1410 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1414 VectorScale(p->vel, f, p->vel);
1418 if (p->type != particletype + pt_static)
1420 switch (p->type - particletype)
1422 case pt_entityparticle:
1423 // particle that removes itself after one rendered frame
1430 #ifdef WORKINGLQUAKE
1431 a = CL_PointQ1Contents(p->org);
1432 if (a <= CONTENTS_WATER)
1434 a = CL_PointSuperContents(p->org);
1435 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1438 p->size += frametime * 8;
1439 //p->alpha -= bloodwaterfade;
1442 p->vel[2] -= gravity;
1443 #ifdef WORKINGLQUAKE
1444 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1446 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1451 #ifdef WORKINGLQUAKE
1452 a = CL_PointQ1Contents(p->org);
1453 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1455 a = CL_PointSuperContents(p->org);
1456 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1464 #ifdef WORKINGLQUAKE
1465 a = CL_PointQ1Contents(p->org);
1466 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1468 a = CL_PointSuperContents(p->org);
1469 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1474 if (cl.time > p->time2)
1477 p->time2 = cl.time + (rand() & 3) * 0.1;
1478 p->vel[0] += lhrandom(-32, 32);
1479 p->vel[1] += lhrandom(-32, 32);
1480 p->vel[2] += lhrandom(-32, 32);
1482 #ifdef WORKINGLQUAKE
1483 a = CL_PointQ1Contents(p->org);
1484 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1486 a = CL_PointSuperContents(p->org);
1487 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1492 p->size += frametime * 15;
1495 // FIXME: this has fairly wacky handling of alpha
1496 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1497 #ifndef WORKINGLQUAKE
1498 if (p->owner->model == p->ownermodel)
1500 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1501 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel);
1508 a = max(0, (cl.time - p->time2) * 40);
1510 p->texnum = tex_rainsplash[a];
1519 cl_numparticles = maxparticle + 1;
1520 cl_freeparticle = 0;
1523 #define MAX_PARTICLETEXTURES 64
1524 // particletexture_t is a rectangle in the particlefonttexture
1527 rtexture_t *texture;
1528 float s1, t1, s2, t2;
1533 static int particlefonttexture;
1535 static rtexturepool_t *particletexturepool;
1536 static rtexture_t *particlefonttexture;
1538 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1540 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1542 #define PARTICLETEXTURESIZE 64
1543 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1545 static qbyte shadebubble(float dx, float dy, vec3_t light)
1549 dz = 1 - (dx*dx+dy*dy);
1550 if (dz > 0) // it does hit the sphere
1554 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1555 VectorNormalize(normal);
1556 dot = DotProduct(normal, light);
1557 if (dot > 0.5) // interior reflection
1558 f += ((dot * 2) - 1);
1559 else if (dot < -0.5) // exterior reflection
1560 f += ((dot * -2) - 1);
1562 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1563 VectorNormalize(normal);
1564 dot = DotProduct(normal, light);
1565 if (dot > 0.5) // interior reflection
1566 f += ((dot * 2) - 1);
1567 else if (dot < -0.5) // exterior reflection
1568 f += ((dot * -2) - 1);
1570 f += 16; // just to give it a haze so you can see the outline
1571 f = bound(0, f, 255);
1578 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1580 int basex, basey, y;
1581 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1582 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1583 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1584 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1585 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1586 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1587 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1588 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1591 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1594 float cx, cy, dx, dy, f, iradius;
1596 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1597 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1598 iradius = 1.0f / radius;
1599 alpha *= (1.0f / 255.0f);
1600 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1602 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1606 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1609 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1610 d[0] += f * (red - d[0]);
1611 d[1] += f * (green - d[1]);
1612 d[2] += f * (blue - d[2]);
1618 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1621 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1623 data[0] = bound(minr, data[0], maxr);
1624 data[1] = bound(ming, data[1], maxg);
1625 data[2] = bound(minb, data[2], maxb);
1629 void particletextureinvert(qbyte *data)
1632 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1634 data[0] = 255 - data[0];
1635 data[1] = 255 - data[1];
1636 data[2] = 255 - data[2];
1640 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1641 static void R_InitBloodTextures (qbyte *particletexturedata)
1644 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1647 for (i = 0;i < 8;i++)
1649 memset(&data[0][0][0], 255, sizeof(data));
1650 for (k = 0;k < 24;k++)
1651 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1652 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1653 particletextureinvert(&data[0][0][0]);
1654 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1658 for (i = 0;i < 8;i++)
1660 memset(&data[0][0][0], 255, sizeof(data));
1662 for (j = 1;j < 10;j++)
1663 for (k = min(j, m - 1);k < m;k++)
1664 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1665 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1666 particletextureinvert(&data[0][0][0]);
1667 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1672 static void R_InitParticleTexture (void)
1674 int x, y, d, i, k, m;
1675 float dx, dy, radius, f, f2;
1676 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1678 qbyte *particletexturedata;
1680 // a note: decals need to modulate (multiply) the background color to
1681 // properly darken it (stain), and they need to be able to alpha fade,
1682 // this is a very difficult challenge because it means fading to white
1683 // (no change to background) rather than black (darkening everything
1684 // behind the whole decal polygon), and to accomplish this the texture is
1685 // inverted (dark red blood on white background becomes brilliant cyan
1686 // and white on black background) so we can alpha fade it to black, then
1687 // we invert it again during the blendfunc to make it work...
1689 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1690 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1693 for (i = 0;i < 8;i++)
1695 memset(&data[0][0][0], 255, sizeof(data));
1698 qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1700 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1701 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
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 d = (noise2[y][x] - 128) * 3 + 192;
1711 d = d * (1-(dx*dx+dy*dy));
1712 d = (d * noise1[y][x]) >> 7;
1713 d = bound(0, d, 255);
1714 data[y][x][3] = (qbyte) d;
1721 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1725 for (i = 0;i < 16;i++)
1727 memset(&data[0][0][0], 255, sizeof(data));
1728 radius = i * 3.0f / 4.0f / 16.0f;
1729 f2 = 255.0f * ((15.0f - i) / 15.0f);
1730 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1732 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1733 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1735 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1736 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1737 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1740 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1744 memset(&data[0][0][0], 255, sizeof(data));
1745 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1747 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1748 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1750 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1751 d = 256 * (1 - (dx*dx+dy*dy));
1752 d = bound(0, d, 255);
1753 data[y][x][3] = (qbyte) d;
1756 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1759 memset(&data[0][0][0], 255, sizeof(data));
1760 light[0] = 1;light[1] = 1;light[2] = 1;
1761 VectorNormalize(light);
1762 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1764 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1765 // stretch upper half of bubble by +50% and shrink lower half by -50%
1766 // (this gives an elongated teardrop shape)
1768 dy = (dy - 0.5f) * 2.0f;
1770 dy = (dy - 0.5f) / 1.5f;
1771 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1773 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1774 // shrink bubble width to half
1776 data[y][x][3] = shadebubble(dx, dy, light);
1779 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1782 memset(&data[0][0][0], 255, sizeof(data));
1783 light[0] = 1;light[1] = 1;light[2] = 1;
1784 VectorNormalize(light);
1785 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1787 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1788 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1790 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1791 data[y][x][3] = shadebubble(dx, dy, light);
1794 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1796 // Blood particles and blood decals
1797 R_InitBloodTextures (particletexturedata);
1800 for (i = 0;i < 8;i++)
1802 memset(&data[0][0][0], 255, sizeof(data));
1803 for (k = 0;k < 12;k++)
1804 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1805 for (k = 0;k < 3;k++)
1806 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1807 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1808 particletextureinvert(&data[0][0][0]);
1809 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1813 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1814 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1815 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1819 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1822 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1823 if (!particlefonttexture)
1824 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1825 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1826 particletexture[i].texture = particlefonttexture;
1829 fractalnoise(&noise3[0][0], 64, 4);
1831 for (y = 0;y < 64;y++)
1833 dy = (y - 0.5f*64) / (64*0.5f-1);
1834 for (x = 0;x < 16;x++)
1836 dx = (x - 0.5f*16) / (16*0.5f-2);
1837 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1838 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1839 data2[y][x][3] = 255;
1844 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1847 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1848 if (!particletexture[tex_beam].texture)
1849 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1850 particletexture[tex_beam].s1 = 0;
1851 particletexture[tex_beam].t1 = 0;
1852 particletexture[tex_beam].s2 = 1;
1853 particletexture[tex_beam].t2 = 1;
1855 Mem_Free(particletexturedata);
1858 static void r_part_start(void)
1860 particletexturepool = R_AllocTexturePool();
1861 R_InitParticleTexture ();
1864 static void r_part_shutdown(void)
1866 R_FreeTexturePool(&particletexturepool);
1869 static void r_part_newmap(void)
1871 cl_numparticles = 0;
1872 cl_freeparticle = 0;
1875 void R_Particles_Init (void)
1877 Cvar_RegisterVariable(&r_drawparticles);
1878 #ifdef WORKINGLQUAKE
1881 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1885 #ifdef WORKINGLQUAKE
1886 void R_InitParticles(void)
1888 CL_Particles_Init();
1893 float particle_vertex3f[12], particle_texcoord2f[8];
1895 #ifdef WORKINGLQUAKE
1896 void R_DrawParticle(particle_t *p)
1899 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1901 const particle_t *p = calldata1;
1905 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, size;
1906 particletexture_t *tex;
1908 VectorCopy(p->org, org);
1910 blendmode = p->type->blendmode;
1911 tex = &particletexture[p->texnum];
1912 cr = p->color[0] * (1.0f / 255.0f);
1913 cg = p->color[1] * (1.0f / 255.0f);
1914 cb = p->color[2] * (1.0f / 255.0f);
1915 ca = p->alpha * (1.0f / 255.0f);
1916 if (blendmode == PBLEND_MOD)
1926 #ifndef WORKINGLQUAKE
1929 VectorSubtract(org, r_vieworigin, fogvec);
1930 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1935 if (blendmode == PBLEND_ADD)
1937 cr += fogcolor[0] * fog;
1938 cg += fogcolor[1] * fog;
1939 cb += fogcolor[2] * fog;
1943 R_Mesh_Matrix(&r_identitymatrix);
1945 memset(&m, 0, sizeof(m));
1946 m.tex[0] = R_GetTexture(tex->texture);
1947 m.pointer_texcoord[0] = particle_texcoord2f;
1948 m.pointer_vertex = particle_vertex3f;
1951 GL_Color(cr, cg, cb, ca);
1953 if (blendmode == PBLEND_ALPHA)
1954 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1955 else if (blendmode == PBLEND_ADD)
1956 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1957 else //if (blendmode == PBLEND_MOD)
1958 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1959 GL_DepthMask(false);
1962 size = p->size * cl_particles_size.value;
1963 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1965 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1968 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1970 VectorNegate(p->vel, v);
1971 VectorVectors(v, right, up);
1974 VectorVectors(p->vel, right, up);
1975 VectorScale(right, size, right);
1976 VectorScale(up, size, up);
1980 VectorScale(r_viewleft, -size, right);
1981 VectorScale(r_viewup, size, up);
1983 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1984 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1985 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1986 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1987 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1988 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1989 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1990 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1991 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1992 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1993 particle_vertex3f[10] = org[1] + right[1] - up[1];
1994 particle_vertex3f[11] = org[2] + right[2] - up[2];
1995 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1996 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1997 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1998 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2000 else if (p->type->orientation == PARTICLE_SPARK)
2002 VectorMA(p->org, -0.02, p->vel, v);
2003 VectorMA(p->org, 0.02, p->vel, up2);
2004 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2005 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2006 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2007 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2008 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2010 else if (p->type->orientation == PARTICLE_BEAM)
2012 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2013 VectorSubtract(p->vel, p->org, up);
2014 VectorNormalizeFast(up);
2015 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2016 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2017 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2018 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2019 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2020 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2023 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2026 if (blendmode == PBLEND_ALPHA)
2027 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2028 else if (blendmode == PBLEND_ADD)
2029 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2030 else //if (blendmode == PBLEND_MOD)
2031 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2032 glColor4f(cr, cg, cb, ca);
2034 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2035 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2036 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2037 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2040 R_Mesh_Draw(0, 4, 2, polygonelements);
2044 void R_DrawParticles (void)
2047 float minparticledist;
2050 #ifdef WORKINGLQUAKE
2054 // LordHavoc: early out conditions
2055 if ((!cl_numparticles) || (!r_drawparticles.integer))
2058 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2060 #ifdef WORKINGLQUAKE
2061 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2063 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2065 // LordHavoc: only render if not too close
2066 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2067 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2070 glDisable(GL_BLEND);
2071 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2073 // LordHavoc: only render if not too close
2074 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2079 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2081 if (p->type == particletype + pt_decal)
2082 R_DrawParticleCallback(p, 0);
2084 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);