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 CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
29 typedef unsigned char unsigned char;
30 #define cl_stainmaps.integer 0
31 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
34 #define CL_EntityParticles R_EntityParticles
35 #define CL_ReadPointFile_f R_ReadPointFile_f
36 #define CL_ParseParticleEffect R_ParseParticleEffect
37 #define CL_ParticleExplosion R_ParticleExplosion
38 #define CL_ParticleExplosion2 R_ParticleExplosion2
39 #define CL_TeleportSplash R_TeleportSplash
40 #define CL_BlobExplosion R_BlobExplosion
41 #define CL_RunParticleEffect R_RunParticleEffect
42 #define CL_LavaSplash R_LavaSplash
43 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
45 vec3_t right1, right2, diff, normal;
47 VectorSubtract (org2, org1, normal);
48 VectorNormalize (normal);
50 // calculate 'right' vector for start
51 VectorSubtract (r_vieworigin, org1, diff);
52 VectorNormalize (diff);
53 CrossProduct (normal, diff, right1);
55 // calculate 'right' vector for end
56 VectorSubtract (r_vieworigin, org2, diff);
57 VectorNormalize (diff);
58 CrossProduct (normal, diff, right2);
60 vert[ 0] = org1[0] + width * right1[0];
61 vert[ 1] = org1[1] + width * right1[1];
62 vert[ 2] = org1[2] + width * right1[2];
63 vert[ 3] = org1[0] - width * right1[0];
64 vert[ 4] = org1[1] - width * right1[1];
65 vert[ 5] = org1[2] - width * right1[2];
66 vert[ 6] = org2[0] - width * right2[0];
67 vert[ 7] = org2[1] - width * right2[1];
68 vert[ 8] = org2[2] - width * right2[2];
69 vert[ 9] = org2[0] + width * right2[0];
70 vert[10] = org2[1] + width * right2[1];
71 vert[11] = org2[2] + width * right2[2];
73 void fractalnoise(unsigned char *noise, int size, int startgrid)
75 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
77 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
79 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
80 if (size != (1 << sizepower))
82 Con_Printf("fractalnoise: size must be power of 2\n");
86 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
87 if (startgrid != (1 << gridpower))
89 Con_Printf("fractalnoise: grid must be power of 2\n");
93 startgrid = bound(0, startgrid, size);
95 amplitude = 0xFFFF; // this gets halved before use
96 noisebuf = malloc(size*size*sizeof(int));
97 memset(noisebuf, 0, size*size*sizeof(int));
99 for (g2 = startgrid;g2;g2 >>= 1)
101 // brownian motion (at every smaller level there is random behavior)
103 for (y = 0;y < size;y += g2)
104 for (x = 0;x < size;x += g2)
105 n(x,y) += (rand()&litude);
110 // subdivide, diamond-square algorithm (really this has little to do with squares)
112 for (y = 0;y < size;y += g2)
113 for (x = 0;x < size;x += g2)
114 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
116 for (y = 0;y < size;y += g2)
117 for (x = 0;x < size;x += g2)
119 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
120 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
124 // find range of noise values
126 for (y = 0;y < size;y++)
127 for (x = 0;x < size;x++)
129 if (n(x,y) < min) min = n(x,y);
130 if (n(x,y) > max) max = n(x,y);
134 // normalize noise and copy to output
135 for (y = 0;y < size;y++)
136 for (x = 0;x < size;x++)
137 *noise++ = (unsigned char) (((n(x,y) - min) * 256) / max);
141 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
145 right[0] = forward[2];
146 right[1] = -forward[0];
147 right[2] = forward[1];
149 d = DotProduct(forward, right);
150 right[0] -= d * forward[0];
151 right[1] -= d * forward[1];
152 right[2] -= d * forward[2];
153 VectorNormalize(right);
154 CrossProduct(right, forward, up);
158 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
160 trace_t CL_TraceBox (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hitbmodels, int *hitent, int hitsupercontentsmask, qboolean hitplayers)
167 memset (&trace, 0, sizeof(trace));
169 VectorCopy (end, trace.endpos);
171 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
173 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
178 #include "cl_collision.h"
182 #define MAX_PARTICLES 32768 // default max # of particles at one time
183 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
187 PARTICLE_BILLBOARD = 0,
189 PARTICLE_ORIENTED_DOUBLESIDED = 2,
202 typedef struct particletype_s
205 porientation_t orientation;
212 pt_alphastatic, pt_static, pt_spark, pt_beam, pt_rain, pt_raindecal, pt_snow, pt_bubble, pt_blood, pt_smoke, pt_decal, pt_entityparticle, pt_total
216 // must match ptype_t values
217 particletype_t particletype[pt_total] =
219 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
220 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
221 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
222 {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
223 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
224 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
225 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
226 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
227 {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
228 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
229 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
230 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
233 typedef struct particle_s
235 particletype_t *type;
238 vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam
240 float alpha; // 0-255
241 float alphafade; // how much alpha reduces per second
242 float time2; // used for snow fluttering and decal fade
243 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)
244 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
245 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
246 unsigned char color[4];
247 unsigned short owner; // decal stuck to this entity
248 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
249 vec3_t relativeorigin; // decal at this location in entity's coordinate space
250 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
254 static int particlepalette[256] =
256 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b, // 0-7
257 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb, // 8-15
258 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b, // 16-23
259 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23, // 24-31
260 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767, // 32-39
261 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb, // 40-47
262 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07, // 48-55
263 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f, // 56-63
264 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000, // 64-71
265 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000, // 72-79
266 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307, // 80-87
267 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723, // 88-95
268 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b, // 96-103
269 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b, // 104-111
270 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733, // 112-119
271 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397, // 120-127
272 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b, // 128-135
273 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707, // 136-143
274 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b, // 144-151
275 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707, // 152-159
276 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353, // 160-167
277 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07, // 168-175
278 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f, // 176-183
279 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07, // 184-191
280 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307, // 192-199
281 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700, // 200-207
282 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f, // 208-215
283 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f, // 216-223
284 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b, // 224-231
285 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b, // 232-239
286 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000, // 240-247
287 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53 // 248-255
290 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
291 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
292 int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
294 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
296 // texture numbers in particle font
297 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
298 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
299 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
300 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
301 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
302 static const int tex_particle = 63;
303 static const int tex_bubble = 62;
304 static const int tex_raindrop = 61;
305 static const int tex_beam = 60;
307 static int cl_maxparticles;
308 static int cl_numparticles;
309 static int cl_freeparticle;
310 static particle_t *particles;
312 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1", "enables particle effects"};
313 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1", "multiplies number of particles and reduces their alpha"};
314 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1", "multiplies particle size"};
315 cvar_t cl_particles_quake = {CVAR_SAVE, "cl_particles_quake", "0", "makes particle effects look mostly like the ones in Quake"};
316 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1", "enables blood shower effects"};
317 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1", "enables blood effects"};
318 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5", "opacity of blood"};
319 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1", "make certain quake particle() calls create blood effects instead"};
320 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1", "enables bulletimpact effects"};
321 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1", "enables bubbles from underwater explosions"};
322 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0", "enables smoke from explosions"};
323 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1", "enables sparks from explosions"};
324 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0", "enables polygonal shell from explosions"};
325 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1", "enables smoke (used by multiple effects)"};
326 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5", "smoke brightness"};
327 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55", "brightness fade per second"};
328 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1", "enables sparks (used by multiple effects)"};
329 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1", "enables bubbles (used by multiple effects)"};
330 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0", "enables decals (bullet holes, blood, etc)"};
331 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0", "how long before decals start to fade away"};
332 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20", "how long decals take to fade away"};
334 void CL_Particles_Clear(void)
338 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
346 void CL_ReadPointFile_f (void);
347 void CL_Particles_Init (void)
351 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
352 i = COM_CheckParm ("-particles");
354 if (i && i < com_argc - 1)
356 cl_maxparticles = (int)(atoi(com_argv[i+1]));
357 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
358 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
361 cl_maxparticles = MAX_PARTICLES;
363 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f, "display point file produced by qbsp when a leak was detected in the map (a line leading through the leak hole, to an entity inside the level)");
365 Cvar_RegisterVariable (&cl_particles);
366 Cvar_RegisterVariable (&cl_particles_quality);
367 Cvar_RegisterVariable (&cl_particles_size);
368 Cvar_RegisterVariable (&cl_particles_quake);
369 Cvar_RegisterVariable (&cl_particles_bloodshowers);
370 Cvar_RegisterVariable (&cl_particles_blood);
371 Cvar_RegisterVariable (&cl_particles_blood_alpha);
372 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
373 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
374 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
375 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
376 Cvar_RegisterVariable (&cl_particles_explosions_shell);
377 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
378 Cvar_RegisterVariable (&cl_particles_smoke);
379 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
380 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
381 Cvar_RegisterVariable (&cl_particles_sparks);
382 Cvar_RegisterVariable (&cl_particles_bubbles);
383 Cvar_RegisterVariable (&cl_decals);
384 Cvar_RegisterVariable (&cl_decals_time);
385 Cvar_RegisterVariable (&cl_decals_fadetime);
388 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
390 particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
392 CL_Particles_Clear();
395 void CL_Particles_Shutdown (void)
398 // No clue what to do here...
402 // list of all 26 parameters:
403 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
404 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
405 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
406 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
407 // palpha - opacity of particle as 0-255 (can be more than 255)
408 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
409 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
410 // pgravity - how much effect gravity has on the particle (0-1)
411 // 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
412 // px,py,pz - starting origin of particle
413 // pvx,pvy,pvz - starting velocity of particle
414 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
415 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, float originjitter, float velocityjitter)
420 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
421 if (cl_freeparticle >= cl_maxparticles)
423 part = &particles[cl_freeparticle++];
424 if (cl_numparticles < cl_freeparticle)
425 cl_numparticles = cl_freeparticle;
426 memset(part, 0, sizeof(*part));
428 l2 = (int)lhrandom(0.5, 256.5);
430 part->color[0] = ((((pcolor1 >> 16) & 0xFF) * l1 + ((pcolor2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
431 part->color[1] = ((((pcolor1 >> 8) & 0xFF) * l1 + ((pcolor2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
432 part->color[2] = ((((pcolor1 >> 0) & 0xFF) * l1 + ((pcolor2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
433 part->color[3] = 0xFF;
436 part->alpha = palpha;
437 part->alphafade = palphafade;
438 part->gravity = pgravity;
439 part->bounce = pbounce;
441 part->org[0] = px + originjitter * v[0];
442 part->org[1] = py + originjitter * v[1];
443 part->org[2] = pz + originjitter * v[2];
444 part->vel[0] = pvx + velocityjitter * v[0];
445 part->vel[1] = pvy + velocityjitter * v[1];
446 part->vel[2] = pvz + velocityjitter * v[2];
448 part->friction = pfriction;
452 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
455 if (!cl_decals.integer)
457 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, 0, 0);
461 #ifndef WORKINGLQUAKE
463 p->ownermodel = cl_entities[p->owner].render.model;
464 Matrix4x4_Transform(&cl_entities[p->owner].render.inversematrix, org, p->relativeorigin);
465 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.inversematrix, normal, p->relativedirection);
466 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
471 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
474 float bestfrac, bestorg[3], bestnormal[3];
476 int besthitent = 0, hitent;
479 for (i = 0;i < 32;i++)
482 VectorMA(org, maxdist, org2, org2);
483 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, &hitent, SUPERCONTENTS_SOLID, false);
484 if (bestfrac > trace.fraction)
486 bestfrac = trace.fraction;
488 VectorCopy(trace.endpos, bestorg);
489 VectorCopy(trace.plane.normal, bestnormal);
493 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
501 void CL_EntityParticles (entity_t *ent)
504 float pitch, yaw, dist = 64, beamlength = 16, org[3], v[3];
505 static vec3_t avelocities[NUMVERTEXNORMALS];
506 if (!cl_particles.integer) return;
509 VectorCopy(ent->origin, org);
511 Matrix4x4_OriginFromMatrix(&ent->render.matrix, org);
514 if (!avelocities[0][0])
515 for (i = 0;i < NUMVERTEXNORMALS * 3;i++)
516 avelocities[0][i] = lhrandom(0, 2.55);
518 for (i = 0;i < NUMVERTEXNORMALS;i++)
520 yaw = cl.time * avelocities[i][0];
521 pitch = cl.time * avelocities[i][1];
522 v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
523 v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
524 v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
525 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 1, 255, 0, 0, 0, v[0], v[1], v[2], 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 = (char *)FS_LoadFile(name, tempmempool, true, NULL);
549 Con_Printf("Could not open %s\n", name);
553 Con_Printf("Reading %s...\n", name);
554 VectorClear(leakorg);
557 pointfilepos = pointfile;
558 while (*pointfilepos)
560 while (*pointfilepos == '\n' || *pointfilepos == '\r')
565 while (*t && *t != '\n' && *t != '\r')
569 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
575 VectorCopy(org, leakorg);
578 if (cl_numparticles < cl_maxparticles - 3)
581 particle(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, 0, 0);
584 #ifndef WORKINGLQUAKE
587 VectorCopy(leakorg, org);
588 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
590 particle(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, 0, 0);
591 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, 0, 0);
592 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, 0, 0);
597 CL_ParseParticleEffect
599 Parse an effect out of the server message
602 void CL_ParseParticleEffect (void)
605 int i, count, msgcount, color;
607 MSG_ReadVector(org, cls.protocol);
608 for (i=0 ; i<3 ; i++)
609 dir[i] = MSG_ReadChar ();
610 msgcount = MSG_ReadByte ();
611 color = MSG_ReadByte ();
618 if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.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)
648 if (cl_stainmaps.integer)
649 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
650 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
652 if (cl_particles_quake.integer)
654 for (i = 0;i < 1024;i++)
660 color = particlepalette[ramp1[r]];
661 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, (1.0f / cl_particles_quality.value) * 32 * (8 - r), (1.0f / cl_particles_quality.value) * 318, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, 16, 256);
665 color = particlepalette[ramp2[r]];
666 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, (1.0f / cl_particles_quality.value) * 32 * (8 - r), (1.0f / cl_particles_quality.value) * 478, 0, 0, org[0], org[1], org[2], 0, 0, 0, 1, 16, 256);
672 i = CL_PointSuperContents(org);
673 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
675 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
676 for (i = 0;i < 128 * cl_particles_quality.value;i++)
677 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], org[1], org[2], 0, 0, 0, (1.0 / 16.0), 16, 96);
681 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
683 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
685 for (i = 0;i < 32;i++)
690 v2[0] = lhrandom(-48, 48);
691 v2[1] = lhrandom(-48, 48);
692 v2[2] = lhrandom(-48, 48);
694 for (k = 0;k < 16;k++)
696 v[0] = org[0] + lhrandom(-48, 48);
697 v[1] = org[1] + lhrandom(-48, 48);
698 v[2] = org[2] + lhrandom(-48, 48);
699 trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
700 if (trace.fraction >= 0.1)
703 VectorSubtract(trace.endpos, org, v2);
705 VectorScale(v2, 2.0f, v2);
706 particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 12, 32, 64, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0, 0, 0);
710 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
711 for (i = 0;i < 128 * cl_particles_quality.value;i++)
712 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], 0, 0, 80, 0.2, 0, 256);
716 if (cl_particles_explosions_shell.integer)
722 CL_ParticleExplosion2
726 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
729 if (!cl_particles.integer) return;
731 for (i = 0;i < 512 * cl_particles_quality.value;i++)
733 k = particlepalette[colorStart + (i % colorLength)];
734 if (cl_particles_quake.integer)
735 particle(particletype + pt_static, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*850, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, 8, 256);
737 particle(particletype + pt_static, k, k, tex_particle, lhrandom(0.5, 1.5), (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*512, 0, 0, org[0], org[1], org[2], 0, 0, 0, lhrandom(1.5, 3), 8, 192);
747 void CL_BlobExplosion (vec3_t org)
750 if (!cl_particles.integer) return;
752 if (!cl_particles_quake.integer)
754 CL_ParticleExplosion(org);
758 for (i = 0;i < 1024 * cl_particles_quality.value;i++)
762 k = particlepalette[66 + rand()%6];
763 particle(particletype + pt_static, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * lhrandom(182, 255), (1.0f/cl_particles_quality.value)*182, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, 16, 256);
767 k = particlepalette[150 + rand()%6];
768 particle(particletype + pt_static, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * lhrandom(182, 255), (1.0f/cl_particles_quality.value)*182, 0, 0, org[0], org[1], org[2], 0, 0, lhrandom(-256, 256), 0, 16, 0);
779 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
785 CL_ParticleExplosion(org);
788 if (!cl_particles.integer) return;
789 if (cl_particles_quake.integer)
791 count *= cl_particles_quality.value;
794 k = particlepalette[color + (rand()&7)];
795 particle(particletype + pt_alphastatic, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * lhrandom(51, 255), (1.0f / cl_particles_quality.value) * 512, 0, 0.05, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 8, 0);
800 count *= cl_particles_quality.value;
803 k = particlepalette[color + (rand()&7)];
804 if (gamemode == GAME_GOODVSBAD2)
805 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], org[1], org[2], 0, 0, 0, 0, 8, 10);
807 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], org[1], org[2], dir[0], dir[1], dir[2], 0, 8, 15);
812 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
818 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale, vec_t radius)
822 if (!cl_particles.integer) return;
824 if (cl_particles_sparks.integer)
827 count *= cl_particles_quality.value;
830 k = particlepalette[0x68 + (rand() & 7)];
831 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], dir[0], dir[1], dir[2] + sv_gravity.value * 0.1, 0, radius, 64);
836 void CL_Smoke (vec3_t org, vec3_t dir, int count, vec_t radius)
842 if (!cl_particles.integer) return;
845 if (cl_particles_smoke.integer)
847 k = count * 0.25 * cl_particles_quality.value;
850 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
851 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
852 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
853 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
854 particle(particletype + pt_smoke, 0x101010, 0x202020, tex_smoke[rand()&7], 3, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 1024, 0, 0, trace.endpos[0], trace.endpos[1], trace.endpos[2], 0, 0, 0, 0, radius, 8);
859 void CL_BulletMark (vec3_t org)
861 if (cl_stainmaps.integer)
862 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
863 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
866 void CL_PlasmaBurn (vec3_t org)
868 if (cl_stainmaps.integer)
869 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
870 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
873 static float bloodcount = 0;
874 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
879 // bloodcount is used to accumulate counts too small to cause a blood particle
880 if (!cl_particles.integer) return;
881 if (cl_particles_quake.integer)
883 CL_RunParticleEffect(org, vel, 73, count * 2);
886 if (!cl_particles_blood.integer) return;
893 while(bloodcount > 0)
895 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
896 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
897 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
898 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
899 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, trace.endpos[0], trace.endpos[1], trace.endpos[2], vel[0], vel[1], vel[2], 1, 0, s);
900 bloodcount -= 16 / cl_particles_quality.value;
904 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
906 vec3_t org, vel, diff, center, velscale;
907 if (!cl_particles.integer) return;
908 if (!cl_particles_bloodshowers.integer) return;
909 if (!cl_particles_blood.integer) return;
911 VectorSubtract(maxs, mins, diff);
912 center[0] = (mins[0] + maxs[0]) * 0.5;
913 center[1] = (mins[1] + maxs[1]) * 0.5;
914 center[2] = (mins[2] + maxs[2]) * 0.5;
915 velscale[0] = velspeed * 2.0 / diff[0];
916 velscale[1] = velspeed * 2.0 / diff[1];
917 velscale[2] = velspeed * 2.0 / diff[2];
919 bloodcount += count * 5.0f;
920 while (bloodcount > 0)
922 org[0] = lhrandom(mins[0], maxs[0]);
923 org[1] = lhrandom(mins[1], maxs[1]);
924 org[2] = lhrandom(mins[2], maxs[2]);
925 vel[0] = (org[0] - center[0]) * velscale[0];
926 vel[1] = (org[1] - center[1]) * velscale[1];
927 vel[2] = (org[2] - center[2]) * velscale[2];
928 bloodcount -= 16 / cl_particles_quality.value;
929 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, 0, 0);
933 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
937 if (!cl_particles.integer) return;
938 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
939 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
940 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
942 count *= cl_particles_quality.value;
945 k = particlepalette[colorbase + (rand()&3)];
946 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], dir[1], dir[2], 0, 0, randomvel);
950 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
953 float t, z, minz, maxz;
955 if (!cl_particles.integer) return;
956 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
957 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
958 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
959 if (dir[2] < 0) // falling
964 minz = z - fabs(dir[2]) * 0.1;
965 maxz = z + fabs(dir[2]) * 0.1;
966 minz = bound(mins[2], minz, maxs[2]);
967 maxz = bound(mins[2], maxz, maxs[2]);
969 count *= cl_particles_quality.value;
974 count *= 4; // ick, this should be in the mod or maps?
978 k = particlepalette[colorbase + (rand()&3)];
979 if (gamemode == GAME_GOODVSBAD2)
980 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, 0, 0);
982 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, 0, 0);
988 k = particlepalette[colorbase + (rand()&3)];
989 if (gamemode == GAME_GOODVSBAD2)
990 p = 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, 0, 0);
992 p = 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, 0, 0);
994 VectorCopy(p->vel, p->relativedirection);
998 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
1002 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
1006 vec3_t o, v, center;
1007 if (!cl_particles.integer) return;
1009 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1010 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1011 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1013 center[0] = (mins[0] + maxs[0]) * 0.5f;
1014 center[1] = (mins[1] + maxs[1]) * 0.5f;
1015 center[2] = (mins[2] + maxs[2]) * 0.5f;
1017 count *= cl_particles_quality.value;
1020 k = particlepalette[224 + (rand()&15)];
1021 o[0] = lhrandom(mins[0], maxs[0]);
1022 o[1] = lhrandom(mins[1], maxs[1]);
1023 o[2] = lhrandom(mins[2], maxs[2]);
1024 VectorSubtract(o, center, v);
1026 VectorScale(v, 100, v);
1027 v[2] += sv_gravity.value * 0.15f;
1028 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, 0, 0);
1032 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
1036 if (!cl_particles.integer) return;
1037 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
1038 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
1039 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
1041 count *= cl_particles_quality.value;
1044 k = particlepalette[224 + (rand()&15)];
1045 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]), 0, 0, 32, 1, 0, 32);
1047 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]), 0, 0, 24, 0, 0, 8);
1051 void CL_Flames (vec3_t org, vec3_t vel, int count)
1054 if (!cl_particles.integer) return;
1056 count *= cl_particles_quality.value;
1059 k = particlepalette[224 + (rand()&15)];
1060 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], vel[1], vel[2], 1, 0, 128);
1072 void CL_LavaSplash (vec3_t origin)
1074 float i, j, inc, vel;
1077 if (!cl_particles.integer) return;
1079 if (cl_particles_quake.integer)
1081 inc = 8 / cl_particles_quality.value;
1082 for (i = -128;i < 128;i += inc)
1084 for (j = -128;j < 128;j += inc)
1086 dir[0] = j + lhrandom(0, inc);
1087 dir[1] = i + lhrandom(0, inc);
1089 org[0] = origin[0] + dir[0];
1090 org[1] = origin[1] + dir[1];
1091 org[2] = origin[2] + lhrandom(0, 64);
1092 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1093 k = l = particlepalette[224 + (rand()&7)];
1094 particle(particletype + pt_alphastatic, k, l, tex_particle, 1, inc * lhrandom(24, 32), inc * 12, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0);
1100 inc = 32 / cl_particles_quality.value;
1101 for (i = -128;i < 128;i += inc)
1103 for (j = -128;j < 128;j += inc)
1105 dir[0] = j + lhrandom(0, inc);
1106 dir[1] = i + lhrandom(0, inc);
1108 org[0] = origin[0] + dir[0];
1109 org[1] = origin[1] + dir[1];
1110 org[2] = origin[2] + lhrandom(0, 64);
1111 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1112 if (gamemode == GAME_GOODVSBAD2)
1114 k = particlepalette[0 + (rand()&255)];
1115 l = particlepalette[0 + (rand()&255)];
1116 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, 0, 0);
1120 k = l = particlepalette[224 + (rand()&7)];
1121 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, 0, 0);
1134 void CL_TeleportSplash (vec3_t org)
1137 if (!cl_particles.integer) return;
1139 if (cl_particles_quake.integer)
1141 inc = 4 / cl_particles_quality.value;
1142 for (i = -16;i < 16;i += inc)
1144 for (j = -16;j < 16;j += inc)
1146 for (k = -24;k < 32;k += inc)
1150 VectorSet(dir, i*8, j*8, k*8);
1151 VectorNormalize(dir);
1152 vel = lhrandom(50, 113);
1153 particle(particletype + pt_alphastatic, particlepalette[7], particlepalette[14], tex_particle, 1, inc * lhrandom(37, 63), inc * 187, 0, 0, org[0] + i + lhrandom(0, inc), org[1] + j + lhrandom(0, inc), org[2] + k + lhrandom(0, inc), dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0);
1160 inc = 8 / cl_particles_quality.value;
1161 for (i = -16;i < 16;i += inc)
1162 for (j = -16;j < 16;j += inc)
1163 for (k = -24;k < 32;k += inc)
1164 particle(particletype + pt_static, 0xA0A0A0, 0xFFFFFF, tex_particle, 10, inc * lhrandom(8, 16), inc * 32, 0, 0, org[0] + i + lhrandom(0, inc), org[1] + j + lhrandom(0, inc), org[2] + k + lhrandom(0, inc), 0, 0, lhrandom(-256, 256), 1, 0, 0);
1168 #ifdef WORKINGLQUAKE
1169 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1171 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1174 vec3_t vec, dir, vel, pos;
1175 float len, dec, speed, qd;
1176 int smoke, blood, bubbles, r;
1177 #ifdef WORKINGLQUAKE
1181 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1184 VectorSubtract(end, start, dir);
1185 VectorNormalize(dir);
1187 VectorSubtract (end, start, vec);
1188 #ifdef WORKINGLQUAKE
1189 len = VectorNormalize (vec);
1191 speed = 1.0f / cl.frametime;
1192 VectorSubtract(end, start, vel);
1194 len = VectorNormalizeLength (vec);
1195 dec = -ent->persistent.trail_time;
1196 ent->persistent.trail_time += len;
1197 if (ent->persistent.trail_time < 0.01f)
1200 // if we skip out, leave it reset
1201 ent->persistent.trail_time = 0.0f;
1203 speed = ent->state_current.time - ent->state_previous.time;
1205 speed = 1.0f / speed;
1206 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1207 color = particlepalette[color];
1209 VectorScale(vel, speed, vel);
1211 // advance into this frame to reach the first puff location
1212 VectorMA(start, dec, vec, pos);
1215 smoke = cl_particles.integer && cl_particles_smoke.integer;
1216 blood = cl_particles.integer && cl_particles_blood.integer;
1217 #ifdef WORKINGLQUAKE
1218 contents = CL_PointQ1Contents(pos);
1219 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1221 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1223 qd = 1.0f / cl_particles_quality.value;
1229 case 0: // rocket trail
1230 if (cl_particles_quake.integer)
1234 color = particlepalette[ramp3[r]];
1235 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*42*(6-r), qd*306, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
1242 particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*62, qd*cl_particles_smoke_alphafade.value*62, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1243 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], 0, 0, 0, 0, 0, 20);
1246 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], 0, 0, 0, (1.0 / 16.0), 0, 16);
1250 case 1: // grenade trail
1251 if (cl_particles_quake.integer)
1255 color = particlepalette[ramp3[r]];
1256 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*42*(6-r), qd*306, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
1262 particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*50, qd*cl_particles_smoke_alphafade.value*50, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1268 case 4: // slight blood
1269 if (cl_particles_quake.integer)
1274 color = particlepalette[67 + (rand()&3)];
1275 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*128, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
1280 color = particlepalette[67 + (rand()&3)];
1281 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*128, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
1288 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, vel[1] * 0.5f, vel[2] * 0.5f, 1, 0, 64);
1292 case 3: // green tracer
1293 if (cl_particles_quake.integer)
1296 color = particlepalette[52 + (rand()&7)];
1297 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*512, 0, 0, pos[0], pos[1], pos[2], 30*vec[1], 30*-vec[0], 0, 0, 0, 0);
1298 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*512, 0, 0, pos[0], pos[1], pos[2], 30*-vec[1], 30*vec[0], 0, 0, 0, 0);
1305 if (gamemode == GAME_GOODVSBAD2)
1308 particle(particletype + pt_static, 0x00002E, 0x000030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1313 color = particlepalette[20 + (rand()&7)];
1314 particle(particletype + pt_static, color, color, tex_particle, 2, qd*64, qd*192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1320 case 5: // flame tracer
1321 if (cl_particles_quake.integer)
1324 color = particlepalette[230 + (rand()&7)];
1325 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*512, 0, 0, pos[0], pos[1], pos[2], 30*vec[1], 30*-vec[0], 0, 0, 0, 0);
1326 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*512, 0, 0, pos[0], pos[1], pos[2], 30*-vec[1], 30*vec[0], 0, 0, 0, 0);
1333 color = particlepalette[226 + (rand()&7)];
1334 particle(particletype + pt_static, color, color, tex_particle, 2, qd*64, qd*192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1339 case 6: // voor trail
1340 if (cl_particles_quake.integer)
1343 color = particlepalette[152 + (rand()&3)];
1344 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, qd*255, qd*850, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 8, 0);
1351 if (gamemode == GAME_GOODVSBAD2)
1354 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], 0, 0, 0, 0, 0, 0);
1356 else if (gamemode == GAME_PRYDON)
1359 particle(particletype + pt_static, 0x103040, 0x204050, tex_particle, 6, qd*64, qd*192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1364 particle(particletype + pt_static, 0x502030, 0x502030, tex_particle, 3, qd*64, qd*192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1369 #ifndef WORKINGLQUAKE
1370 case 7: // Nehahra smoke tracer
1373 particle(particletype + pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, qd*64, qd*320, 0, 0, pos[0], pos[1], pos[2], 0, 0, lhrandom(4, 12), 0, 0, 4);
1375 case 8: // Nexuiz plasma trail
1378 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, 0, 16);
1380 case 9: // glow trail
1383 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, 0, 0);
1387 Sys_Error("CL_RocketTrail: unknown trail type %i", type);
1390 // advance to next time and position
1392 VectorMA (pos, dec, vec, pos);
1394 #ifndef WORKINGLQUAKE
1395 ent->persistent.trail_time = len;
1399 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1401 int tempcolor2, cr, cg, cb;
1405 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1406 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, 0, 0);
1409 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1412 if (!cl_particles.integer) return;
1415 if (cl_particles_smoke.integer)
1416 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1417 particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, count * 0.125f, count * 0.5f);
1420 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1423 if (!cl_particles.integer) return;
1425 if (cl_stainmaps.integer)
1426 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1427 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1430 if (cl_particles_smoke.integer)
1431 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1432 particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255 / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, count * 0.125f, count);
1435 if (cl_particles_sparks.integer)
1436 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1437 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], dir[0], dir[1], dir[2], 0, 0, count * 3.0f);
1445 void CL_MoveParticles (void)
1448 int i, maxparticle, j, a, content;
1449 float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
1453 // LordHavoc: early out condition
1454 if (!cl_numparticles)
1456 cl_freeparticle = 0;
1460 #ifdef WORKINGLQUAKE
1461 frametime = cl.frametime;
1463 frametime = cl.time - cl.oldtime;
1465 gravity = frametime * sv_gravity.value;
1466 dvel = 1+4*frametime;
1467 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1471 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1478 p->alpha -= p->alphafade * frametime;
1486 if (p->type->orientation != PARTICLE_BEAM)
1488 VectorCopy(p->org, oldorg);
1489 VectorMA(p->org, frametime, p->vel, p->org);
1490 VectorCopy(p->org, org);
1493 if (p->type == particletype + pt_rain)
1495 // raindrop - splash on solid/water/slime/lava
1496 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
1497 if (trace.fraction < 1)
1500 // convert from a raindrop particle to a rainsplash decal
1501 VectorCopy(trace.endpos, p->org);
1502 VectorCopy(trace.plane.normal, p->vel);
1503 VectorAdd(p->org, p->vel, p->org);
1504 p->type = particletype + pt_raindecal;
1505 p->texnum = tex_rainsplash[0];
1507 p->alphafade = p->alpha / 0.4;
1514 particle(particletype + pt_spark, 0x000000, 0x707070, tex_particle, 0.25f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, 1, 0, p->org[0], p->org[1], p->org[2], p->vel[0]*16, p->vel[1]*16, 32 + p->vel[2]*16, 0, 0, 32);
1517 else if (p->type == particletype + pt_blood)
1519 // blood - splash on solid
1520 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
1521 if (trace.fraction < 1)
1523 // convert from a blood particle to a blood decal
1524 VectorCopy(trace.endpos, p->org);
1525 VectorCopy(trace.plane.normal, p->vel);
1526 VectorAdd(p->org, p->vel, p->org);
1527 #ifndef WORKINGLQUAKE
1528 if (cl_stainmaps.integer)
1529 R_Stain(p->org, 32, 32, 16, 16, p->alpha * p->size * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->size * (1.0f / 40.0f));
1531 if (!cl_decals.integer)
1537 p->type = particletype + pt_decal;
1538 p->texnum = tex_blooddecal[rand()&7];
1539 #ifndef WORKINGLQUAKE
1541 p->ownermodel = cl_entities[hitent].render.model;
1542 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1543 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1555 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
1556 if (trace.fraction < 1)
1558 VectorCopy(trace.endpos, p->org);
1566 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1567 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1568 if (DotProduct(p->vel, p->vel) < 0.03)
1569 VectorClear(p->vel);
1574 p->vel[2] -= p->gravity * gravity;
1578 f = p->friction * frametime;
1579 #ifdef WORKINGLQUAKE
1580 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1582 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1586 VectorScale(p->vel, f, p->vel);
1590 if (p->type != particletype + pt_static)
1592 switch (p->type - particletype)
1594 case pt_entityparticle:
1595 // particle that removes itself after one rendered frame
1602 #ifdef WORKINGLQUAKE
1603 a = CL_PointQ1Contents(p->org);
1604 if (a <= CONTENTS_WATER)
1606 a = CL_PointSuperContents(p->org);
1607 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1610 p->size += frametime * 8;
1611 //p->alpha -= bloodwaterfade;
1614 p->vel[2] -= gravity;
1615 #ifdef WORKINGLQUAKE
1616 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1618 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1623 #ifdef WORKINGLQUAKE
1624 a = CL_PointQ1Contents(p->org);
1625 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1627 a = CL_PointSuperContents(p->org);
1628 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1636 #ifdef WORKINGLQUAKE
1637 a = CL_PointQ1Contents(p->org);
1638 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1640 a = CL_PointSuperContents(p->org);
1641 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1646 if (cl.time > p->time2)
1649 p->time2 = cl.time + (rand() & 3) * 0.1;
1650 p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
1651 p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1652 //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
1654 #ifdef WORKINGLQUAKE
1655 a = CL_PointQ1Contents(p->org);
1656 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1658 a = CL_PointSuperContents(p->org);
1659 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1664 //p->size += frametime * 15;
1667 // FIXME: this has fairly wacky handling of alpha
1668 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1669 #ifndef WORKINGLQUAKE
1670 if (cl_entities[p->owner].render.model == p->ownermodel)
1672 Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1673 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1680 a = max(0, (cl.time - p->time2) * 40);
1682 p->texnum = tex_rainsplash[a];
1691 cl_numparticles = maxparticle + 1;
1692 cl_freeparticle = 0;
1695 #define MAX_PARTICLETEXTURES 64
1696 // particletexture_t is a rectangle in the particlefonttexture
1697 typedef struct particletexture_s
1699 rtexture_t *texture;
1700 float s1, t1, s2, t2;
1705 static int particlefonttexture;
1707 static rtexturepool_t *particletexturepool;
1708 static rtexture_t *particlefonttexture;
1710 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1712 static cvar_t r_drawparticles = {0, "r_drawparticles", "1", "enables drawing of particles"};
1714 #define PARTICLETEXTURESIZE 64
1715 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1717 static unsigned char shadebubble(float dx, float dy, vec3_t light)
1721 dz = 1 - (dx*dx+dy*dy);
1722 if (dz > 0) // it does hit the sphere
1726 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1727 VectorNormalize(normal);
1728 dot = DotProduct(normal, light);
1729 if (dot > 0.5) // interior reflection
1730 f += ((dot * 2) - 1);
1731 else if (dot < -0.5) // exterior reflection
1732 f += ((dot * -2) - 1);
1734 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1735 VectorNormalize(normal);
1736 dot = DotProduct(normal, light);
1737 if (dot > 0.5) // interior reflection
1738 f += ((dot * 2) - 1);
1739 else if (dot < -0.5) // exterior reflection
1740 f += ((dot * -2) - 1);
1742 f += 16; // just to give it a haze so you can see the outline
1743 f = bound(0, f, 255);
1744 return (unsigned char) f;
1750 static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
1752 int basex, basey, y;
1753 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1754 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1755 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1756 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1757 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1758 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1759 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1760 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1763 void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
1766 float cx, cy, dx, dy, f, iradius;
1768 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1769 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1770 iradius = 1.0f / radius;
1771 alpha *= (1.0f / 255.0f);
1772 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1774 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1778 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1781 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1782 d[0] += f * (red - d[0]);
1783 d[1] += f * (green - d[1]);
1784 d[2] += f * (blue - d[2]);
1790 void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1793 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1795 data[0] = bound(minr, data[0], maxr);
1796 data[1] = bound(ming, data[1], maxg);
1797 data[2] = bound(minb, data[2], maxb);
1801 void particletextureinvert(unsigned char *data)
1804 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1806 data[0] = 255 - data[0];
1807 data[1] = 255 - data[1];
1808 data[2] = 255 - data[2];
1812 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1813 static void R_InitBloodTextures (unsigned char *particletexturedata)
1816 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1819 for (i = 0;i < 8;i++)
1821 memset(&data[0][0][0], 255, sizeof(data));
1822 for (k = 0;k < 24;k++)
1823 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1824 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1825 particletextureinvert(&data[0][0][0]);
1826 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1830 for (i = 0;i < 8;i++)
1832 memset(&data[0][0][0], 255, sizeof(data));
1834 for (j = 1;j < 10;j++)
1835 for (k = min(j, m - 1);k < m;k++)
1836 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1837 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1838 particletextureinvert(&data[0][0][0]);
1839 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1844 static void R_InitParticleTexture (void)
1846 int x, y, d, i, k, m;
1847 float dx, dy, radius, f, f2;
1848 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1850 unsigned char *particletexturedata;
1852 // a note: decals need to modulate (multiply) the background color to
1853 // properly darken it (stain), and they need to be able to alpha fade,
1854 // this is a very difficult challenge because it means fading to white
1855 // (no change to background) rather than black (darkening everything
1856 // behind the whole decal polygon), and to accomplish this the texture is
1857 // inverted (dark red blood on white background becomes brilliant cyan
1858 // and white on black background) so we can alpha fade it to black, then
1859 // we invert it again during the blendfunc to make it work...
1861 particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1862 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1865 for (i = 0;i < 8;i++)
1867 memset(&data[0][0][0], 255, sizeof(data));
1870 unsigned char noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1872 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1873 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1875 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1877 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1878 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1880 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1881 d = (noise2[y][x] - 128) * 3 + 192;
1883 d = d * (1-(dx*dx+dy*dy));
1884 d = (d * noise1[y][x]) >> 7;
1885 d = bound(0, d, 255);
1886 data[y][x][3] = (unsigned char) d;
1893 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1897 for (i = 0;i < 16;i++)
1899 memset(&data[0][0][0], 255, sizeof(data));
1900 radius = i * 3.0f / 4.0f / 16.0f;
1901 f2 = 255.0f * ((15.0f - i) / 15.0f);
1902 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1904 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1905 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1907 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1908 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1909 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1912 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1916 memset(&data[0][0][0], 255, sizeof(data));
1917 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1919 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1920 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1922 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1923 d = 256 * (1 - (dx*dx+dy*dy));
1924 d = bound(0, d, 255);
1925 data[y][x][3] = (unsigned char) d;
1928 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1931 memset(&data[0][0][0], 255, sizeof(data));
1932 light[0] = 1;light[1] = 1;light[2] = 1;
1933 VectorNormalize(light);
1934 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1936 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1937 // stretch upper half of bubble by +50% and shrink lower half by -50%
1938 // (this gives an elongated teardrop shape)
1940 dy = (dy - 0.5f) * 2.0f;
1942 dy = (dy - 0.5f) / 1.5f;
1943 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1945 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1946 // shrink bubble width to half
1948 data[y][x][3] = shadebubble(dx, dy, light);
1951 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1954 memset(&data[0][0][0], 255, sizeof(data));
1955 light[0] = 1;light[1] = 1;light[2] = 1;
1956 VectorNormalize(light);
1957 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1959 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1960 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1962 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1963 data[y][x][3] = shadebubble(dx, dy, light);
1966 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1968 // Blood particles and blood decals
1969 R_InitBloodTextures (particletexturedata);
1972 for (i = 0;i < 8;i++)
1974 memset(&data[0][0][0], 255, sizeof(data));
1975 for (k = 0;k < 12;k++)
1976 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1977 for (k = 0;k < 3;k++)
1978 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1979 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1980 particletextureinvert(&data[0][0][0]);
1981 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1985 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1986 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1987 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1991 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1994 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1995 if (!particlefonttexture)
1996 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1997 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1998 particletexture[i].texture = particlefonttexture;
2001 fractalnoise(&noise3[0][0], 64, 4);
2003 for (y = 0;y < 64;y++)
2005 dy = (y - 0.5f*64) / (64*0.5f-1);
2006 for (x = 0;x < 16;x++)
2008 dx = (x - 0.5f*16) / (16*0.5f-2);
2009 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
2010 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
2011 data2[y][x][3] = 255;
2016 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
2019 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
2020 if (!particletexture[tex_beam].texture)
2021 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2022 particletexture[tex_beam].s1 = 0;
2023 particletexture[tex_beam].t1 = 0;
2024 particletexture[tex_beam].s2 = 1;
2025 particletexture[tex_beam].t2 = 1;
2027 Mem_Free(particletexturedata);
2030 static void r_part_start(void)
2032 particletexturepool = R_AllocTexturePool();
2033 R_InitParticleTexture ();
2036 static void r_part_shutdown(void)
2038 R_FreeTexturePool(&particletexturepool);
2041 static void r_part_newmap(void)
2043 cl_numparticles = 0;
2044 cl_freeparticle = 0;
2047 void R_Particles_Init (void)
2049 Cvar_RegisterVariable(&r_drawparticles);
2050 #ifdef WORKINGLQUAKE
2053 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
2057 #ifdef WORKINGLQUAKE
2058 void R_InitParticles(void)
2060 CL_Particles_Init();
2065 float particle_vertex3f[12], particle_texcoord2f[8];
2067 #ifdef WORKINGLQUAKE
2068 void R_DrawParticle(particle_t *p)
2071 void R_DrawParticle_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
2073 const particle_t *p = particles + surfacenumber;
2077 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, cr, cg, cb, ca, size;
2078 particletexture_t *tex;
2080 VectorCopy(p->org, org);
2082 blendmode = p->type->blendmode;
2083 tex = &particletexture[p->texnum];
2084 cr = p->color[0] * (1.0f / 255.0f);
2085 cg = p->color[1] * (1.0f / 255.0f);
2086 cb = p->color[2] * (1.0f / 255.0f);
2087 ca = p->alpha * (1.0f / 255.0f);
2088 if (blendmode == PBLEND_MOD)
2098 #ifndef WORKINGLQUAKE
2099 if (p->type->lighting)
2101 float ambient[3], diffuse[3], diffusenormal[3];
2102 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
2103 cr *= (ambient[0] + 0.5 * diffuse[0]);
2104 cg *= (ambient[1] + 0.5 * diffuse[1]);
2105 cb *= (ambient[2] + 0.5 * diffuse[2]);
2109 fog = VERTEXFOGTABLE(VectorDistance(org, r_vieworigin));
2114 if (blendmode == PBLEND_ALPHA)
2116 cr += fogcolor[0] * fog;
2117 cg += fogcolor[1] * fog;
2118 cb += fogcolor[2] * fog;
2122 R_Mesh_Matrix(&r_identitymatrix);
2124 memset(&m, 0, sizeof(m));
2125 m.tex[0] = R_GetTexture(tex->texture);
2126 m.pointer_texcoord[0] = particle_texcoord2f;
2127 m.pointer_vertex = particle_vertex3f;
2130 GL_Color(cr, cg, cb, ca);
2132 if (blendmode == PBLEND_ALPHA)
2133 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2134 else if (blendmode == PBLEND_ADD)
2135 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2136 else //if (blendmode == PBLEND_MOD)
2137 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2138 GL_DepthMask(false);
2141 size = p->size * cl_particles_size.value;
2142 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
2144 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
2147 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
2149 VectorNegate(p->vel, v);
2150 VectorVectors(v, right, up);
2153 VectorVectors(p->vel, right, up);
2154 VectorScale(right, size, right);
2155 VectorScale(up, size, up);
2159 VectorScale(r_viewleft, -size, right);
2160 VectorScale(r_viewup, size, up);
2162 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
2163 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
2164 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
2165 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
2166 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
2167 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
2168 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
2169 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
2170 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
2171 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
2172 particle_vertex3f[10] = org[1] + right[1] - up[1];
2173 particle_vertex3f[11] = org[2] + right[2] - up[2];
2174 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2175 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2176 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2177 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2179 else if (p->type->orientation == PARTICLE_SPARK)
2181 VectorMA(p->org, -0.02, p->vel, v);
2182 VectorMA(p->org, 0.02, p->vel, up2);
2183 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2184 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2185 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2186 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2187 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2189 else if (p->type->orientation == PARTICLE_BEAM)
2191 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2192 VectorSubtract(p->vel, p->org, up);
2193 VectorNormalize(up);
2194 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2195 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2196 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2197 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2198 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2199 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2203 Con_Printf("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2208 if (blendmode == PBLEND_ALPHA)
2209 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2210 else if (blendmode == PBLEND_ADD)
2211 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2212 else //if (blendmode == PBLEND_MOD)
2213 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2214 glColor4f(cr, cg, cb, ca);
2216 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2217 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2218 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2219 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2222 R_Mesh_Draw(0, 4, 2, polygonelements);
2226 void R_DrawParticles (void)
2229 float minparticledist;
2232 #ifdef WORKINGLQUAKE
2236 // LordHavoc: early out conditions
2237 if ((!cl_numparticles) || (!r_drawparticles.integer))
2240 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2242 #ifdef WORKINGLQUAKE
2243 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2245 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2247 // LordHavoc: only render if not too close
2248 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2249 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2252 glDisable(GL_BLEND);
2253 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2255 // LordHavoc: only render if not too close
2256 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2260 renderstats.particles++;
2261 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2263 if (p->type == particletype + pt_decal)
2264 R_DrawParticle_TransparentCallback(0, i, 0);
2266 R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL);