2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
25 #define NUMVERTEXNORMALS 162
26 siextern float r_avertexnormals[NUMVERTEXNORMALS][3];
27 #define m_bytenormals r_avertexnormals
28 #define VectorNormalizeFast VectorNormalize
29 #define CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
30 typedef unsigned char qbyte;
31 #define cl_stainmaps.integer 0
32 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
35 #define CL_EntityParticles R_EntityParticles
36 #define CL_ReadPointFile_f R_ReadPointFile_f
37 #define CL_ParseParticleEffect R_ParseParticleEffect
38 #define CL_ParticleExplosion R_ParticleExplosion
39 #define CL_ParticleExplosion2 R_ParticleExplosion2
40 #define CL_TeleportSplash R_TeleportSplash
41 #define CL_BlobExplosion R_BlobExplosion
42 #define CL_RunParticleEffect R_RunParticleEffect
43 #define CL_LavaSplash R_LavaSplash
44 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
46 vec3_t right1, right2, diff, normal;
48 VectorSubtract (org2, org1, normal);
49 VectorNormalizeFast (normal);
51 // calculate 'right' vector for start
52 VectorSubtract (r_vieworigin, org1, diff);
53 VectorNormalizeFast (diff);
54 CrossProduct (normal, diff, right1);
56 // calculate 'right' vector for end
57 VectorSubtract (r_vieworigin, org2, diff);
58 VectorNormalizeFast (diff);
59 CrossProduct (normal, diff, right2);
61 vert[ 0] = org1[0] + width * right1[0];
62 vert[ 1] = org1[1] + width * right1[1];
63 vert[ 2] = org1[2] + width * right1[2];
64 vert[ 3] = org1[0] - width * right1[0];
65 vert[ 4] = org1[1] - width * right1[1];
66 vert[ 5] = org1[2] - width * right1[2];
67 vert[ 6] = org2[0] - width * right2[0];
68 vert[ 7] = org2[1] - width * right2[1];
69 vert[ 8] = org2[2] - width * right2[2];
70 vert[ 9] = org2[0] + width * right2[0];
71 vert[10] = org2[1] + width * right2[1];
72 vert[11] = org2[2] + width * right2[2];
74 void fractalnoise(qbyte *noise, int size, int startgrid)
76 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
78 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
80 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
81 if (size != (1 << sizepower))
82 Sys_Error("fractalnoise: size must be power of 2\n");
84 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
85 if (startgrid != (1 << gridpower))
86 Sys_Error("fractalnoise: grid must be power of 2\n");
88 startgrid = bound(0, startgrid, size);
90 amplitude = 0xFFFF; // this gets halved before use
91 noisebuf = malloc(size*size*sizeof(int));
92 memset(noisebuf, 0, size*size*sizeof(int));
94 for (g2 = startgrid;g2;g2 >>= 1)
96 // brownian motion (at every smaller level there is random behavior)
98 for (y = 0;y < size;y += g2)
99 for (x = 0;x < size;x += g2)
100 n(x,y) += (rand()&litude);
105 // subdivide, diamond-square algorithm (really this has little to do with squares)
107 for (y = 0;y < size;y += g2)
108 for (x = 0;x < size;x += g2)
109 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
111 for (y = 0;y < size;y += g2)
112 for (x = 0;x < size;x += g2)
114 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
115 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
119 // find range of noise values
121 for (y = 0;y < size;y++)
122 for (x = 0;x < size;x++)
124 if (n(x,y) < min) min = n(x,y);
125 if (n(x,y) > max) max = n(x,y);
129 // normalize noise and copy to output
130 for (y = 0;y < size;y++)
131 for (x = 0;x < size;x++)
132 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
136 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
140 right[0] = forward[2];
141 right[1] = -forward[0];
142 right[2] = forward[1];
144 d = DotProduct(forward, right);
145 right[0] -= d * forward[0];
146 right[1] -= d * forward[1];
147 right[2] -= d * forward[2];
148 VectorNormalizeFast(right);
149 CrossProduct(right, forward, up);
153 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
155 float CL_TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal, int hitbmodels, int *hitent, int hitsupercontentsmask)
162 memset (&trace, 0, sizeof(trace));
164 VectorCopy (end, trace.endpos);
166 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
168 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
170 VectorCopy(trace.endpos, impact);
171 VectorCopy(trace.plane.normal, normal);
172 return trace.fraction;
175 #include "cl_collision.h"
179 #define MAX_PARTICLES 32768 // default max # of particles at one time
180 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
184 PARTICLE_BILLBOARD = 0,
186 PARTICLE_ORIENTED_DOUBLESIDED = 2,
199 typedef struct particletype_s
202 porientation_t orientation;
208 pt_alphastatic, pt_static, pt_spark, pt_beam, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_grow, pt_decal, pt_entityparticle, pt_total
212 // must match ptype_t values
213 particletype_t particletype[pt_total] =
215 {PBLEND_ALPHA, PARTICLE_BILLBOARD}, //pt_alphastatic
216 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_static
217 {PBLEND_ADD, PARTICLE_SPARK}, //pt_spark
218 {PBLEND_ADD, PARTICLE_BEAM}, //pt_beam
219 {PBLEND_ADD, PARTICLE_SPARK}, //pt_rain
220 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED}, //pt_raindecal
221 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_snow
222 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_bubble
223 {PBLEND_MOD, PARTICLE_BILLBOARD}, //pt_blood
224 {PBLEND_ADD, PARTICLE_BILLBOARD}, //pt_grow
225 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED}, //pt_decal
226 {PBLEND_ALPHA, PARTICLE_BILLBOARD}, //pt_entityparticle
229 typedef struct particle_s
231 particletype_t *type;
234 vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam
236 float alpha; // 0-255
237 float alphafade; // how much alpha reduces per second
238 float time2; // used for snow fluttering and decal fade
239 float bounce; // how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide, 2 = keep bouncing forever, 1.5 is typical)
240 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
241 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
243 #ifndef WORKINGLQUAKE
244 unsigned short owner; // decal stuck to this entity
245 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
246 vec3_t relativeorigin; // decal at this location in entity's coordinate space
247 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
252 static int particlepalette[256] =
254 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
255 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
256 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
257 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
258 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
259 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
260 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
261 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
262 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
263 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
264 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
265 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
266 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
267 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
268 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
269 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
270 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
271 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
272 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
273 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
274 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
275 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
276 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
277 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
278 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
279 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
280 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
281 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
282 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
283 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
284 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
285 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
288 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
290 // texture numbers in particle font
291 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
292 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
293 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
294 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
295 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
296 static const int tex_particle = 63;
297 static const int tex_bubble = 62;
298 static const int tex_raindrop = 61;
299 static const int tex_beam = 60;
301 static int cl_maxparticles;
302 static int cl_numparticles;
303 static int cl_freeparticle;
304 static particle_t *particles;
306 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
307 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
308 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
309 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
310 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
311 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
312 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
313 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
314 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
315 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
316 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
317 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
318 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
319 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
320 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
321 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
322 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
323 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
324 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
325 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
327 void CL_Particles_Clear(void)
331 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
339 void CL_ReadPointFile_f (void);
340 void CL_Particles_Init (void)
344 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
345 i = COM_CheckParm ("-particles");
347 if (i && i < com_argc - 1)
349 cl_maxparticles = (int)(atoi(com_argv[i+1]));
350 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
351 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
354 cl_maxparticles = MAX_PARTICLES;
356 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
358 Cvar_RegisterVariable (&cl_particles);
359 Cvar_RegisterVariable (&cl_particles_quality);
360 Cvar_RegisterVariable (&cl_particles_size);
361 Cvar_RegisterVariable (&cl_particles_bloodshowers);
362 Cvar_RegisterVariable (&cl_particles_blood);
363 Cvar_RegisterVariable (&cl_particles_blood_alpha);
364 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
365 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
366 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
367 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
368 Cvar_RegisterVariable (&cl_particles_explosions_shell);
369 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
370 Cvar_RegisterVariable (&cl_particles_smoke);
371 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
372 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
373 Cvar_RegisterVariable (&cl_particles_sparks);
374 Cvar_RegisterVariable (&cl_particles_bubbles);
375 Cvar_RegisterVariable (&cl_decals);
376 Cvar_RegisterVariable (&cl_decals_time);
377 Cvar_RegisterVariable (&cl_decals_fadetime);
380 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
382 particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
384 CL_Particles_Clear();
387 void CL_Particles_Shutdown (void)
390 // No clue what to do here...
394 // list of all 26 parameters:
395 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
396 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
397 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
398 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
399 // palpha - opacity of particle as 0-255 (can be more than 255)
400 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
401 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
402 // pgravity - how much effect gravity has on the particle (0-1)
403 // 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
404 // px,py,pz - starting origin of particle
405 // pvx,pvy,pvz - starting velocity of particle
406 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
407 particle_t *particle(particletype_t *ptype, int pcolor1, int pcolor2, int ptex, float psize, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pfriction)
410 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
411 ptempcolor = (pcolor1);
412 ptempcolor2 = (pcolor2);
413 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
414 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
415 pcb2 = (ptempcolor2) & 0xFF;
416 if (ptempcolor != ptempcolor2)
418 pcr1 = ((ptempcolor) >> 16) & 0xFF;
419 pcg1 = ((ptempcolor) >> 8) & 0xFF;
420 pcb1 = (ptempcolor) & 0xFF;
421 ptempcolor = rand() & 0xFF;
422 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
423 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
424 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
426 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
427 if (cl_freeparticle >= cl_maxparticles)
429 part = &particles[cl_freeparticle++];
430 if (cl_numparticles < cl_freeparticle)
431 cl_numparticles = cl_freeparticle;
432 memset(part, 0, sizeof(*part));
433 part->type = (ptype);
434 part->color[0] = pcr2;
435 part->color[1] = pcg2;
436 part->color[2] = pcb2;
437 part->color[3] = 0xFF;
439 part->size = (psize);
440 part->alpha = (palpha);
441 part->alphafade = (palphafade);
442 part->gravity = (pgravity);
443 part->bounce = (pbounce);
447 part->vel[0] = (pvx);
448 part->vel[1] = (pvy);
449 part->vel[2] = (pvz);
451 part->friction = (pfriction);
455 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
458 if (!cl_decals.integer)
460 p = particle(particletype + pt_decal, color1, color2, texnum, size, alpha, 0, 0, 0, org[0] + normal[0], org[1] + normal[1], org[2] + normal[2], normal[0], normal[1], normal[2], 0);
464 #ifndef WORKINGLQUAKE
466 p->ownermodel = cl_entities[p->owner].render.model;
467 Matrix4x4_Transform(&cl_entities[p->owner].render.inversematrix, org, p->relativeorigin);
468 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.inversematrix, normal, p->relativedirection);
469 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
474 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
477 float bestfrac, bestorg[3], bestnormal[3];
478 float frac, v[3], normal[3], org2[3];
479 int besthitent = 0, hitent;
481 for (i = 0;i < 32;i++)
484 VectorMA(org, maxdist, org2, org2);
485 frac = CL_TraceLine(org, org2, v, normal, true, &hitent, SUPERCONTENTS_SOLID);
490 VectorCopy(v, bestorg);
491 VectorCopy(normal, bestnormal);
495 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
503 void CL_EntityParticles (entity_t *ent)
507 float sp, sy, cp, cy;
511 static vec3_t avelocities[NUMVERTEXNORMALS];
512 if (!cl_particles.integer) return;
517 if (!avelocities[0][0])
518 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
519 avelocities[0][i] = (rand()&255) * 0.01;
521 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
523 angle = cl.time * avelocities[i][0];
526 angle = cl.time * avelocities[i][1];
535 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 0, 0, 0, ent->origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0);
537 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 0, 0, 0, ent->render.origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->render.origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->render.origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0);
543 void CL_ReadPointFile_f (void)
547 char *pointfile = NULL, *pointfilepos, *t, tchar;
548 char name[MAX_OSPATH];
553 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
554 strlcat (name, ".pts", sizeof (name));
556 pointfile = COM_LoadTempFile (name);
558 pointfile = FS_LoadFile(name, tempmempool, true);
562 Con_Printf("Could not open %s\n", name);
566 Con_Printf("Reading %s...\n", name);
569 pointfilepos = pointfile;
570 while (*pointfilepos)
572 while (*pointfilepos == '\n' || *pointfilepos == '\r')
577 while (*t && *t != '\n' && *t != '\r')
581 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
587 VectorCopy(org, leakorg);
590 if (cl_numparticles < cl_maxparticles - 3)
593 particle(particletype + pt_static, particlepalette[(-c)&15], particlepalette[(-c)&15], tex_particle, 2, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0);
596 #ifndef WORKINGLQUAKE
599 VectorCopy(leakorg, org);
600 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
602 particle(particletype + pt_beam, 0xFF0000, 0xFF0000, tex_beam, 64, 255, 0, 0, 0, org[0] - 4096, org[1], org[2], org[0] + 4096, org[1], org[2], 0);
603 particle(particletype + pt_beam, 0x00FF00, 0x00FF00, tex_beam, 64, 255, 0, 0, 0, org[0], org[1] - 4096, org[2], org[0], org[1] + 4096, org[2], 0);
604 particle(particletype + pt_beam, 0x0000FF, 0x0000FF, tex_beam, 64, 255, 0, 0, 0, org[0], org[1], org[2] - 4096, org[0], org[1], org[2] + 4096, 0);
609 CL_ParseParticleEffect
611 Parse an effect out of the server message
614 void CL_ParseParticleEffect (void)
617 int i, count, msgcount, color;
619 MSG_ReadVector(org, cl.protocol);
620 for (i=0 ; i<3 ; i++)
621 dir[i] = MSG_ReadChar () * (1.0/16);
622 msgcount = MSG_ReadByte ();
623 color = MSG_ReadByte ();
630 if (cl_particles_blood_bloodhack.integer)
635 CL_BloodPuff(org, dir, count / 2);
641 CL_BloodPuff(org, dir, count / 2);
645 CL_RunParticleEffect (org, dir, color, count);
654 void CL_ParticleExplosion (vec3_t org)
659 if (cl_stainmaps.integer)
660 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
661 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
663 i = CL_PointSuperContents(org);
664 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
666 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
667 for (i = 0;i < 128 * cl_particles_quality.value;i++)
668 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, (1.0f / cl_particles_quality.value) * lhrandom(128, 255), (1.0f / cl_particles_quality.value) * 128, -0.125, 1.5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-96, 96), lhrandom(-96, 96), lhrandom(-96, 96), (1.0 / 16.0));
672 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
674 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
676 for (i = 0;i < 32;i++)
681 v2[0] = lhrandom(-48, 48);
682 v2[1] = lhrandom(-48, 48);
683 v2[2] = lhrandom(-48, 48);
685 for (k = 0;k < 16;k++)
687 v[0] = org[0] + lhrandom(-48, 48);
688 v[1] = org[1] + lhrandom(-48, 48);
689 v[2] = org[2] + lhrandom(-48, 48);
690 if (CL_TraceLine(org, v, v2, NULL, true, NULL, SUPERCONTENTS_SOLID) >= 0.1)
693 VectorSubtract(v2, org, v2);
695 VectorScale(v2, 2.0f, v2);
696 particle(particletype + pt_static, 0xFFFFFF, 0xFFFFFF, tex_smoke[rand()&7], 12, 32, 64, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0);
700 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
701 for (i = 0;i < 128 * cl_particles_quality.value;i++)
702 particle(particletype + pt_spark, 0x903010, 0xFFD030, tex_particle, 1.0f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 512, 1, 0, org[0], org[1], org[2], lhrandom(-256, 256), lhrandom(-256, 256), lhrandom(-256, 256) + 80, 0.2);
705 if (cl_particles_explosions_shell.integer)
711 CL_ParticleExplosion2
715 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
721 if (!cl_particles.integer) return;
723 for (i = 0;i < 512 * cl_particles_quality.value;i++)
725 VectorRandom (offset);
726 VectorScale (offset, 192, vel);
727 VectorScale (offset, 8, offset);
728 k = particlepalette[colorStart + (i % colorLength)];
729 pscale = lhrandom(0.5, 1.5);
730 particle(particletype + pt_static, k, k, tex_particle, pscale, (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*512, 0, 0, org[0] + offset[0], org[1] + offset[1], org[2] + offset[2], vel[0], vel[1], vel[2], lhrandom(1.5, 3));
740 void CL_BlobExplosion (vec3_t org)
742 CL_ParticleExplosion(org);
751 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
757 CL_ParticleExplosion(org);
760 if (!cl_particles.integer) return;
761 count *= cl_particles_quality.value;
764 k = particlepalette[color + (rand()&7)];
765 if (gamemode == GAME_GOODVSBAD2)
766 particle(particletype + pt_alphastatic, k, k, tex_particle, 5, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 300, 0, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-10, 10), lhrandom(-10, 10), lhrandom(-10, 10), 0);
768 particle(particletype + pt_alphastatic, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 512, 0, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), dir[0] + lhrandom(-15, 15), dir[1] + lhrandom(-15, 15), dir[2] + lhrandom(-15, 15), 0);
772 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
778 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
782 if (!cl_particles.integer) return;
784 if (cl_particles_sparks.integer)
787 count *= cl_particles_quality.value;
790 k = particlepalette[0x68 + (rand() & 7)];
791 particle(particletype + pt_spark, k, k, tex_particle, 0.4f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, gravityscale, 0, org[0], org[1], org[2], lhrandom(-64, 64) + dir[0], lhrandom(-64, 64) + dir[1], lhrandom(0, 128) + dir[2], 0);
796 void CL_Smoke (vec3_t org, vec3_t dir, int count)
801 if (!cl_particles.integer) return;
804 if (cl_particles_smoke.integer)
806 k = count * 0.25 * cl_particles_quality.value;
809 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
810 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
811 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
812 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
813 particle(particletype + pt_grow, 0x101010, 0x202020, tex_smoke[rand()&7], 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 0, 0, org3[0], org3[1], org3[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
818 void CL_BulletMark (vec3_t org)
820 if (cl_stainmaps.integer)
821 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
822 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
825 void CL_PlasmaBurn (vec3_t org)
827 if (cl_stainmaps.integer)
828 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
829 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
832 static float bloodcount = 0;
833 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
837 // bloodcount is used to accumulate counts too small to cause a blood particle
838 if (!cl_particles.integer) return;
839 if (!cl_particles_blood.integer) return;
846 while(bloodcount > 0)
848 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
849 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
850 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
851 CL_TraceLine(org, org2, org3, NULL, true, NULL, SUPERCONTENTS_SOLID);
852 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, org3[0], org3[1], org3[2], vel[0] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 1);
853 bloodcount -= 16 / cl_particles_quality.value;
857 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
859 vec3_t org, vel, diff, center, velscale;
860 if (!cl_particles.integer) return;
861 if (!cl_particles_bloodshowers.integer) return;
862 if (!cl_particles_blood.integer) return;
864 VectorSubtract(maxs, mins, diff);
865 center[0] = (mins[0] + maxs[0]) * 0.5;
866 center[1] = (mins[1] + maxs[1]) * 0.5;
867 center[2] = (mins[2] + maxs[2]) * 0.5;
868 velscale[0] = velspeed * 2.0 / diff[0];
869 velscale[1] = velspeed * 2.0 / diff[1];
870 velscale[2] = velspeed * 2.0 / diff[2];
872 bloodcount += count * 5.0f;
873 while (bloodcount > 0)
875 org[0] = lhrandom(mins[0], maxs[0]);
876 org[1] = lhrandom(mins[1], maxs[1]);
877 org[2] = lhrandom(mins[2], maxs[2]);
878 vel[0] = (org[0] - center[0]) * velscale[0];
879 vel[1] = (org[1] - center[1]) * velscale[1];
880 vel[2] = (org[2] - center[2]) * velscale[2];
881 bloodcount -= 16 / cl_particles_quality.value;
882 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768 / cl_particles_quality.value, cl_particles_blood_alpha.value * 384 / cl_particles_quality.value, 0, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 1);
886 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
890 if (!cl_particles.integer) return;
891 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
892 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
893 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
895 count *= cl_particles_quality.value;
898 k = particlepalette[colorbase + (rand()&3)];
899 particle(particletype + pt_alphastatic, k, k, tex_particle, 2, 255 / cl_particles_quality.value, (255 / cl_particles_quality.value) / 2, gravity ? 1 : 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0] + lhrandom(-randomvel, randomvel), dir[1] + lhrandom(-randomvel, randomvel), dir[2] + lhrandom(-randomvel, randomvel), 0);
903 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
906 float t, z, minz, maxz;
907 if (!cl_particles.integer) return;
908 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
909 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
910 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
911 if (dir[2] < 0) // falling
916 minz = z - fabs(dir[2]) * 0.1;
917 maxz = z + fabs(dir[2]) * 0.1;
918 minz = bound(mins[2], minz, maxs[2]);
919 maxz = bound(mins[2], maxz, maxs[2]);
921 count *= cl_particles_quality.value;
926 count *= 4; // ick, this should be in the mod or maps?
930 k = particlepalette[colorbase + (rand()&3)];
931 if (gamemode == GAME_GOODVSBAD2)
932 particle(particletype + pt_rain, k, k, tex_particle, 20, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
934 particle(particletype + pt_rain, k, k, tex_particle, 0.5, lhrandom(8, 16) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
940 k = particlepalette[colorbase + (rand()&3)];
941 if (gamemode == GAME_GOODVSBAD2)
942 particle(particletype + pt_snow, k, k, tex_particle, 20, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
944 particle(particletype + pt_snow, k, k, tex_particle, 1, lhrandom(64, 128) / cl_particles_quality.value, 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0);
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(particletype + pt_static, 0x903010, 0xFFD030, tex_particle, 1.5, lhrandom(64, 128) / cl_particles_quality.value, 128 / cl_particles_quality.value, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0.2);
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(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(0, 64), 1);
997 particle(particletype + pt_static, 0x303030, 0x606060, tex_smoke[rand()&7], 6, lhrandom(48, 96) / cl_particles_quality.value, 64 / cl_particles_quality.value, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 32), 0);
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(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 1);
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(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 1, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0);
1049 k = l = particlepalette[224 + (rand()&7)];
1050 particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0);
1062 void CL_TeleportSplash (vec3_t org)
1065 if (!cl_particles.integer) return;
1067 inc = 8 / cl_particles_quality.value;
1068 for (i = -16;i < 16;i += inc)
1069 for (j = -16;j < 16;j += inc)
1070 for (k = -24;k < 32;k += inc)
1071 particle(particletype + pt_static, 0xA0A0A0, 0xFFFFFF, tex_particle, 10, inc * lhrandom(8, 16), inc * 32, 0, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-256, 256), 1);
1074 #ifdef WORKINGLQUAKE
1075 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1077 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1080 vec3_t vec, dir, vel, pos;
1081 float len, dec, speed, qd;
1082 int smoke, blood, bubbles;
1083 #ifdef WORKINGLQUAKE
1087 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1090 VectorSubtract(end, start, dir);
1091 VectorNormalize(dir);
1093 VectorSubtract (end, start, vec);
1094 #ifdef WORKINGLQUAKE
1095 len = VectorNormalize (vec);
1097 speed = 1.0f / cl.frametime;
1098 VectorSubtract(end, start, vel);
1100 len = VectorNormalizeLength (vec);
1101 dec = -ent->persistent.trail_time;
1102 ent->persistent.trail_time += len;
1103 if (ent->persistent.trail_time < 0.01f)
1106 // if we skip out, leave it reset
1107 ent->persistent.trail_time = 0.0f;
1109 speed = ent->state_current.time - ent->state_previous.time;
1111 speed = 1.0f / speed;
1112 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1113 color = particlepalette[color];
1115 VectorScale(vel, speed, vel);
1117 // advance into this frame to reach the first puff location
1118 VectorMA(start, dec, vec, pos);
1121 smoke = cl_particles.integer && cl_particles_smoke.integer;
1122 blood = cl_particles.integer && cl_particles_blood.integer;
1123 #ifdef WORKINGLQUAKE
1124 contents = CL_PointQ1Contents(pos);
1125 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1127 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1129 qd = 1.0f / cl_particles_quality.value;
1135 case 0: // rocket trail
1139 particle(particletype + pt_grow, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*125, qd*cl_particles_smoke_alphafade.value*125, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1140 particle(particletype + pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*288, qd*cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], lhrandom(-20, 20), lhrandom(-20, 20), lhrandom(-20, 20), 0);
1143 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, qd*lhrandom(64, 255), qd*256, -0.25, 1.5, pos[0], pos[1], pos[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), (1.0 / 16.0));
1146 case 1: // grenade trail
1147 // FIXME: make it gradually stop smoking
1150 particle(particletype + pt_grow, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*100, qd*cl_particles_smoke_alphafade.value*100, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1155 case 4: // slight blood
1158 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], vel[0] * 0.5f + lhrandom(-64, 64), vel[1] * 0.5f + lhrandom(-64, 64), vel[2] * 0.5f + lhrandom(-64, 64), 1);
1161 case 3: // green tracer
1165 if (gamemode == GAME_GOODVSBAD2)
1166 particle(particletype + pt_static, 0x00002E, 0x000030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1168 particle(particletype + pt_static, 0x002000, 0x003000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1172 case 5: // flame tracer
1175 particle(particletype + pt_static, 0x301000, 0x502000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1178 case 6: // voor trail
1182 if (gamemode == GAME_GOODVSBAD2)
1183 particle(particletype + pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, qd*255, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1184 else if (gamemode == GAME_PRYDON)
1185 particle(particletype + pt_static, 0x103040, 0x204050, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1187 particle(particletype + pt_static, 0x502030, 0x502030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1190 #ifndef WORKINGLQUAKE
1191 case 7: // Nehahra smoke tracer
1194 particle(particletype + pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, qd*64, qd*320, 0, 0, pos[0], pos[1], pos[2], lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(0, 16), 0);
1196 case 8: // Nexuiz plasma trail
1199 particle(particletype + pt_static, 0x283880, 0x283880, tex_particle, 4, qd*255, qd*1024, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0);
1201 case 9: // glow trail
1204 particle(particletype + pt_alphastatic, color, color, tex_particle, 5, qd*128, qd*320, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0);
1209 // advance to next time and position
1211 VectorMA (pos, dec, vec, pos);
1213 #ifndef WORKINGLQUAKE
1214 ent->persistent.trail_time = len;
1218 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1220 int tempcolor2, cr, cg, cb;
1224 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1225 particle(particletype + pt_beam, tempcolor2, tempcolor2, tex_beam, radius, alpha * 255, alpha * 255 / lifetime, 0, 0, start[0], start[1], start[2], end[0], end[1], end[2], 0);
1228 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1231 if (!cl_particles.integer) return;
1234 if (cl_particles_smoke.integer)
1235 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1236 particle(particletype + pt_grow, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count) * 0.5f, dir[1] + lhrandom(-count, count) * 0.5f, dir[2] + lhrandom(-count, count) * 0.5f, 0);
1239 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1242 if (!cl_particles.integer) return;
1244 if (cl_stainmaps.integer)
1245 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1246 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1249 if (cl_particles_smoke.integer)
1250 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1251 particle(particletype + pt_grow, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count), dir[1] + lhrandom(-count, count), dir[2] + lhrandom(-count, count), 0);
1254 if (cl_particles_sparks.integer)
1255 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1256 particle(particletype + pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, lhrandom(64, 255) / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0], org[1], org[2], lhrandom(-count, count) * 3.0f + dir[0], lhrandom(-count, count) * 3.0f + dir[1], lhrandom(-count, count) * 3.0f + dir[2], 0);
1264 void CL_MoveParticles (void)
1267 int i, maxparticle, j, a, content;
1268 float gravity, dvel, bloodwaterfade, frametime, f, dist, normal[3], v[3], org[3], oldorg[3];
1271 // LordHavoc: early out condition
1272 if (!cl_numparticles)
1274 cl_freeparticle = 0;
1278 #ifdef WORKINGLQUAKE
1279 frametime = cl.frametime;
1281 frametime = cl.time - cl.oldtime;
1283 gravity = frametime * sv_gravity.value;
1284 dvel = 1+4*frametime;
1285 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1289 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1296 p->alpha -= p->alphafade * frametime;
1304 if (p->type->orientation != PARTICLE_BEAM)
1306 VectorCopy(p->org, oldorg);
1307 VectorMA(p->org, frametime, p->vel, p->org);
1308 VectorCopy(p->org, org);
1311 if (p->type == particletype + pt_rain)
1313 // raindrop - splash on solid/water/slime/lava
1314 if (CL_TraceLine(oldorg, p->org, v, normal, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK) < 1)
1316 VectorCopy(v, p->org);
1318 p->type = particletype + pt_raindecal;
1319 // convert from a raindrop particle to a rainsplash decal
1320 p->texnum = tex_rainsplash[0];
1322 p->alphafade = p->alpha / 0.4;
1323 VectorCopy(normal, p->vel);
1324 VectorAdd(p->org, normal, p->org);
1331 else if (p->type == particletype + pt_blood)
1333 // blood - splash on solid
1334 if (CL_TraceLine(oldorg, p->org, v, normal, true, &hitent, SUPERCONTENTS_SOLID) < 1)
1336 VectorCopy(v, p->org);
1337 #ifndef WORKINGLQUAKE
1338 if (cl_stainmaps.integer)
1339 R_Stain(v, 32, 32, 16, 16, p->alpha * p->size * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->size * (1.0f / 40.0f));
1341 if (!cl_decals.integer)
1347 p->type = particletype + pt_decal;
1348 // convert from a blood particle to a blood decal
1349 p->texnum = tex_blooddecal[rand()&7];
1350 #ifndef WORKINGLQUAKE
1352 p->ownermodel = cl_entities[hitent].render.model;
1353 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, v, p->relativeorigin);
1354 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, normal, p->relativedirection);
1355 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
1359 VectorCopy(normal, p->vel);
1360 VectorAdd(p->org, normal, p->org);
1369 if (CL_TraceLine(oldorg, p->org, v, normal, true, NULL, SUPERCONTENTS_SOLID) < 1)
1371 VectorCopy(v, p->org);
1379 dist = DotProduct(p->vel, normal) * -p->bounce;
1380 VectorMA(p->vel, dist, normal, p->vel);
1381 if (DotProduct(p->vel, p->vel) < 0.03)
1382 VectorClear(p->vel);
1387 p->vel[2] -= p->gravity * gravity;
1391 f = p->friction * frametime;
1392 #ifdef WORKINGLQUAKE
1393 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1395 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1399 VectorScale(p->vel, f, p->vel);
1403 if (p->type != particletype + pt_static)
1405 switch (p->type - particletype)
1407 case pt_entityparticle:
1408 // particle that removes itself after one rendered frame
1415 #ifdef WORKINGLQUAKE
1416 a = CL_PointQ1Contents(p->org);
1417 if (a <= CONTENTS_WATER)
1419 a = CL_PointSuperContents(p->org);
1420 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1423 p->size += frametime * 8;
1424 //p->alpha -= bloodwaterfade;
1427 p->vel[2] -= gravity;
1428 #ifdef WORKINGLQUAKE
1429 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1431 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1436 #ifdef WORKINGLQUAKE
1437 a = CL_PointQ1Contents(p->org);
1438 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1440 a = CL_PointSuperContents(p->org);
1441 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1449 #ifdef WORKINGLQUAKE
1450 a = CL_PointQ1Contents(p->org);
1451 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1453 a = CL_PointSuperContents(p->org);
1454 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1459 if (cl.time > p->time2)
1462 p->time2 = cl.time + (rand() & 3) * 0.1;
1463 p->vel[0] += lhrandom(-32, 32);
1464 p->vel[1] += lhrandom(-32, 32);
1465 p->vel[2] += lhrandom(-32, 32);
1467 #ifdef WORKINGLQUAKE
1468 a = CL_PointQ1Contents(p->org);
1469 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1471 a = CL_PointSuperContents(p->org);
1472 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1477 p->size += frametime * 15;
1480 // FIXME: this has fairly wacky handling of alpha
1481 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1482 #ifndef WORKINGLQUAKE
1483 if (cl_entities[p->owner].render.model == p->ownermodel)
1485 Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1486 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1493 a = max(0, (cl.time - p->time2) * 40);
1495 p->texnum = tex_rainsplash[a];
1504 cl_numparticles = maxparticle + 1;
1505 cl_freeparticle = 0;
1508 #define MAX_PARTICLETEXTURES 64
1509 // particletexture_t is a rectangle in the particlefonttexture
1512 rtexture_t *texture;
1513 float s1, t1, s2, t2;
1518 static int particlefonttexture;
1520 static rtexturepool_t *particletexturepool;
1521 static rtexture_t *particlefonttexture;
1523 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1525 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1527 #define PARTICLETEXTURESIZE 64
1528 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
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) * PARTICLETEXTURESIZE;
1567 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1568 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1569 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1570 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1571 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1572 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1573 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 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, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1582 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1583 iradius = 1.0f / radius;
1584 alpha *= (1.0f / 255.0f);
1585 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1587 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1591 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1594 d = data + (y * PARTICLETEXTURESIZE + 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 < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;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 < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1619 data[0] = 255 - data[0];
1620 data[1] = 255 - data[1];
1621 data[2] = 255 - data[2];
1625 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1626 static void R_InitBloodTextures (qbyte *particletexturedata)
1629 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1632 for (i = 0;i < 8;i++)
1634 memset(&data[0][0][0], 255, sizeof(data));
1635 for (k = 0;k < 24;k++)
1636 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1637 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1638 particletextureinvert(&data[0][0][0]);
1639 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1643 for (i = 0;i < 8;i++)
1645 memset(&data[0][0][0], 255, sizeof(data));
1647 for (j = 1;j < 10;j++)
1648 for (k = min(j, m - 1);k < m;k++)
1649 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1650 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1651 particletextureinvert(&data[0][0][0]);
1652 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1657 static void R_InitParticleTexture (void)
1659 int x, y, d, i, k, m;
1660 float dx, dy, radius, f, f2;
1661 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1663 qbyte *particletexturedata;
1665 // a note: decals need to modulate (multiply) the background color to
1666 // properly darken it (stain), and they need to be able to alpha fade,
1667 // this is a very difficult challenge because it means fading to white
1668 // (no change to background) rather than black (darkening everything
1669 // behind the whole decal polygon), and to accomplish this the texture is
1670 // inverted (dark red blood on white background becomes brilliant cyan
1671 // and white on black background) so we can alpha fade it to black, then
1672 // we invert it again during the blendfunc to make it work...
1674 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1675 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1678 for (i = 0;i < 8;i++)
1680 memset(&data[0][0][0], 255, sizeof(data));
1683 qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1685 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1686 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1688 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1690 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1691 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1693 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1694 d = (noise2[y][x] - 128) * 3 + 192;
1696 d = d * (1-(dx*dx+dy*dy));
1697 d = (d * noise1[y][x]) >> 7;
1698 d = bound(0, d, 255);
1699 data[y][x][3] = (qbyte) d;
1706 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1710 for (i = 0;i < 16;i++)
1712 memset(&data[0][0][0], 255, sizeof(data));
1713 radius = i * 3.0f / 4.0f / 16.0f;
1714 f2 = 255.0f * ((15.0f - i) / 15.0f);
1715 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1717 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1718 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1720 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1721 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1722 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1725 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1729 memset(&data[0][0][0], 255, sizeof(data));
1730 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1732 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1733 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1735 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1736 d = 256 * (1 - (dx*dx+dy*dy));
1737 d = bound(0, d, 255);
1738 data[y][x][3] = (qbyte) d;
1741 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1744 memset(&data[0][0][0], 255, sizeof(data));
1745 light[0] = 1;light[1] = 1;light[2] = 1;
1746 VectorNormalize(light);
1747 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1749 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1750 // stretch upper half of bubble by +50% and shrink lower half by -50%
1751 // (this gives an elongated teardrop shape)
1753 dy = (dy - 0.5f) * 2.0f;
1755 dy = (dy - 0.5f) / 1.5f;
1756 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1758 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1759 // shrink bubble width to half
1761 data[y][x][3] = shadebubble(dx, dy, light);
1764 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1767 memset(&data[0][0][0], 255, sizeof(data));
1768 light[0] = 1;light[1] = 1;light[2] = 1;
1769 VectorNormalize(light);
1770 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1772 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1773 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1775 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1776 data[y][x][3] = shadebubble(dx, dy, light);
1779 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1781 // Blood particles and blood decals
1782 R_InitBloodTextures (particletexturedata);
1785 for (i = 0;i < 8;i++)
1787 memset(&data[0][0][0], 255, sizeof(data));
1788 for (k = 0;k < 12;k++)
1789 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1790 for (k = 0;k < 3;k++)
1791 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1792 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1793 particletextureinvert(&data[0][0][0]);
1794 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1798 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1799 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1800 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1804 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1807 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1808 if (!particlefonttexture)
1809 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1810 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1811 particletexture[i].texture = particlefonttexture;
1814 fractalnoise(&noise3[0][0], 64, 4);
1816 for (y = 0;y < 64;y++)
1818 dy = (y - 0.5f*64) / (64*0.5f-1);
1819 for (x = 0;x < 16;x++)
1821 dx = (x - 0.5f*16) / (16*0.5f-2);
1822 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1823 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1824 data2[y][x][3] = 255;
1829 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1832 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1833 if (!particletexture[tex_beam].texture)
1834 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1835 particletexture[tex_beam].s1 = 0;
1836 particletexture[tex_beam].t1 = 0;
1837 particletexture[tex_beam].s2 = 1;
1838 particletexture[tex_beam].t2 = 1;
1840 Mem_Free(particletexturedata);
1843 static void r_part_start(void)
1845 particletexturepool = R_AllocTexturePool();
1846 R_InitParticleTexture ();
1849 static void r_part_shutdown(void)
1851 R_FreeTexturePool(&particletexturepool);
1854 static void r_part_newmap(void)
1856 cl_numparticles = 0;
1857 cl_freeparticle = 0;
1860 void R_Particles_Init (void)
1862 Cvar_RegisterVariable(&r_drawparticles);
1863 #ifdef WORKINGLQUAKE
1866 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1870 #ifdef WORKINGLQUAKE
1871 void R_InitParticles(void)
1873 CL_Particles_Init();
1878 float particle_vertex3f[12], particle_texcoord2f[8];
1880 #ifdef WORKINGLQUAKE
1881 void R_DrawParticle(particle_t *p)
1884 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1886 const particle_t *p = calldata1;
1890 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, size;
1891 particletexture_t *tex;
1893 VectorCopy(p->org, org);
1895 blendmode = p->type->blendmode;
1896 tex = &particletexture[p->texnum];
1897 cr = p->color[0] * (1.0f / 255.0f);
1898 cg = p->color[1] * (1.0f / 255.0f);
1899 cb = p->color[2] * (1.0f / 255.0f);
1900 ca = p->alpha * (1.0f / 255.0f);
1901 if (blendmode == PBLEND_MOD)
1911 #ifndef WORKINGLQUAKE
1914 VectorSubtract(org, r_vieworigin, fogvec);
1915 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1920 if (blendmode == PBLEND_ADD)
1922 cr += fogcolor[0] * fog;
1923 cg += fogcolor[1] * fog;
1924 cb += fogcolor[2] * fog;
1928 R_Mesh_Matrix(&r_identitymatrix);
1930 memset(&m, 0, sizeof(m));
1931 m.tex[0] = R_GetTexture(tex->texture);
1932 m.pointer_texcoord[0] = particle_texcoord2f;
1933 m.pointer_vertex = particle_vertex3f;
1936 GL_Color(cr, cg, cb, ca);
1938 if (blendmode == PBLEND_ALPHA)
1939 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1940 else if (blendmode == PBLEND_ADD)
1941 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1942 else //if (blendmode == PBLEND_MOD)
1943 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1944 GL_DepthMask(false);
1947 size = p->size * cl_particles_size.value;
1948 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1950 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1953 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1955 VectorNegate(p->vel, v);
1956 VectorVectors(v, right, up);
1959 VectorVectors(p->vel, right, up);
1960 VectorScale(right, size, right);
1961 VectorScale(up, size, up);
1965 VectorScale(r_viewleft, -size, right);
1966 VectorScale(r_viewup, size, up);
1968 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1969 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1970 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1971 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1972 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1973 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1974 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1975 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1976 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1977 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1978 particle_vertex3f[10] = org[1] + right[1] - up[1];
1979 particle_vertex3f[11] = org[2] + right[2] - up[2];
1980 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1981 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1982 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1983 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1985 else if (p->type->orientation == PARTICLE_SPARK)
1987 VectorMA(p->org, -0.02, p->vel, v);
1988 VectorMA(p->org, 0.02, p->vel, up2);
1989 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
1990 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1991 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1992 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1993 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1995 else if (p->type->orientation == PARTICLE_BEAM)
1997 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
1998 VectorSubtract(p->vel, p->org, up);
1999 VectorNormalizeFast(up);
2000 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2001 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2002 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2003 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2004 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2005 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2008 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2011 if (blendmode == PBLEND_ALPHA)
2012 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2013 else if (blendmode == PBLEND_ADD)
2014 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2015 else //if (blendmode == PBLEND_MOD)
2016 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2017 glColor4f(cr, cg, cb, ca);
2019 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2020 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2021 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2022 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2025 R_Mesh_Draw(0, 4, 2, polygonelements);
2029 void R_DrawParticles (void)
2032 float minparticledist;
2035 #ifdef WORKINGLQUAKE
2039 // LordHavoc: early out conditions
2040 if ((!cl_numparticles) || (!r_drawparticles.integer))
2043 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2045 #ifdef WORKINGLQUAKE
2046 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2048 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2050 // LordHavoc: only render if not too close
2051 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2052 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2055 glDisable(GL_BLEND);
2056 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2058 // LordHavoc: only render if not too close
2059 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2064 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2066 if (p->type == particletype + pt_decal)
2067 R_DrawParticleCallback(p, 0);
2069 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);