2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
25 #define NUMVERTEXNORMALS 162
26 siextern float r_avertexnormals[NUMVERTEXNORMALS][3];
27 #define m_bytenormals r_avertexnormals
28 #define VectorNormalizeFast VectorNormalize
29 #define CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
30 typedef unsigned char qbyte;
31 #define cl_stainmaps.integer 0
32 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
35 #define CL_EntityParticles R_EntityParticles
36 #define CL_ReadPointFile_f R_ReadPointFile_f
37 #define CL_ParseParticleEffect R_ParseParticleEffect
38 #define CL_ParticleExplosion R_ParticleExplosion
39 #define CL_ParticleExplosion2 R_ParticleExplosion2
40 #define CL_BlobExplosion R_BlobExplosion
41 #define CL_RunParticleEffect R_RunParticleEffect
42 #define CL_LavaSplash R_LavaSplash
43 #define CL_RocketTrail2 R_RocketTrail2
44 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
46 vec3_t right1, right2, diff, normal;
48 VectorSubtract (org2, org1, normal);
49 VectorNormalizeFast (normal);
51 // calculate 'right' vector for start
52 VectorSubtract (r_vieworigin, org1, diff);
53 VectorNormalizeFast (diff);
54 CrossProduct (normal, diff, right1);
56 // calculate 'right' vector for end
57 VectorSubtract (r_vieworigin, org2, diff);
58 VectorNormalizeFast (diff);
59 CrossProduct (normal, diff, right2);
61 vert[ 0] = org1[0] + width * right1[0];
62 vert[ 1] = org1[1] + width * right1[1];
63 vert[ 2] = org1[2] + width * right1[2];
64 vert[ 3] = org1[0] - width * right1[0];
65 vert[ 4] = org1[1] - width * right1[1];
66 vert[ 5] = org1[2] - width * right1[2];
67 vert[ 6] = org2[0] - width * right2[0];
68 vert[ 7] = org2[1] - width * right2[1];
69 vert[ 8] = org2[2] - width * right2[2];
70 vert[ 9] = org2[0] + width * right2[0];
71 vert[10] = org2[1] + width * right2[1];
72 vert[11] = org2[2] + width * right2[2];
74 void fractalnoise(qbyte *noise, int size, int startgrid)
76 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
78 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
80 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
81 if (size != (1 << sizepower))
82 Sys_Error("fractalnoise: size must be power of 2\n");
84 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
85 if (startgrid != (1 << gridpower))
86 Sys_Error("fractalnoise: grid must be power of 2\n");
88 startgrid = bound(0, startgrid, size);
90 amplitude = 0xFFFF; // this gets halved before use
91 noisebuf = malloc(size*size*sizeof(int));
92 memset(noisebuf, 0, size*size*sizeof(int));
94 for (g2 = startgrid;g2;g2 >>= 1)
96 // brownian motion (at every smaller level there is random behavior)
98 for (y = 0;y < size;y += g2)
99 for (x = 0;x < size;x += g2)
100 n(x,y) += (rand()&litude);
105 // subdivide, diamond-square algorithm (really this has little to do with squares)
107 for (y = 0;y < size;y += g2)
108 for (x = 0;x < size;x += g2)
109 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
111 for (y = 0;y < size;y += g2)
112 for (x = 0;x < size;x += g2)
114 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
115 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
119 // find range of noise values
121 for (y = 0;y < size;y++)
122 for (x = 0;x < size;x++)
124 if (n(x,y) < min) min = n(x,y);
125 if (n(x,y) > max) max = n(x,y);
129 // normalize noise and copy to output
130 for (y = 0;y < size;y++)
131 for (x = 0;x < size;x++)
132 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
136 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
140 right[0] = forward[2];
141 right[1] = -forward[0];
142 right[2] = forward[1];
144 d = DotProduct(forward, right);
145 right[0] -= d * forward[0];
146 right[1] -= d * forward[1];
147 right[2] -= d * forward[2];
148 VectorNormalizeFast(right);
149 CrossProduct(right, forward, up);
153 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
155 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int hitbmodels, void **hitent, int hitsupercontentsmask)
162 memset (&trace, 0, sizeof(trace));
164 VectorCopy (end, trace.endpos);
166 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
168 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
170 VectorCopy(trace.endpos, impact);
171 VectorCopy(trace.plane.normal, normal);
172 return trace.fraction;
175 #include "cl_collision.h"
178 #define MAX_PARTICLES 32768 // default max # of particles at one time
179 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
183 pt_dead, pt_static, pt_rain, pt_bubble, pt_blood, pt_grow, pt_decal, pt_decalfade
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_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
297 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
298 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
299 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
300 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
301 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
302 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
303 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
305 #ifndef WORKINGLQUAKE
306 static mempool_t *cl_part_mempool;
309 void CL_Particles_Clear(void)
320 void CL_ReadPointFile_f (void);
321 void CL_Particles_Init (void)
325 i = COM_CheckParm ("-particles");
327 if (i && i < com_argc - 1)
329 cl_maxparticles = (int)(atoi(com_argv[i+1]));
330 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
331 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
334 cl_maxparticles = MAX_PARTICLES;
336 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
338 Cvar_RegisterVariable (&cl_particles);
339 Cvar_RegisterVariable (&cl_particles_quality);
340 Cvar_RegisterVariable (&cl_particles_size);
341 Cvar_RegisterVariable (&cl_particles_bloodshowers);
342 Cvar_RegisterVariable (&cl_particles_blood);
343 Cvar_RegisterVariable (&cl_particles_blood_alpha);
344 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
345 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
346 Cvar_RegisterVariable (&cl_particles_smoke);
347 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
348 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
349 Cvar_RegisterVariable (&cl_particles_sparks);
350 Cvar_RegisterVariable (&cl_particles_bubbles);
351 Cvar_RegisterVariable (&cl_decals);
352 Cvar_RegisterVariable (&cl_decals_time);
353 Cvar_RegisterVariable (&cl_decals_fadetime);
356 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
358 cl_part_mempool = Mem_AllocPool("CL_Part");
359 particles = (particle_t *) Mem_Alloc(cl_part_mempool, cl_maxparticles * sizeof(particle_t));
365 // list of all 26 parameters:
366 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
367 // porientation - PARTICLE_ enum values (PARTICLE_BILLBOARD, PARTICLE_SPARK, etc)
368 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
369 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
370 // plight - no longer used (this used to turn on particle lighting)
371 // pblendmode - PBLEND_ enum values (PBLEND_ALPHA, PBLEND_ADD, etc)
372 // pscalex,pscaley - width and height of particle (according to orientation), these are normally the same except when making sparks and beams
373 // palpha - opacity of particle as 0-255 (can be more than 255)
374 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
375 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
376 // pgravity - how much effect gravity has on the particle (0-1)
377 // 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
378 // px,py,pz - starting origin of particle
379 // pvx,pvy,pvz - starting velocity of particle
380 // ptime2 - extra time parameter for certain particle types (pt_decal delayed fades and pt_rain snowflutter use this)
381 // pvx2,pvy2,pvz2 - for PARTICLE_ORIENTED_DOUBLESIDED this is the surface normal of the orientation (forward vector), pt_rain uses this for snow fluttering
382 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
383 // 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
384 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)
387 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
388 ptempcolor = (pcolor1);
389 ptempcolor2 = (pcolor2);
390 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
391 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
392 pcb2 = (ptempcolor2) & 0xFF;
393 if (ptempcolor != ptempcolor2)
395 pcr1 = ((ptempcolor) >> 16) & 0xFF;
396 pcg1 = ((ptempcolor) >> 8) & 0xFF;
397 pcb1 = (ptempcolor) & 0xFF;
398 ptempcolor = rand() & 0xFF;
399 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
400 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
401 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
403 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
404 if (cl_freeparticle >= cl_maxparticles)
406 part = &particles[cl_freeparticle++];
407 if (cl_numparticles < cl_freeparticle)
408 cl_numparticles = cl_freeparticle;
409 memset(part, 0, sizeof(*part));
410 part->type = (ptype);
411 part->color[0] = pcr2;
412 part->color[1] = pcg2;
413 part->color[2] = pcb2;
414 part->color[3] = 0xFF;
415 part->orientation = porientation;
417 part->blendmode = pblendmode;
418 part->scalex = (pscalex);
419 part->scaley = (pscaley);
420 part->alpha = (palpha);
421 part->alphafade = (palphafade);
422 part->die = cl.time + (ptime);
423 part->gravity = (pgravity);
424 part->bounce = (pbounce);
428 part->vel[0] = (pvx);
429 part->vel[1] = (pvy);
430 part->vel[2] = (pvz);
431 part->time2 = (ptime2);
432 part->vel2[0] = (pvx2);
433 part->vel2[1] = (pvy2);
434 part->vel2[2] = (pvz2);
435 part->friction = (pfriction);
436 part->pressure = (ppressure);
440 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
443 if (!cl_decals.integer)
445 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);
446 #ifndef WORKINGLQUAKE
450 p->ownermodel = p->owner->model;
451 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
452 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
453 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
458 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
461 float bestfrac, bestorg[3], bestnormal[3];
462 float frac, v[3], normal[3], org2[3];
464 void *besthitent = NULL, *hitent;
466 entity_render_t *besthitent = NULL, *hitent;
469 for (i = 0;i < 32;i++)
472 VectorMA(org, maxdist, org2, org2);
473 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
478 VectorCopy(v, bestorg);
479 VectorCopy(normal, bestnormal);
483 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
491 void CL_EntityParticles (entity_t *ent)
495 float sp, sy, cp, cy;
499 static vec3_t avelocities[NUMVERTEXNORMALS];
500 if (!cl_particles.integer) return;
505 if (!avelocities[0][0])
506 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
507 avelocities[0][i] = (rand()&255) * 0.01;
509 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
511 angle = cl.time * avelocities[i][0];
514 angle = cl.time * avelocities[i][1];
523 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);
525 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);
531 void CL_ReadPointFile_f (void)
535 char *pointfile = NULL, *pointfilepos, *t, tchar;
536 char name[MAX_OSPATH];
541 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
542 strlcat (name, ".pts", sizeof (name));
544 pointfile = COM_LoadTempFile (name);
546 pointfile = FS_LoadFile(name, tempmempool, true);
550 Con_Printf("Could not open %s\n", name);
554 Con_Printf("Reading %s...\n", name);
557 pointfilepos = pointfile;
558 while (*pointfilepos)
560 while (*pointfilepos == '\n' || *pointfilepos == '\r')
565 while (*t && *t != '\n' && *t != '\r')
569 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
575 VectorCopy(org, leakorg);
578 if (cl_numparticles < cl_maxparticles - 3)
581 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);
584 #ifndef WORKINGLQUAKE
587 VectorCopy(leakorg, org);
588 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
590 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);
591 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);
592 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);
597 CL_ParseParticleEffect
599 Parse an effect out of the server message
602 void CL_ParseParticleEffect (void)
605 int i, count, msgcount, color;
608 for (i=0 ; i<3 ; i++)
609 dir[i] = MSG_ReadChar () * (1.0/16);
610 msgcount = MSG_ReadByte ();
611 color = MSG_ReadByte ();
618 if (cl_particles_blood_bloodhack.integer)
623 CL_BloodPuff(org, dir, count / 2);
629 CL_BloodPuff(org, dir, count / 2);
633 CL_RunParticleEffect (org, dir, color, count);
642 void CL_ParticleExplosion (vec3_t org)
647 if (cl_stainmaps.integer)
648 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
649 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
651 i = CL_PointQ1Contents(org);
652 if ((i == CONTENTS_SLIME || i == CONTENTS_WATER) && cl_particles.integer && cl_particles_bubbles.integer)
654 for (i = 0;i < 128 * cl_particles_quality.value;i++)
655 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);
660 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
662 if (cl_particles.integer && cl_particles_smoke.integer)
664 for (i = 0;i < 64;i++)
667 v2[0] = lhrandom(-64, 64);
668 v2[1] = lhrandom(-64, 64);
669 v2[2] = lhrandom(-8, 24);
671 for (k = 0;k < 16;k++)
673 v[0] = org[0] + lhrandom(-64, 64);
674 v[1] = org[1] + lhrandom(-64, 64);
675 v[2] = org[2] + lhrandom(-8, 24);
676 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
679 VectorSubtract(v2, org, v2);
681 VectorScale(v2, 2.0f, v2);
682 particle(pt_static, PARTICLE_BILLBOARD, 0x101010, 0x202020, tex_smoke[rand()&7], true, PBLEND_ADD, 12, 12, 255, 512, 9999, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0, 0, 0, 0, 0, 0);
687 if (cl_particles.integer && cl_particles_sparks.integer)
690 for (i = 0;i < 256 * cl_particles_quality.value;i++)
692 k = particlepalette[0x68 + (rand() & 7)];
693 particle(pt_static, PARTICLE_SPARK, k, k, 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);
698 if (cl_explosions.integer)
704 CL_ParticleExplosion2
708 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
713 if (!cl_particles.integer) return;
715 for (i = 0;i < 512 * cl_particles_quality.value;i++)
717 VectorRandom (offset);
718 VectorScale (offset, 192, vel);
719 VectorScale (offset, 8, offset);
720 k = particlepalette[colorStart + (i % colorLength)];
721 particle(pt_static, PARTICLE_BILLBOARD, k, k, tex_particle, false, PBLEND_ALPHA, 1.5, 1.5, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 384, 0.3, 0, 0, org[0] + offset[0], org[1] + offset[1], org[2] + offset[2], vel[0], vel[1], vel[2], 0, 0, 0, 0, 1, 0);
731 void CL_BlobExplosion (vec3_t org)
733 if (cl_stainmaps.integer)
734 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
735 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
737 if (cl_explosions.integer)
747 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
753 CL_ParticleExplosion(org);
756 if (!cl_particles.integer) return;
757 count *= cl_particles_quality.value;
760 k = particlepalette[color + (rand()&7)];
761 if (gamemode == GAME_GOODVSBAD2)
762 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);
764 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);
768 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
774 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
779 if (cl_stainmaps.integer)
780 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
781 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
783 if (!cl_particles.integer) return;
785 if (cl_particles_bulletimpacts.integer)
788 if (cl_particles_smoke.integer)
790 k = count * 0.25 * cl_particles_quality.value;
793 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
794 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
795 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
796 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
797 particle(pt_grow, PARTICLE_BILLBOARD, 0x101010, 0x202020, tex_smoke[rand()&7], true, PBLEND_ADD, 3, 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 9999, -0.2, 0, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 16), 15, 0, 0, 0, 0.2, 0);
801 if (cl_particles_sparks.integer)
804 count *= cl_particles_quality.value;
807 k = particlepalette[0x68 + (rand() & 7)];
808 particle(pt_static, PARTICLE_SPARK, k, k, tex_particle, false, PBLEND_ADD, 0.4f, 0.015f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, 9999, 1, 0, org[0], org[1], org[2], lhrandom(-64, 64) + dir[0], lhrandom(-64, 64) + dir[1], lhrandom(0, 128) + dir[2], 0, 0, 0, 0, 0.2, 0);
814 void CL_PlasmaBurn (vec3_t org)
816 if (cl_stainmaps.integer)
817 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
818 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
821 static float bloodcount = 0;
822 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
826 // bloodcount is used to accumulate counts too small to cause a blood particle
827 if (!cl_particles.integer) return;
828 if (!cl_particles_blood.integer) return;
835 while(bloodcount > 0)
837 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
838 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
839 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
840 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
841 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);
842 bloodcount -= 16 / cl_particles_quality.value;
846 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
848 vec3_t org, vel, diff, center, velscale;
849 if (!cl_particles.integer) return;
850 if (!cl_particles_bloodshowers.integer) return;
851 if (!cl_particles_blood.integer) return;
853 VectorSubtract(maxs, mins, diff);
854 center[0] = (mins[0] + maxs[0]) * 0.5;
855 center[1] = (mins[1] + maxs[1]) * 0.5;
856 center[2] = (mins[2] + maxs[2]) * 0.5;
857 velscale[0] = velspeed * 2.0 / diff[0];
858 velscale[1] = velspeed * 2.0 / diff[1];
859 velscale[2] = velspeed * 2.0 / diff[2];
861 bloodcount += count * 5.0f;
862 while (bloodcount > 0)
864 org[0] = lhrandom(mins[0], maxs[0]);
865 org[1] = lhrandom(mins[1], maxs[1]);
866 org[2] = lhrandom(mins[2], maxs[2]);
867 vel[0] = (org[0] - center[0]) * velscale[0];
868 vel[1] = (org[1] - center[1]) * velscale[1];
869 vel[2] = (org[2] - center[2]) * velscale[2];
870 bloodcount -= 16 / cl_particles_quality.value;
871 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);
875 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
879 if (!cl_particles.integer) return;
880 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
881 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
882 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
884 count *= cl_particles_quality.value;
887 k = particlepalette[colorbase + (rand()&3)];
888 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);
892 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
895 float t, z, minz, maxz;
896 if (!cl_particles.integer) return;
897 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
898 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
899 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
900 if (dir[2] < 0) // falling
902 t = (maxs[2] - mins[2]) / -dir[2];
907 t = (maxs[2] - mins[2]) / dir[2];
910 if (t < 0 || t > 2) // sanity check
913 minz = z - fabs(dir[2]) * 0.1;
914 maxz = z + fabs(dir[2]) * 0.1;
915 minz = bound(mins[2], minz, maxs[2]);
916 maxz = bound(mins[2], maxz, maxs[2]);
918 count *= cl_particles_quality.value;
923 count *= 4; // ick, this should be in the mod or maps?
927 k = particlepalette[colorbase + (rand()&3)];
928 if (gamemode == GAME_GOODVSBAD2)
930 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);
934 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);
941 k = particlepalette[colorbase + (rand()&3)];
942 if (gamemode == GAME_GOODVSBAD2)
944 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);
948 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);
953 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
957 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
962 if (!cl_particles.integer) return;
964 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
965 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
966 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
968 center[0] = (mins[0] + maxs[0]) * 0.5f;
969 center[1] = (mins[1] + maxs[1]) * 0.5f;
970 center[2] = (mins[2] + maxs[2]) * 0.5f;
972 count *= cl_particles_quality.value;
975 k = particlepalette[224 + (rand()&15)];
976 o[0] = lhrandom(mins[0], maxs[0]);
977 o[1] = lhrandom(mins[1], maxs[1]);
978 o[2] = lhrandom(mins[2], maxs[2]);
979 VectorSubtract(o, center, v);
980 VectorNormalizeFast(v);
981 VectorScale(v, 100, v);
982 v[2] += sv_gravity.value * 0.15f;
983 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);
987 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
991 if (!cl_particles.integer) return;
992 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
993 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
994 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
996 count *= cl_particles_quality.value;
999 k = particlepalette[224 + (rand()&15)];
1000 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);
1002 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);
1006 void CL_Flames (vec3_t org, vec3_t vel, int count)
1009 if (!cl_particles.integer) return;
1011 count *= cl_particles_quality.value;
1014 k = particlepalette[224 + (rand()&15)];
1015 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);
1027 void CL_LavaSplash (vec3_t origin)
1029 float i, j, inc, vel;
1032 if (!cl_particles.integer) return;
1034 inc = 32 / cl_particles_quality.value;
1035 for (i = -128;i < 128;i += inc)
1037 for (j = -128;j < 128;j += inc)
1039 dir[0] = j + lhrandom(0, 8);
1040 dir[1] = i + lhrandom(0, 8);
1042 org[0] = origin[0] + dir[0];
1043 org[1] = origin[1] + dir[1];
1044 org[2] = origin[2] + lhrandom(0, 64);
1045 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1046 if (gamemode == GAME_GOODVSBAD2)
1048 k = particlepalette[0 + (rand()&255)];
1049 l = particlepalette[0 + (rand()&255)];
1050 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);
1054 k = l = particlepalette[224 + (rand()&7)];
1055 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);
1068 void R_TeleportSplash (vec3_t org)
1071 if (!cl_particles.integer) return;
1073 inc = 8 / cl_particles_quality.value;
1074 for (i = -16;i < 16;i += inc)
1075 for (j = -16;j < 16;j += inc)
1076 for (k = -24;k < 32;k += inc)
1077 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);
1081 #ifdef WORKINGLQUAKE
1082 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1084 void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
1087 vec3_t vec, dir, vel, pos;
1088 float len, dec, speed, qd;
1089 int contents, smoke, blood, bubbles;
1091 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1094 VectorSubtract(end, start, dir);
1095 VectorNormalize(dir);
1097 VectorSubtract (end, start, vec);
1098 #ifdef WORKINGLQUAKE
1099 len = VectorNormalize (vec);
1101 speed = 1.0f / cl.frametime;
1102 VectorSubtract(end, start, vel);
1104 len = VectorNormalizeLength (vec);
1105 dec = -ent->persistent.trail_time;
1106 ent->persistent.trail_time += len;
1107 if (ent->persistent.trail_time < 0.01f)
1110 // if we skip out, leave it reset
1111 ent->persistent.trail_time = 0.0f;
1113 speed = ent->state_current.time - ent->state_previous.time;
1115 speed = 1.0f / speed;
1116 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1118 VectorScale(vel, speed, vel);
1120 // advance into this frame to reach the first puff location
1121 VectorMA(start, dec, vec, pos);
1124 contents = CL_PointQ1Contents(pos);
1125 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1128 smoke = cl_particles.integer && cl_particles_smoke.integer;
1129 blood = cl_particles.integer && cl_particles_blood.integer;
1130 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1131 qd = 1.0f / cl_particles_quality.value;
1137 case 0: // rocket trail
1141 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);
1142 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);
1145 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);
1148 case 1: // grenade trail
1149 // FIXME: make it gradually stop smoking
1152 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);
1157 case 4: // slight blood
1160 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);
1163 case 3: // green tracer
1167 if (gamemode == GAME_GOODVSBAD2)
1168 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);
1170 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);
1174 case 5: // flame tracer
1177 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);
1180 case 6: // voor trail
1184 if (gamemode == GAME_GOODVSBAD2)
1185 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);
1187 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);
1191 case 7: // Nehahra smoke tracer
1194 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);
1196 case 8: // Nexuiz plasma trail
1199 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);
1203 // advance to next time and position
1205 VectorMA (pos, dec, vec, pos);
1207 #ifndef WORKINGLQUAKE
1208 ent->persistent.trail_time = len;
1212 void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1216 if (!cl_particles.integer) return;
1217 if (!cl_particles_smoke.integer) return;
1219 VectorCopy(start, pos);
1220 VectorSubtract(end, start, vec);
1221 #ifdef WORKINGLQUAKE
1222 len = VectorNormalize(vec);
1224 len = VectorNormalizeLength(vec);
1226 color = particlepalette[color];
1227 dec = 3.0f / cl_particles_quality.value;
1230 particle(pt_static, PARTICLE_BILLBOARD, color, color, tex_particle, false, PBLEND_ALPHA, 5, 5, 128 / cl_particles_quality.value, 320 / cl_particles_quality.value, 9999, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, 0, 0);
1232 VectorMA(pos, dec, vec, pos);
1236 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1238 int tempcolor2, cr, cg, cb;
1242 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1243 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);
1246 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1249 if (!cl_particles.integer) return;
1252 if (cl_particles_smoke.integer)
1253 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1254 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);
1257 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1260 if (!cl_particles.integer) return;
1262 if (cl_stainmaps.integer)
1263 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1264 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1267 if (cl_particles_smoke.integer)
1268 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1269 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);
1272 if (cl_particles_sparks.integer)
1273 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1274 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);
1282 void CL_MoveParticles (void)
1285 int i, maxparticle, j, a, content;
1286 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1287 #ifdef WORKINGLQUAKE
1290 entity_render_t *hitent;
1293 cl_freeparticle = 0;
1295 // LordHavoc: early out condition
1296 if (!cl_numparticles)
1299 #ifdef WORKINGLQUAKE
1300 frametime = cl.frametime;
1302 frametime = cl.time - cl.oldtime;
1304 gravity = frametime * sv_gravity.value;
1305 dvel = 1+4*frametime;
1306 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1310 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1316 VectorCopy(p->org, p->oldorg);
1317 VectorMA(p->org, frametime, p->vel, p->org);
1318 VectorCopy(p->org, org);
1321 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1323 VectorCopy(v, p->org);
1326 // assume it's blood (lame, but...)
1327 #ifndef WORKINGLQUAKE
1328 if (cl_stainmaps.integer)
1329 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));
1331 if (!cl_decals.integer)
1338 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1339 // convert from a blood particle to a blood decal
1340 p->texnum = tex_blooddecal[rand()&7];
1341 #ifndef WORKINGLQUAKE
1343 p->ownermodel = hitent->model;
1344 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1345 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1346 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1348 p->time2 = cl.time + cl_decals_time.value;
1349 p->die = p->time2 + cl_decals_fadetime.value;
1351 VectorCopy(normal, p->vel2);
1352 VectorClear(p->vel);
1353 VectorAdd(p->org, normal, p->org);
1362 dist = DotProduct(p->vel, normal) * -p->bounce;
1363 VectorMA(p->vel, dist, normal, p->vel);
1364 if (DotProduct(p->vel, p->vel) < 0.03)
1365 VectorClear(p->vel);
1370 p->vel[2] -= p->gravity * gravity;
1372 p->alpha -= p->alphafade * frametime;
1374 if (p->alpha <= 0 || cl.time > p->die)
1382 f = p->friction * frametime;
1384 content = CL_PointQ1Contents(p->org);
1385 if (content != CONTENTS_EMPTY)
1388 VectorScale(p->vel, f, p->vel);
1391 if (p->type != pt_static)
1397 content = CL_PointQ1Contents(p->org);
1399 if (a != CONTENTS_EMPTY)
1401 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1403 p->scalex += frametime * 8;
1404 p->scaley += frametime * 8;
1405 //p->alpha -= bloodwaterfade;
1411 p->vel[2] -= gravity;
1415 content = CL_PointQ1Contents(p->org);
1416 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1423 if (cl.time > p->time2)
1426 p->time2 = cl.time + (rand() & 3) * 0.1;
1427 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1428 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1429 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1432 content = CL_PointQ1Contents(p->org);
1434 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1438 p->scalex += frametime * p->time2;
1439 p->scaley += frametime * p->time2;
1442 #ifndef WORKINGLQUAKE
1443 if (p->owner->model == p->ownermodel)
1445 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1446 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1447 if (cl.time > p->time2)
1449 p->alphafade = p->alpha / (p->die - cl.time);
1450 p->type = pt_decalfade;
1458 #ifndef WORKINGLQUAKE
1459 if (p->owner->model == p->ownermodel)
1461 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1462 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1469 Con_Printf("unknown particle type %i\n", p->type);
1475 cl_numparticles = maxparticle + 1;
1478 #define MAX_PARTICLETEXTURES 64
1479 // particletexture_t is a rectangle in the particlefonttexture
1482 rtexture_t *texture;
1483 float s1, t1, s2, t2;
1488 static int particlefonttexture;
1490 static rtexturepool_t *particletexturepool;
1491 static rtexture_t *particlefonttexture;
1493 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1495 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
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) * 32;
1534 basey = ((texnum >> 3) & 7) * 32;
1535 particletexture[texnum].s1 = (basex + 1) / 256.0f;
1536 particletexture[texnum].t1 = (basey + 1) / 256.0f;
1537 particletexture[texnum].s2 = (basex + 31) / 256.0f;
1538 particletexture[texnum].t2 = (basey + 31) / 256.0f;
1539 for (y = 0;y < 32;y++)
1540 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 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, 30 - radius);
1549 cy = lhrandom(radius + 1, 30 - radius);
1550 iradius = 1.0f / radius;
1551 alpha *= (1.0f / 255.0f);
1552 for (y = 0;y < 32;y++)
1554 for (x = 0;x < 32;x++)
1558 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1561 d = data + (y * 32 + 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 < 32*32;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 < 32*32;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[32][32][4], noise1[64][64], noise2[64][64], data2[64][16][4];
1598 qbyte particletexturedata[256*256*4];
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 memset(particletexturedata, 255, sizeof(particletexturedata));
1612 for (i = 0;i < 8;i++)
1614 memset(&data[0][0][0], 255, sizeof(data));
1617 fractalnoise(&noise1[0][0], 64, 4);
1618 fractalnoise(&noise2[0][0], 64, 8);
1620 for (y = 0;y < 32;y++)
1623 for (x = 0;x < 32;x++)
1626 d = (noise2[y][x] - 128) * 3 + 192;
1628 d = d * (256 - (int) (dx*dx+dy*dy)) / 256;
1629 d = (d * noise1[y][x]) >> 7;
1630 d = bound(0, d, 255);
1631 data[y][x][3] = (qbyte) d;
1638 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1642 for (i = 0;i < 16;i++)
1644 memset(&data[0][0][0], 255, sizeof(data));
1645 radius = i * 3.0f / 16.0f;
1646 f2 = 255.0f * ((15.0f - i) / 15.0f);
1647 for (y = 0;y < 32;y++)
1649 dy = (y - 16) * 0.25f;
1650 for (x = 0;x < 32;x++)
1652 dx = (x - 16) * 0.25f;
1653 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
1654 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1657 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1661 memset(&data[0][0][0], 255, sizeof(data));
1662 for (y = 0;y < 32;y++)
1665 for (x = 0;x < 32;x++)
1668 d = (256 - (dx*dx+dy*dy));
1669 d = bound(0, d, 255);
1670 data[y][x][3] = (qbyte) d;
1673 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1676 memset(&data[0][0][0], 255, sizeof(data));
1677 light[0] = 1;light[1] = 1;light[2] = 1;
1678 VectorNormalize(light);
1679 for (y = 0;y < 32;y++)
1680 for (x = 0;x < 32;x++)
1681 data[y][x][3] = shadebubble((x - 16) * (1.0 / 8.0), y < 24 ? (y - 24) * (1.0 / 24.0) : (y - 24) * (1.0 / 8.0), light);
1682 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1685 memset(&data[0][0][0], 255, sizeof(data));
1686 light[0] = 1;light[1] = 1;light[2] = 1;
1687 VectorNormalize(light);
1688 for (y = 0;y < 32;y++)
1689 for (x = 0;x < 32;x++)
1690 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
1691 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1694 for (i = 0;i < 8;i++)
1696 memset(&data[0][0][0], 255, sizeof(data));
1697 for (k = 0;k < 24;k++)
1698 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 160);
1699 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1700 particletextureinvert(&data[0][0][0]);
1701 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1705 for (i = 0;i < 8;i++)
1707 memset(&data[0][0][0], 255, sizeof(data));
1708 for (k = 0;k < 24;k++)
1709 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 96);
1710 for (j = 3;j < 7;j++)
1711 for (k = 0, m = rand() % 12;k < m;k++)
1712 particletextureblotch(&data[0][0][0], j, 96, 0, 0, 192);
1713 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1714 particletextureinvert(&data[0][0][0]);
1715 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1719 for (i = 0;i < 8;i++)
1721 memset(&data[0][0][0], 255, sizeof(data));
1722 for (k = 0;k < 12;k++)
1723 particletextureblotch(&data[0][0][0], 2, 0, 0, 0, 128);
1724 for (k = 0;k < 3;k++)
1725 particletextureblotch(&data[0][0][0], 14, 0, 0, 0, 160);
1726 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1727 particletextureinvert(&data[0][0][0]);
1728 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1732 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1733 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1734 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1736 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1737 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1738 particletexture[i].texture = particlefonttexture;
1741 fractalnoise(&noise1[0][0], 64, 4);
1743 for (y = 0;y < 64;y++)
1745 for (x = 0;x < 16;x++)
1751 d = d * d * noise1[y][x] / (7 * 7);
1752 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1753 data2[y][x][3] = 255;
1757 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "beam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1758 particletexture[tex_beam].s1 = 0;
1759 particletexture[tex_beam].t1 = 0;
1760 particletexture[tex_beam].s2 = 1;
1761 particletexture[tex_beam].t2 = 1;
1765 static void r_part_start(void)
1767 particletexturepool = R_AllocTexturePool();
1768 R_InitParticleTexture ();
1771 static void r_part_shutdown(void)
1773 R_FreeTexturePool(&particletexturepool);
1776 static void r_part_newmap(void)
1778 cl_numparticles = 0;
1779 cl_freeparticle = 0;
1782 void R_Particles_Init (void)
1784 Cvar_RegisterVariable(&r_drawparticles);
1785 #ifdef WORKINGLQUAKE
1788 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1792 #ifdef WORKINGLQUAKE
1793 void R_InitParticles(void)
1795 CL_Particles_Init();
1800 float particle_vertex3f[12], particle_texcoord2f[8];
1802 #ifdef WORKINGLQUAKE
1803 void R_DrawParticle(particle_t *p)
1806 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1808 const particle_t *p = calldata1;
1811 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1812 particletexture_t *tex;
1814 VectorCopy(p->org, org);
1816 tex = &particletexture[p->texnum];
1817 cr = p->color[0] * (1.0f / 255.0f);
1818 cg = p->color[1] * (1.0f / 255.0f);
1819 cb = p->color[2] * (1.0f / 255.0f);
1820 ca = p->alpha * (1.0f / 255.0f);
1821 if (p->blendmode == PBLEND_MOD)
1832 #ifndef WORKINGLQUAKE
1833 if (fogenabled && p->blendmode != PBLEND_MOD)
1835 VectorSubtract(org, r_vieworigin, fogvec);
1836 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1841 if (p->blendmode == 0)
1843 cr += fogcolor[0] * fog;
1844 cg += fogcolor[1] * fog;
1845 cb += fogcolor[2] * fog;
1849 R_Mesh_Matrix(&r_identitymatrix);
1851 memset(&m, 0, sizeof(m));
1852 m.tex[0] = R_GetTexture(tex->texture);
1853 m.pointer_texcoord[0] = particle_texcoord2f;
1854 m.pointer_vertex = particle_vertex3f;
1857 GL_Color(cr, cg, cb, ca);
1859 if (p->blendmode == 0)
1860 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1861 else if (p->blendmode == 1)
1862 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1864 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1865 GL_DepthMask(false);
1868 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1870 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1873 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1875 VectorNegate(p->vel2, v);
1876 VectorVectors(v, right, up);
1879 VectorVectors(p->vel2, right, up);
1880 VectorScale(right, p->scalex, right);
1881 VectorScale(up, p->scaley, up);
1885 VectorScale(r_viewleft, -p->scalex, right);
1886 VectorScale(r_viewup, p->scaley, up);
1888 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1889 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1890 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1891 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1892 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1893 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1894 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1895 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1896 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1897 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1898 particle_vertex3f[10] = org[1] + right[1] - up[1];
1899 particle_vertex3f[11] = org[2] + right[2] - up[2];
1900 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1901 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1902 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1903 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1905 else if (p->orientation == PARTICLE_SPARK)
1907 VectorMA(p->org, -p->scaley, p->vel, v);
1908 VectorMA(p->org, p->scaley, p->vel, up2);
1909 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1910 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1911 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1912 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1913 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1915 else if (p->orientation == PARTICLE_BEAM)
1917 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1918 VectorSubtract(p->vel2, p->org, up);
1919 VectorNormalizeFast(up);
1920 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1921 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1922 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1923 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1924 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1925 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1928 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1931 if (p->blendmode == 0)
1932 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1933 else if (p->blendmode == 1)
1934 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1936 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1937 glColor4f(cr, cg, cb, ca);
1939 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1940 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1941 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1942 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1945 R_Mesh_Draw(4, 2, polygonelements);
1949 void R_DrawParticles (void)
1952 float minparticledist;
1955 #ifdef WORKINGLQUAKE
1959 // LordHavoc: early out conditions
1960 if ((!cl_numparticles) || (!r_drawparticles.integer))
1963 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1965 #ifdef WORKINGLQUAKE
1966 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
1968 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1970 // LordHavoc: only render if not too close
1971 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1972 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
1975 glDisable(GL_BLEND);
1976 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1978 // LordHavoc: only render if not too close
1979 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1984 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
1985 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);