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_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 particle_t *particles;
286 static particle_t **freeparticles; // list used only in compacting particles array
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)
319 void CL_ReadPointFile_f (void);
320 void CL_Particles_Init (void)
324 i = COM_CheckParm ("-particles");
326 if (i && i < com_argc - 1)
328 cl_maxparticles = (int)(atoi(com_argv[i+1]));
329 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
330 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
333 cl_maxparticles = MAX_PARTICLES;
335 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
337 Cvar_RegisterVariable (&cl_particles);
338 Cvar_RegisterVariable (&cl_particles_quality);
339 Cvar_RegisterVariable (&cl_particles_size);
340 Cvar_RegisterVariable (&cl_particles_bloodshowers);
341 Cvar_RegisterVariable (&cl_particles_blood);
342 Cvar_RegisterVariable (&cl_particles_blood_alpha);
343 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
344 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
345 Cvar_RegisterVariable (&cl_particles_smoke);
346 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
347 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
348 Cvar_RegisterVariable (&cl_particles_sparks);
349 Cvar_RegisterVariable (&cl_particles_bubbles);
350 Cvar_RegisterVariable (&cl_decals);
351 Cvar_RegisterVariable (&cl_decals_time);
352 Cvar_RegisterVariable (&cl_decals_fadetime);
355 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
356 freeparticles = (void *) 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));
360 freeparticles = (void *) 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)
386 if (cl_numparticles < cl_maxparticles)
389 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
390 ptempcolor = (pcolor1);
391 ptempcolor2 = (pcolor2);
392 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
393 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
394 pcb2 = (ptempcolor2) & 0xFF;
395 if (ptempcolor != ptempcolor2)
397 pcr1 = ((ptempcolor) >> 16) & 0xFF;
398 pcg1 = ((ptempcolor) >> 8) & 0xFF;
399 pcb1 = (ptempcolor) & 0xFF;
400 ptempcolor = rand() & 0xFF;
401 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
402 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
403 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
405 part = &particles[cl_numparticles++];
406 memset(part, 0, sizeof(*part));
407 part->type = (ptype);
408 part->color[0] = pcr2;
409 part->color[1] = pcg2;
410 part->color[2] = pcb2;
411 part->color[3] = 0xFF;
412 part->orientation = porientation;
414 part->blendmode = pblendmode;
415 part->scalex = (pscalex);
416 part->scaley = (pscaley);
417 part->alpha = (palpha);
418 part->alphafade = (palphafade);
419 part->die = cl.time + (ptime);
420 part->gravity = (pgravity);
421 part->bounce = (pbounce);
425 part->vel[0] = (pvx);
426 part->vel[1] = (pvy);
427 part->vel[2] = (pvz);
428 part->time2 = (ptime2);
429 part->vel2[0] = (pvx2);
430 part->vel2[1] = (pvy2);
431 part->vel2[2] = (pvz2);
432 part->friction = (pfriction);
433 part->pressure = (ppressure);
439 void CL_SpawnDecalParticleForSurface(void *hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
442 if (!cl_decals.integer)
444 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);
445 #ifndef WORKINGLQUAKE
449 p->ownermodel = p->owner->model;
450 Matrix4x4_Transform(&p->owner->inversematrix, org, p->relativeorigin);
451 Matrix4x4_Transform3x3(&p->owner->inversematrix, normal, p->relativedirection);
452 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
457 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
460 float bestfrac, bestorg[3], bestnormal[3];
461 float frac, v[3], normal[3], org2[3];
463 void *besthitent = NULL, *hitent;
465 entity_render_t *besthitent = NULL, *hitent;
468 for (i = 0;i < 32;i++)
471 VectorMA(org, maxdist, org2, org2);
472 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
477 VectorCopy(v, bestorg);
478 VectorCopy(normal, bestnormal);
482 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
490 void CL_EntityParticles (entity_t *ent)
494 float sp, sy, cp, cy;
498 static vec3_t avelocities[NUMVERTEXNORMALS];
499 if (!cl_particles.integer) return;
504 if (!avelocities[0][0])
505 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
506 avelocities[0][i] = (rand()&255) * 0.01;
508 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
510 angle = cl.time * avelocities[i][0];
513 angle = cl.time * avelocities[i][1];
522 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);
524 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);
530 void CL_ReadPointFile_f (void)
534 char *pointfile = NULL, *pointfilepos, *t, tchar;
535 char name[MAX_OSPATH];
540 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
541 strlcat (name, ".pts", sizeof (name));
543 pointfile = COM_LoadTempFile (name);
545 pointfile = FS_LoadFile(name, true);
549 Con_Printf ("Could not open %s\n", name);
553 Con_Printf ("Reading %s...\n", name);
556 pointfilepos = pointfile;
557 while (*pointfilepos)
559 while (*pointfilepos == '\n' || *pointfilepos == '\r')
564 while (*t && *t != '\n' && *t != '\r')
568 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
574 VectorCopy(org, leakorg);
577 if (cl_numparticles < cl_maxparticles - 3)
580 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);
583 #ifndef WORKINGLQUAKE
586 VectorCopy(leakorg, org);
587 Con_Printf ("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
589 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);
590 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);
591 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);
596 CL_ParseParticleEffect
598 Parse an effect out of the server message
601 void CL_ParseParticleEffect (void)
604 int i, count, msgcount, color;
606 for (i=0 ; i<3 ; i++)
607 org[i] = MSG_ReadCoord ();
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, 0);
698 if (cl_explosions.integer)
704 CL_ParticleExplosion2
708 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
711 if (!cl_particles.integer) return;
713 for (i = 0;i < 512 * cl_particles_quality.value;i++)
715 k = particlepalette[colorStart + (i % colorLength)];
716 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] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192), 0, 0, 0, 0, 1, 0);
726 void CL_BlobExplosion (vec3_t org)
728 if (cl_stainmaps.integer)
729 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
730 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
732 if (cl_explosions.integer)
742 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
748 CL_ParticleExplosion(org);
751 if (!cl_particles.integer) return;
752 count *= cl_particles_quality.value;
755 k = particlepalette[color + (rand()&7)];
756 if (gamemode == GAME_GOODVSBAD2)
757 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);
759 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);
763 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
769 void CL_SparkShower (vec3_t org, vec3_t dir, int count)
774 if (cl_stainmaps.integer)
775 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
776 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
778 if (!cl_particles.integer) return;
780 if (cl_particles_bulletimpacts.integer)
783 if (cl_particles_smoke.integer)
785 k = count * 0.25 * cl_particles_quality.value;
788 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
789 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
790 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
791 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
792 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, 0);
796 if (cl_particles_sparks.integer)
799 count *= cl_particles_quality.value;
802 k = particlepalette[0x68 + (rand() & 7)];
803 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, 0);
809 void CL_PlasmaBurn (vec3_t org)
811 if (cl_stainmaps.integer)
812 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
813 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
816 static float bloodcount = 0;
817 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
821 // bloodcount is used to accumulate counts too small to cause a blood particle
822 if (!cl_particles.integer) return;
823 if (!cl_particles_blood.integer) return;
830 while(bloodcount > 0)
832 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
833 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
834 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
835 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
836 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);
837 bloodcount -= 16 / cl_particles_quality.value;
841 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
843 vec3_t org, vel, diff, center, velscale;
844 if (!cl_particles.integer) return;
845 if (!cl_particles_bloodshowers.integer) return;
846 if (!cl_particles_blood.integer) return;
848 VectorSubtract(maxs, mins, diff);
849 center[0] = (mins[0] + maxs[0]) * 0.5;
850 center[1] = (mins[1] + maxs[1]) * 0.5;
851 center[2] = (mins[2] + maxs[2]) * 0.5;
852 velscale[0] = velspeed * 2.0 / diff[0];
853 velscale[1] = velspeed * 2.0 / diff[1];
854 velscale[2] = velspeed * 2.0 / diff[2];
856 bloodcount += count * 5.0f;
857 while (bloodcount > 0)
859 org[0] = lhrandom(mins[0], maxs[0]);
860 org[1] = lhrandom(mins[1], maxs[1]);
861 org[2] = lhrandom(mins[2], maxs[2]);
862 vel[0] = (org[0] - center[0]) * velscale[0];
863 vel[1] = (org[1] - center[1]) * velscale[1];
864 vel[2] = (org[2] - center[2]) * velscale[2];
865 bloodcount -= 16 / cl_particles_quality.value;
866 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);
870 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
874 if (!cl_particles.integer) return;
875 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
876 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
877 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
879 count *= cl_particles_quality.value;
882 k = particlepalette[colorbase + (rand()&3)];
883 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);
887 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
890 float t, z, minz, maxz;
891 if (!cl_particles.integer) return;
892 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
893 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
894 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
895 if (dir[2] < 0) // falling
897 t = (maxs[2] - mins[2]) / -dir[2];
902 t = (maxs[2] - mins[2]) / dir[2];
905 if (t < 0 || t > 2) // sanity check
908 minz = z - fabs(dir[2]) * 0.1;
909 maxz = z + fabs(dir[2]) * 0.1;
910 minz = bound(mins[2], minz, maxs[2]);
911 maxz = bound(mins[2], maxz, maxs[2]);
913 count *= cl_particles_quality.value;
918 count *= 4; // ick, this should be in the mod or maps?
922 k = particlepalette[colorbase + (rand()&3)];
923 if (gamemode == GAME_GOODVSBAD2)
925 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);
929 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);
936 k = particlepalette[colorbase + (rand()&3)];
937 if (gamemode == GAME_GOODVSBAD2)
939 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);
943 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);
948 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
952 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
957 if (!cl_particles.integer) return;
959 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
960 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
961 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
963 center[0] = (mins[0] + maxs[0]) * 0.5f;
964 center[1] = (mins[1] + maxs[1]) * 0.5f;
965 center[2] = (mins[2] + maxs[2]) * 0.5f;
967 count *= cl_particles_quality.value;
970 k = particlepalette[224 + (rand()&15)];
971 o[0] = lhrandom(mins[0], maxs[0]);
972 o[1] = lhrandom(mins[1], maxs[1]);
973 o[2] = lhrandom(mins[2], maxs[2]);
974 VectorSubtract(o, center, v);
975 VectorNormalizeFast(v);
976 VectorScale(v, 100, v);
977 v[2] += sv_gravity.value * 0.15f;
978 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, 0);
982 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
986 if (!cl_particles.integer) return;
987 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
988 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
989 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
991 count *= cl_particles_quality.value;
994 k = particlepalette[224 + (rand()&15)];
995 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);
997 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);
1001 void CL_Flames (vec3_t org, vec3_t vel, int count)
1004 if (!cl_particles.integer) return;
1006 count *= cl_particles_quality.value;
1009 k = particlepalette[224 + (rand()&15)];
1010 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);
1022 void CL_LavaSplash (vec3_t origin)
1024 float i, j, inc, vel;
1027 if (!cl_particles.integer) return;
1029 inc = 32 / cl_particles_quality.value;
1030 for (i = -128;i < 128;i += inc)
1032 for (j = -128;j < 128;j += inc)
1034 dir[0] = j + lhrandom(0, 8);
1035 dir[1] = i + lhrandom(0, 8);
1037 org[0] = origin[0] + dir[0];
1038 org[1] = origin[1] + dir[1];
1039 org[2] = origin[2] + lhrandom(0, 64);
1040 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1041 if (gamemode == GAME_GOODVSBAD2)
1043 k = particlepalette[0 + (rand()&255)];
1044 l = particlepalette[0 + (rand()&255)];
1045 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);
1049 k = l = particlepalette[224 + (rand()&7)];
1050 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);
1063 void R_TeleportSplash (vec3_t org)
1066 if (!cl_particles.integer) return;
1068 inc = 8 / cl_particles_quality.value;
1069 for (i = -16;i < 16;i += inc)
1070 for (j = -16;j < 16;j += inc)
1071 for (k = -24;k < 32;k += inc)
1072 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);
1076 #ifdef WORKINGLQUAKE
1077 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1079 void CL_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
1082 vec3_t vec, dir, vel, pos;
1083 float len, dec, speed, qd;
1084 int contents, smoke, blood, bubbles;
1086 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1089 VectorSubtract(end, start, dir);
1090 VectorNormalize(dir);
1092 VectorSubtract (end, start, vec);
1093 #ifdef WORKINGLQUAKE
1094 len = VectorNormalize (vec);
1096 speed = 1.0f / cl.frametime;
1097 VectorSubtract(end, start, vel);
1099 len = VectorNormalizeLength (vec);
1100 dec = -ent->persistent.trail_time;
1101 ent->persistent.trail_time += len;
1102 if (ent->persistent.trail_time < 0.01f)
1105 // if we skip out, leave it reset
1106 ent->persistent.trail_time = 0.0f;
1108 speed = ent->state_current.time - ent->state_previous.time;
1110 speed = 1.0f / speed;
1111 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1113 VectorScale(vel, speed, vel);
1115 // advance into this frame to reach the first puff location
1116 VectorMA(start, dec, vec, pos);
1119 contents = CL_PointQ1Contents(pos);
1120 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
1123 smoke = cl_particles.integer && cl_particles_smoke.integer;
1124 blood = cl_particles.integer && cl_particles_blood.integer;
1125 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1126 qd = 1.0f / cl_particles_quality.value;
1132 case 0: // rocket trail
1136 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);
1137 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);
1140 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);
1143 case 1: // grenade trail
1144 // FIXME: make it gradually stop smoking
1147 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);
1152 case 4: // slight blood
1155 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);
1158 case 3: // green tracer
1162 if (gamemode == GAME_GOODVSBAD2)
1163 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);
1165 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);
1169 case 5: // flame tracer
1172 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);
1175 case 6: // voor trail
1179 if (gamemode == GAME_GOODVSBAD2)
1180 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);
1182 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);
1186 case 7: // Nehahra smoke tracer
1189 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);
1191 case 8: // Nexuiz plasma trail
1194 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);
1198 // advance to next time and position
1200 VectorMA (pos, dec, vec, pos);
1202 #ifndef WORKINGLQUAKE
1203 ent->persistent.trail_time = len;
1207 void CL_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
1211 if (!cl_particles.integer) return;
1212 if (!cl_particles_smoke.integer) return;
1214 VectorCopy(start, pos);
1215 VectorSubtract(end, start, vec);
1216 #ifdef WORKINGLQUAKE
1217 len = VectorNormalize(vec);
1219 len = VectorNormalizeLength(vec);
1221 color = particlepalette[color];
1222 dec = 3.0f / cl_particles_quality.value;
1225 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);
1227 VectorMA(pos, dec, vec, pos);
1231 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1233 int tempcolor2, cr, cg, cb;
1237 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1238 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);
1241 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1244 if (!cl_particles.integer) return;
1247 if (cl_particles_smoke.integer)
1248 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1249 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);
1252 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1255 if (!cl_particles.integer) return;
1257 if (cl_stainmaps.integer)
1258 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1259 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1262 if (cl_particles_smoke.integer)
1263 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1264 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);
1267 if (cl_particles_sparks.integer)
1268 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1269 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);
1277 void CL_MoveParticles (void)
1280 int i, activeparticles, maxparticle, j, a, pressureused = false, content;
1281 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3];
1282 #ifdef WORKINGLQUAKE
1285 entity_render_t *hitent;
1288 // LordHavoc: early out condition
1289 if (!cl_numparticles)
1292 #ifdef WORKINGLQUAKE
1293 frametime = cl.frametime;
1295 frametime = cl.time - cl.oldtime;
1297 gravity = frametime * sv_gravity.value;
1298 dvel = 1+4*frametime;
1299 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1301 activeparticles = 0;
1304 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1307 VectorCopy(p->org, p->oldorg);
1308 VectorMA(p->org, frametime, p->vel, p->org);
1309 VectorCopy(p->org, org);
1312 if (CL_TraceLine(p->oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1314 VectorCopy(v, p->org);
1317 // assume it's blood (lame, but...)
1318 #ifndef WORKINGLQUAKE
1319 if (cl_stainmaps.integer)
1320 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));
1322 if (cl_decals.integer)
1325 p->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
1326 // convert from a blood particle to a blood decal
1327 p->texnum = tex_blooddecal[rand()&7];
1328 #ifndef WORKINGLQUAKE
1330 p->ownermodel = hitent->model;
1331 Matrix4x4_Transform(&hitent->inversematrix, v, p->relativeorigin);
1332 Matrix4x4_Transform3x3(&hitent->inversematrix, normal, p->relativedirection);
1333 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1335 p->time2 = cl.time + cl_decals_time.value;
1336 p->die = p->time2 + cl_decals_fadetime.value;
1338 VectorCopy(normal, p->vel2);
1339 VectorClear(p->vel);
1340 VectorAdd(p->org, normal, p->org);
1350 freeparticles[j++] = p;
1356 dist = DotProduct(p->vel, normal) * -p->bounce;
1357 VectorMA(p->vel, dist, normal, p->vel);
1358 if (DotProduct(p->vel, p->vel) < 0.03)
1359 VectorClear(p->vel);
1363 p->vel[2] -= p->gravity * gravity;
1364 p->alpha -= p->alphafade * frametime;
1367 f = p->friction * frametime;
1369 content = CL_PointQ1Contents(p->org);
1370 if (content != CONTENTS_EMPTY)
1373 VectorScale(p->vel, f, p->vel);
1376 if (p->type != pt_static)
1382 content = CL_PointQ1Contents(p->org);
1384 if (a != CONTENTS_EMPTY)
1386 if (a == CONTENTS_WATER || a == CONTENTS_SLIME)
1388 p->scalex += frametime * 8;
1389 p->scaley += frametime * 8;
1390 //p->alpha -= bloodwaterfade;
1396 p->vel[2] -= gravity;
1400 content = CL_PointQ1Contents(p->org);
1401 if (content != CONTENTS_WATER && content != CONTENTS_SLIME)
1408 if (cl.time > p->time2)
1411 p->time2 = cl.time + (rand() & 3) * 0.1;
1412 p->vel[0] = lhrandom(-32, 32) + p->vel2[0];
1413 p->vel[1] = lhrandom(-32, 32) + p->vel2[1];
1414 p->vel[2] = /*lhrandom(-32, 32) +*/ p->vel2[2];
1417 content = CL_PointQ1Contents(p->org);
1419 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1423 p->scalex += frametime * p->time2;
1424 p->scaley += frametime * p->time2;
1427 #ifndef WORKINGLQUAKE
1428 if (p->owner->model == p->ownermodel)
1430 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1431 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1436 if (cl.time > p->time2)
1438 p->alphafade = p->alpha / (p->die - cl.time);
1439 p->type = pt_decalfade;
1443 #ifndef WORKINGLQUAKE
1444 if (p->owner->model == p->ownermodel)
1446 Matrix4x4_Transform(&p->owner->matrix, p->relativeorigin, p->org);
1447 Matrix4x4_Transform3x3(&p->owner->matrix, p->relativedirection, p->vel2);
1454 Con_Printf("unknown particle type %i\n", p->type);
1460 // remove dead particles
1461 if (p->alpha < 1 || p->die < cl.time)
1462 freeparticles[j++] = p;
1468 pressureused = true;
1471 // fill in gaps to compact the array
1473 while (maxparticle >= activeparticles)
1475 *freeparticles[i++] = particles[maxparticle--];
1476 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1479 cl_numparticles = activeparticles;
1483 activeparticles = 0;
1484 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1486 freeparticles[activeparticles++] = p;
1488 if (activeparticles)
1490 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1492 for (j = 0;j < activeparticles;j++)
1494 if (freeparticles[j] != p)
1496 float dist, diff[3];
1497 VectorSubtract(p->org, freeparticles[j]->org, diff);
1498 dist = DotProduct(diff, diff);
1499 if (dist < 4096 && dist >= 1)
1501 dist = freeparticles[j]->scalex * 4.0f * frametime / sqrt(dist);
1502 VectorMA(p->vel, dist, diff, p->vel);
1511 #define MAX_PARTICLETEXTURES 64
1512 // particletexture_t is a rectangle in the particlefonttexture
1515 rtexture_t *texture;
1516 float s1, t1, s2, t2;
1521 static int particlefonttexture;
1523 static rtexturepool_t *particletexturepool;
1524 static rtexture_t *particlefonttexture;
1526 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1528 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1530 static qbyte shadebubble(float dx, float dy, vec3_t light)
1534 dz = 1 - (dx*dx+dy*dy);
1535 if (dz > 0) // it does hit the sphere
1539 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1540 VectorNormalize(normal);
1541 dot = DotProduct(normal, light);
1542 if (dot > 0.5) // interior reflection
1543 f += ((dot * 2) - 1);
1544 else if (dot < -0.5) // exterior reflection
1545 f += ((dot * -2) - 1);
1547 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1548 VectorNormalize(normal);
1549 dot = DotProduct(normal, light);
1550 if (dot > 0.5) // interior reflection
1551 f += ((dot * 2) - 1);
1552 else if (dot < -0.5) // exterior reflection
1553 f += ((dot * -2) - 1);
1555 f += 16; // just to give it a haze so you can see the outline
1556 f = bound(0, f, 255);
1563 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1565 int basex, basey, y;
1566 basex = ((texnum >> 0) & 7) * 32;
1567 basey = ((texnum >> 3) & 7) * 32;
1568 particletexture[texnum].s1 = (basex + 1) / 256.0f;
1569 particletexture[texnum].t1 = (basey + 1) / 256.0f;
1570 particletexture[texnum].s2 = (basex + 31) / 256.0f;
1571 particletexture[texnum].t2 = (basey + 31) / 256.0f;
1572 for (y = 0;y < 32;y++)
1573 memcpy(particletexturedata + ((basey + y) * 256 + basex) * 4, data + y * 32 * 4, 32 * 4);
1576 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1579 float cx, cy, dx, dy, f, iradius;
1581 cx = lhrandom(radius + 1, 30 - radius);
1582 cy = lhrandom(radius + 1, 30 - radius);
1583 iradius = 1.0f / radius;
1584 alpha *= (1.0f / 255.0f);
1585 for (y = 0;y < 32;y++)
1587 for (x = 0;x < 32;x++)
1591 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1594 d = data + (y * 32 + x) * 4;
1595 d[0] += f * (red - d[0]);
1596 d[1] += f * (green - d[1]);
1597 d[2] += f * (blue - d[2]);
1603 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1606 for (i = 0;i < 32*32;i++, data += 4)
1608 data[0] = bound(minr, data[0], maxr);
1609 data[1] = bound(ming, data[1], maxg);
1610 data[2] = bound(minb, data[2], maxb);
1614 void particletextureinvert(qbyte *data)
1617 for (i = 0;i < 32*32;i++, data += 4)
1619 data[0] = 255 - data[0];
1620 data[1] = 255 - data[1];
1621 data[2] = 255 - data[2];
1625 static void R_InitParticleTexture (void)
1627 int x, y, d, i, j, k, m;
1628 float dx, dy, radius, f, f2;
1629 qbyte data[32][32][4], noise1[64][64], noise2[64][64], data2[64][16][4];
1631 qbyte particletexturedata[256*256*4];
1633 // a note: decals need to modulate (multiply) the background color to
1634 // properly darken it (stain), and they need to be able to alpha fade,
1635 // this is a very difficult challenge because it means fading to white
1636 // (no change to background) rather than black (darkening everything
1637 // behind the whole decal polygon), and to accomplish this the texture is
1638 // inverted (dark red blood on white background becomes brilliant cyan
1639 // and white on black background) so we can alpha fade it to black, then
1640 // we invert it again during the blendfunc to make it work...
1642 memset(particletexturedata, 255, sizeof(particletexturedata));
1645 for (i = 0;i < 8;i++)
1647 memset(&data[0][0][0], 255, sizeof(data));
1650 fractalnoise(&noise1[0][0], 64, 4);
1651 fractalnoise(&noise2[0][0], 64, 8);
1653 for (y = 0;y < 32;y++)
1656 for (x = 0;x < 32;x++)
1659 d = (noise2[y][x] - 128) * 3 + 192;
1661 d = d * (256 - (int) (dx*dx+dy*dy)) / 256;
1662 d = (d * noise1[y][x]) >> 7;
1663 d = bound(0, d, 255);
1664 data[y][x][3] = (qbyte) d;
1671 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1675 for (i = 0;i < 16;i++)
1677 memset(&data[0][0][0], 255, sizeof(data));
1678 radius = i * 3.0f / 16.0f;
1679 f2 = 255.0f * ((15.0f - i) / 15.0f);
1680 for (y = 0;y < 32;y++)
1682 dy = (y - 16) * 0.25f;
1683 for (x = 0;x < 32;x++)
1685 dx = (x - 16) * 0.25f;
1686 f = (1.0 - fabs(radius - sqrt(dx*dx+dy*dy))) * f2;
1687 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1690 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1694 memset(&data[0][0][0], 255, sizeof(data));
1695 for (y = 0;y < 32;y++)
1698 for (x = 0;x < 32;x++)
1701 d = (256 - (dx*dx+dy*dy));
1702 d = bound(0, d, 255);
1703 data[y][x][3] = (qbyte) d;
1706 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1709 memset(&data[0][0][0], 255, sizeof(data));
1710 light[0] = 1;light[1] = 1;light[2] = 1;
1711 VectorNormalize(light);
1712 for (y = 0;y < 32;y++)
1713 for (x = 0;x < 32;x++)
1714 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);
1715 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1718 memset(&data[0][0][0], 255, sizeof(data));
1719 light[0] = 1;light[1] = 1;light[2] = 1;
1720 VectorNormalize(light);
1721 for (y = 0;y < 32;y++)
1722 for (x = 0;x < 32;x++)
1723 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
1724 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1727 for (i = 0;i < 8;i++)
1729 memset(&data[0][0][0], 255, sizeof(data));
1730 for (k = 0;k < 24;k++)
1731 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 160);
1732 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1733 particletextureinvert(&data[0][0][0]);
1734 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1738 for (i = 0;i < 8;i++)
1740 memset(&data[0][0][0], 255, sizeof(data));
1741 for (k = 0;k < 24;k++)
1742 particletextureblotch(&data[0][0][0], 2, 96, 0, 0, 96);
1743 for (j = 3;j < 7;j++)
1744 for (k = 0, m = rand() % 12;k < m;k++)
1745 particletextureblotch(&data[0][0][0], j, 96, 0, 0, 192);
1746 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1747 particletextureinvert(&data[0][0][0]);
1748 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1752 for (i = 0;i < 8;i++)
1754 memset(&data[0][0][0], 255, sizeof(data));
1755 for (k = 0;k < 12;k++)
1756 particletextureblotch(&data[0][0][0], 2, 0, 0, 0, 128);
1757 for (k = 0;k < 3;k++)
1758 particletextureblotch(&data[0][0][0], 14, 0, 0, 0, 160);
1759 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1760 particletextureinvert(&data[0][0][0]);
1761 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1765 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1766 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1767 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1769 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", 256, 256, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1770 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1771 particletexture[i].texture = particlefonttexture;
1774 fractalnoise(&noise1[0][0], 64, 4);
1776 for (y = 0;y < 64;y++)
1778 for (x = 0;x < 16;x++)
1784 d = d * d * noise1[y][x] / (7 * 7);
1785 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1786 data2[y][x][3] = 255;
1790 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "beam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1791 particletexture[tex_beam].s1 = 0;
1792 particletexture[tex_beam].t1 = 0;
1793 particletexture[tex_beam].s2 = 1;
1794 particletexture[tex_beam].t2 = 1;
1798 static void r_part_start(void)
1800 particletexturepool = R_AllocTexturePool();
1801 R_InitParticleTexture ();
1804 static void r_part_shutdown(void)
1806 R_FreeTexturePool(&particletexturepool);
1809 static void r_part_newmap(void)
1811 cl_numparticles = 0;
1814 void R_Particles_Init (void)
1816 Cvar_RegisterVariable(&r_drawparticles);
1817 #ifdef WORKINGLQUAKE
1820 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1824 #ifdef WORKINGLQUAKE
1825 void R_InitParticles(void)
1827 CL_Particles_Init();
1832 float particle_vertex3f[12], particle_texcoord2f[8];
1834 #ifdef WORKINGLQUAKE
1835 void R_DrawParticle(particle_t *p)
1838 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1840 const particle_t *p = calldata1;
1843 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca;
1844 particletexture_t *tex;
1846 VectorCopy(p->org, org);
1848 tex = &particletexture[p->texnum];
1849 cr = p->color[0] * (1.0f / 255.0f);
1850 cg = p->color[1] * (1.0f / 255.0f);
1851 cb = p->color[2] * (1.0f / 255.0f);
1852 ca = p->alpha * (1.0f / 255.0f);
1853 if (p->blendmode == PBLEND_MOD)
1864 #ifndef WORKINGLQUAKE
1865 if (fogenabled && p->blendmode != PBLEND_MOD)
1867 VectorSubtract(org, r_vieworigin, fogvec);
1868 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1873 if (p->blendmode == 0)
1875 cr += fogcolor[0] * fog;
1876 cg += fogcolor[1] * fog;
1877 cb += fogcolor[2] * fog;
1881 GL_Color(cr, cg, cb, ca);
1883 R_Mesh_Matrix(&r_identitymatrix);
1885 memset(&m, 0, sizeof(m));
1886 m.tex[0] = R_GetTexture(tex->texture);
1887 m.pointer_texcoord[0] = particle_texcoord2f;
1888 R_Mesh_State_Texture(&m);
1890 if (p->blendmode == 0)
1891 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1892 else if (p->blendmode == 1)
1893 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1895 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1896 GL_DepthMask(false);
1898 GL_VertexPointer(particle_vertex3f);
1900 if (p->orientation == PARTICLE_BILLBOARD || p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1902 if (p->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1905 if (DotProduct(p->vel2, r_vieworigin) > DotProduct(p->vel2, org))
1907 VectorNegate(p->vel2, v);
1908 VectorVectors(v, right, up);
1911 VectorVectors(p->vel2, right, up);
1912 VectorScale(right, p->scalex, right);
1913 VectorScale(up, p->scaley, up);
1917 VectorScale(r_viewleft, -p->scalex, right);
1918 VectorScale(r_viewup, p->scaley, up);
1920 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1921 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1922 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1923 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1924 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1925 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1926 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1927 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1928 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1929 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1930 particle_vertex3f[10] = org[1] + right[1] - up[1];
1931 particle_vertex3f[11] = org[2] + right[2] - up[2];
1932 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1933 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1934 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1935 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1937 else if (p->orientation == PARTICLE_SPARK)
1939 VectorMA(p->org, -p->scaley, p->vel, v);
1940 VectorMA(p->org, p->scaley, p->vel, up2);
1941 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, p->scalex);
1942 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1943 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1944 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1945 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1947 else if (p->orientation == PARTICLE_BEAM)
1949 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel2, p->scalex);
1950 VectorSubtract(p->vel2, p->org, up);
1951 VectorNormalizeFast(up);
1952 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) - cl.time * 0.25;
1953 v[1] = DotProduct(p->vel2, up) * (1.0f / 64.0f) - cl.time * 0.25;
1954 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1955 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1956 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1957 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1960 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->orientation);
1963 if (p->blendmode == 0)
1964 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1965 else if (p->blendmode == 1)
1966 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1968 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1969 glColor4f(cr, cg, cb, ca);
1971 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
1972 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
1973 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
1974 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
1977 R_Mesh_Draw(4, 2, polygonelements);
1981 void R_DrawParticles (void)
1984 float minparticledist;
1987 #ifdef WORKINGLQUAKE
1991 // LordHavoc: early out conditions
1992 if ((!cl_numparticles) || (!r_drawparticles.integer))
1995 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1997 #ifdef WORKINGLQUAKE
1998 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2000 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2002 // LordHavoc: only render if not too close
2003 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2004 if (DotProduct(p->org, r_viewforward) >= minparticledist)
2007 glDisable(GL_BLEND);
2008 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2010 // LordHavoc: only render if not too close
2011 c_particles += cl_numparticles;
2012 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2013 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->orientation == PARTICLE_BEAM)
2014 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);