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"};
313 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
314 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
315 cvar_t cl_particles_quake = {CVAR_SAVE, "cl_particles_quake", "0"};
316 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
317 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
318 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
319 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
320 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
321 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
322 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
323 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
324 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
325 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
326 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
327 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
328 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
329 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
330 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
331 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
332 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
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);
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, cl.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)
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, 0, 64);
836 void CL_Smoke (vec3_t org, vec3_t dir, int count)
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, 0, 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)
1499 // convert from a raindrop particle to a rainsplash decal
1500 VectorCopy(trace.endpos, p->org);
1501 VectorCopy(trace.plane.normal, p->vel);
1502 VectorAdd(p->org, p->vel, p->org);
1503 p->type = particletype + pt_raindecal;
1504 p->texnum = tex_rainsplash[0];
1506 p->alphafade = p->alpha / 0.4;
1513 else if (p->type == particletype + pt_blood)
1515 // blood - splash on solid
1516 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
1517 if (trace.fraction < 1)
1519 // convert from a blood particle to a blood decal
1520 VectorCopy(trace.endpos, p->org);
1521 VectorCopy(trace.plane.normal, p->vel);
1522 VectorAdd(p->org, p->vel, p->org);
1523 #ifndef WORKINGLQUAKE
1524 if (cl_stainmaps.integer)
1525 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));
1527 if (!cl_decals.integer)
1533 p->type = particletype + pt_decal;
1534 p->texnum = tex_blooddecal[rand()&7];
1535 #ifndef WORKINGLQUAKE
1537 p->ownermodel = cl_entities[hitent].render.model;
1538 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1539 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1551 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
1552 if (trace.fraction < 1)
1554 VectorCopy(trace.endpos, p->org);
1562 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1563 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1564 if (DotProduct(p->vel, p->vel) < 0.03)
1565 VectorClear(p->vel);
1570 p->vel[2] -= p->gravity * gravity;
1574 f = p->friction * frametime;
1575 #ifdef WORKINGLQUAKE
1576 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1578 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1582 VectorScale(p->vel, f, p->vel);
1586 if (p->type != particletype + pt_static)
1588 switch (p->type - particletype)
1590 case pt_entityparticle:
1591 // particle that removes itself after one rendered frame
1598 #ifdef WORKINGLQUAKE
1599 a = CL_PointQ1Contents(p->org);
1600 if (a <= CONTENTS_WATER)
1602 a = CL_PointSuperContents(p->org);
1603 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1606 p->size += frametime * 8;
1607 //p->alpha -= bloodwaterfade;
1610 p->vel[2] -= gravity;
1611 #ifdef WORKINGLQUAKE
1612 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1614 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1619 #ifdef WORKINGLQUAKE
1620 a = CL_PointQ1Contents(p->org);
1621 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1623 a = CL_PointSuperContents(p->org);
1624 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1632 #ifdef WORKINGLQUAKE
1633 a = CL_PointQ1Contents(p->org);
1634 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1636 a = CL_PointSuperContents(p->org);
1637 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1642 if (cl.time > p->time2)
1645 p->time2 = cl.time + (rand() & 3) * 0.1;
1646 p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
1647 p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1648 //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
1650 #ifdef WORKINGLQUAKE
1651 a = CL_PointQ1Contents(p->org);
1652 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1654 a = CL_PointSuperContents(p->org);
1655 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1660 //p->size += frametime * 15;
1663 // FIXME: this has fairly wacky handling of alpha
1664 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1665 #ifndef WORKINGLQUAKE
1666 if (cl_entities[p->owner].render.model == p->ownermodel)
1668 Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1669 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1676 a = max(0, (cl.time - p->time2) * 40);
1678 p->texnum = tex_rainsplash[a];
1687 cl_numparticles = maxparticle + 1;
1688 cl_freeparticle = 0;
1691 #define MAX_PARTICLETEXTURES 64
1692 // particletexture_t is a rectangle in the particlefonttexture
1693 typedef struct particletexture_s
1695 rtexture_t *texture;
1696 float s1, t1, s2, t2;
1701 static int particlefonttexture;
1703 static rtexturepool_t *particletexturepool;
1704 static rtexture_t *particlefonttexture;
1706 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1708 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1710 #define PARTICLETEXTURESIZE 64
1711 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1713 static unsigned char shadebubble(float dx, float dy, vec3_t light)
1717 dz = 1 - (dx*dx+dy*dy);
1718 if (dz > 0) // it does hit the sphere
1722 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1723 VectorNormalize(normal);
1724 dot = DotProduct(normal, light);
1725 if (dot > 0.5) // interior reflection
1726 f += ((dot * 2) - 1);
1727 else if (dot < -0.5) // exterior reflection
1728 f += ((dot * -2) - 1);
1730 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1731 VectorNormalize(normal);
1732 dot = DotProduct(normal, light);
1733 if (dot > 0.5) // interior reflection
1734 f += ((dot * 2) - 1);
1735 else if (dot < -0.5) // exterior reflection
1736 f += ((dot * -2) - 1);
1738 f += 16; // just to give it a haze so you can see the outline
1739 f = bound(0, f, 255);
1740 return (unsigned char) f;
1746 static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
1748 int basex, basey, y;
1749 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1750 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1751 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1752 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1753 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1754 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1755 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1756 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1759 void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
1762 float cx, cy, dx, dy, f, iradius;
1764 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1765 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1766 iradius = 1.0f / radius;
1767 alpha *= (1.0f / 255.0f);
1768 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1770 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1774 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1777 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1778 d[0] += f * (red - d[0]);
1779 d[1] += f * (green - d[1]);
1780 d[2] += f * (blue - d[2]);
1786 void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1789 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1791 data[0] = bound(minr, data[0], maxr);
1792 data[1] = bound(ming, data[1], maxg);
1793 data[2] = bound(minb, data[2], maxb);
1797 void particletextureinvert(unsigned char *data)
1800 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1802 data[0] = 255 - data[0];
1803 data[1] = 255 - data[1];
1804 data[2] = 255 - data[2];
1808 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1809 static void R_InitBloodTextures (unsigned char *particletexturedata)
1812 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1815 for (i = 0;i < 8;i++)
1817 memset(&data[0][0][0], 255, sizeof(data));
1818 for (k = 0;k < 24;k++)
1819 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1820 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1821 particletextureinvert(&data[0][0][0]);
1822 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1826 for (i = 0;i < 8;i++)
1828 memset(&data[0][0][0], 255, sizeof(data));
1830 for (j = 1;j < 10;j++)
1831 for (k = min(j, m - 1);k < m;k++)
1832 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1833 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1834 particletextureinvert(&data[0][0][0]);
1835 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1840 static void R_InitParticleTexture (void)
1842 int x, y, d, i, k, m;
1843 float dx, dy, radius, f, f2;
1844 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1846 unsigned char *particletexturedata;
1848 // a note: decals need to modulate (multiply) the background color to
1849 // properly darken it (stain), and they need to be able to alpha fade,
1850 // this is a very difficult challenge because it means fading to white
1851 // (no change to background) rather than black (darkening everything
1852 // behind the whole decal polygon), and to accomplish this the texture is
1853 // inverted (dark red blood on white background becomes brilliant cyan
1854 // and white on black background) so we can alpha fade it to black, then
1855 // we invert it again during the blendfunc to make it work...
1857 particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1858 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1861 for (i = 0;i < 8;i++)
1863 memset(&data[0][0][0], 255, sizeof(data));
1866 unsigned char noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1868 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1869 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1871 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1873 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1874 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1876 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1877 d = (noise2[y][x] - 128) * 3 + 192;
1879 d = d * (1-(dx*dx+dy*dy));
1880 d = (d * noise1[y][x]) >> 7;
1881 d = bound(0, d, 255);
1882 data[y][x][3] = (unsigned char) d;
1889 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1893 for (i = 0;i < 16;i++)
1895 memset(&data[0][0][0], 255, sizeof(data));
1896 radius = i * 3.0f / 4.0f / 16.0f;
1897 f2 = 255.0f * ((15.0f - i) / 15.0f);
1898 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1900 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1901 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1903 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1904 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1905 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1908 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1912 memset(&data[0][0][0], 255, sizeof(data));
1913 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1915 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1916 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1918 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1919 d = 256 * (1 - (dx*dx+dy*dy));
1920 d = bound(0, d, 255);
1921 data[y][x][3] = (unsigned char) d;
1924 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1927 memset(&data[0][0][0], 255, sizeof(data));
1928 light[0] = 1;light[1] = 1;light[2] = 1;
1929 VectorNormalize(light);
1930 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1932 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1933 // stretch upper half of bubble by +50% and shrink lower half by -50%
1934 // (this gives an elongated teardrop shape)
1936 dy = (dy - 0.5f) * 2.0f;
1938 dy = (dy - 0.5f) / 1.5f;
1939 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1941 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1942 // shrink bubble width to half
1944 data[y][x][3] = shadebubble(dx, dy, light);
1947 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1950 memset(&data[0][0][0], 255, sizeof(data));
1951 light[0] = 1;light[1] = 1;light[2] = 1;
1952 VectorNormalize(light);
1953 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1955 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1956 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1958 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1959 data[y][x][3] = shadebubble(dx, dy, light);
1962 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1964 // Blood particles and blood decals
1965 R_InitBloodTextures (particletexturedata);
1968 for (i = 0;i < 8;i++)
1970 memset(&data[0][0][0], 255, sizeof(data));
1971 for (k = 0;k < 12;k++)
1972 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1973 for (k = 0;k < 3;k++)
1974 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1975 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1976 particletextureinvert(&data[0][0][0]);
1977 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1981 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1982 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1983 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1987 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1990 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1991 if (!particlefonttexture)
1992 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1993 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1994 particletexture[i].texture = particlefonttexture;
1997 fractalnoise(&noise3[0][0], 64, 4);
1999 for (y = 0;y < 64;y++)
2001 dy = (y - 0.5f*64) / (64*0.5f-1);
2002 for (x = 0;x < 16;x++)
2004 dx = (x - 0.5f*16) / (16*0.5f-2);
2005 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
2006 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
2007 data2[y][x][3] = 255;
2012 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
2015 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
2016 if (!particletexture[tex_beam].texture)
2017 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2018 particletexture[tex_beam].s1 = 0;
2019 particletexture[tex_beam].t1 = 0;
2020 particletexture[tex_beam].s2 = 1;
2021 particletexture[tex_beam].t2 = 1;
2023 Mem_Free(particletexturedata);
2026 static void r_part_start(void)
2028 particletexturepool = R_AllocTexturePool();
2029 R_InitParticleTexture ();
2032 static void r_part_shutdown(void)
2034 R_FreeTexturePool(&particletexturepool);
2037 static void r_part_newmap(void)
2039 cl_numparticles = 0;
2040 cl_freeparticle = 0;
2043 void R_Particles_Init (void)
2045 Cvar_RegisterVariable(&r_drawparticles);
2046 #ifdef WORKINGLQUAKE
2049 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
2053 #ifdef WORKINGLQUAKE
2054 void R_InitParticles(void)
2056 CL_Particles_Init();
2061 float particle_vertex3f[12], particle_texcoord2f[8];
2063 #ifdef WORKINGLQUAKE
2064 void R_DrawParticle(particle_t *p)
2067 void R_DrawParticleCallback(const void *calldata1, int calldata2)
2069 const particle_t *p = (particle_t *)calldata1;
2073 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, cr, cg, cb, ca, size;
2074 particletexture_t *tex;
2076 VectorCopy(p->org, org);
2078 blendmode = p->type->blendmode;
2079 tex = &particletexture[p->texnum];
2080 cr = p->color[0] * (1.0f / 255.0f);
2081 cg = p->color[1] * (1.0f / 255.0f);
2082 cb = p->color[2] * (1.0f / 255.0f);
2083 ca = p->alpha * (1.0f / 255.0f);
2084 if (blendmode == PBLEND_MOD)
2094 #ifndef WORKINGLQUAKE
2095 if (p->type->lighting)
2097 float ambient[3], diffuse[3], diffusenormal[3];
2098 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
2099 cr *= (ambient[0] + 0.5 * diffuse[0]);
2100 cg *= (ambient[1] + 0.5 * diffuse[1]);
2101 cb *= (ambient[2] + 0.5 * diffuse[2]);
2105 fog = VERTEXFOGTABLE(VectorDistance(org, r_vieworigin));
2110 if (blendmode == PBLEND_ALPHA)
2112 cr += fogcolor[0] * fog;
2113 cg += fogcolor[1] * fog;
2114 cb += fogcolor[2] * fog;
2118 R_Mesh_Matrix(&r_identitymatrix);
2120 memset(&m, 0, sizeof(m));
2121 m.tex[0] = R_GetTexture(tex->texture);
2122 m.pointer_texcoord[0] = particle_texcoord2f;
2123 m.pointer_vertex = particle_vertex3f;
2126 GL_Color(cr, cg, cb, ca);
2128 if (blendmode == PBLEND_ALPHA)
2129 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2130 else if (blendmode == PBLEND_ADD)
2131 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2132 else //if (blendmode == PBLEND_MOD)
2133 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2134 GL_DepthMask(false);
2137 size = p->size * cl_particles_size.value;
2138 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
2140 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
2143 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
2145 VectorNegate(p->vel, v);
2146 VectorVectors(v, right, up);
2149 VectorVectors(p->vel, right, up);
2150 VectorScale(right, size, right);
2151 VectorScale(up, size, up);
2155 VectorScale(r_viewleft, -size, right);
2156 VectorScale(r_viewup, size, up);
2158 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
2159 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
2160 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
2161 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
2162 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
2163 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
2164 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
2165 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
2166 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
2167 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
2168 particle_vertex3f[10] = org[1] + right[1] - up[1];
2169 particle_vertex3f[11] = org[2] + right[2] - up[2];
2170 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2171 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2172 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2173 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2175 else if (p->type->orientation == PARTICLE_SPARK)
2177 VectorMA(p->org, -0.02, p->vel, v);
2178 VectorMA(p->org, 0.02, p->vel, up2);
2179 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2180 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2181 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2182 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2183 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2185 else if (p->type->orientation == PARTICLE_BEAM)
2187 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2188 VectorSubtract(p->vel, p->org, up);
2189 VectorNormalize(up);
2190 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2191 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2192 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2193 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2194 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2195 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2199 Con_Printf("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2204 if (blendmode == PBLEND_ALPHA)
2205 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2206 else if (blendmode == PBLEND_ADD)
2207 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2208 else //if (blendmode == PBLEND_MOD)
2209 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2210 glColor4f(cr, cg, cb, ca);
2212 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2213 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2214 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2215 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2218 R_Mesh_Draw(0, 4, 2, polygonelements);
2222 void R_DrawParticles (void)
2225 float minparticledist;
2228 #ifdef WORKINGLQUAKE
2232 // LordHavoc: early out conditions
2233 if ((!cl_numparticles) || (!r_drawparticles.integer))
2236 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2238 #ifdef WORKINGLQUAKE
2239 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2241 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2243 // LordHavoc: only render if not too close
2244 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2245 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2248 glDisable(GL_BLEND);
2249 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2251 // LordHavoc: only render if not too close
2252 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2256 renderstats.particles++;
2257 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2259 if (p->type == particletype + pt_decal)
2260 R_DrawParticleCallback(p, 0);
2262 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);