2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN))
25 #define NUMVERTEXNORMALS 162
26 siextern float r_avertexnormals[NUMVERTEXNORMALS][3];
27 #define m_bytenormals r_avertexnormals
28 #define VectorNormalizeFast VectorNormalize
29 #define CL_PointQ1Contents(v) (Mod_PointInLeaf(v,cl.worldmodel)->contents)
30 typedef unsigned char qbyte;
31 #define cl_stainmaps.integer 0
32 void R_Stain (vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
35 #define CL_EntityParticles R_EntityParticles
36 #define CL_ReadPointFile_f R_ReadPointFile_f
37 #define CL_ParseParticleEffect R_ParseParticleEffect
38 #define CL_ParticleExplosion R_ParticleExplosion
39 #define CL_ParticleExplosion2 R_ParticleExplosion2
40 #define CL_TeleportSplash R_TeleportSplash
41 #define CL_BlobExplosion R_BlobExplosion
42 #define CL_RunParticleEffect R_RunParticleEffect
43 #define CL_LavaSplash R_LavaSplash
44 void R_CalcBeam_Vertex3f (float *vert, vec3_t org1, vec3_t org2, float width)
46 vec3_t right1, right2, diff, normal;
48 VectorSubtract (org2, org1, normal);
49 VectorNormalizeFast (normal);
51 // calculate 'right' vector for start
52 VectorSubtract (r_vieworigin, org1, diff);
53 VectorNormalizeFast (diff);
54 CrossProduct (normal, diff, right1);
56 // calculate 'right' vector for end
57 VectorSubtract (r_vieworigin, org2, diff);
58 VectorNormalizeFast (diff);
59 CrossProduct (normal, diff, right2);
61 vert[ 0] = org1[0] + width * right1[0];
62 vert[ 1] = org1[1] + width * right1[1];
63 vert[ 2] = org1[2] + width * right1[2];
64 vert[ 3] = org1[0] - width * right1[0];
65 vert[ 4] = org1[1] - width * right1[1];
66 vert[ 5] = org1[2] - width * right1[2];
67 vert[ 6] = org2[0] - width * right2[0];
68 vert[ 7] = org2[1] - width * right2[1];
69 vert[ 8] = org2[2] - width * right2[2];
70 vert[ 9] = org2[0] + width * right2[0];
71 vert[10] = org2[1] + width * right2[1];
72 vert[11] = org2[2] + width * right2[2];
74 void fractalnoise(qbyte *noise, int size, int startgrid)
76 int x, y, g, g2, amplitude, min, max, size1 = size - 1, sizepower, gridpower;
78 #define n(x,y) noisebuf[((y)&size1)*size+((x)&size1)]
80 for (sizepower = 0;(1 << sizepower) < size;sizepower++);
81 if (size != (1 << sizepower))
82 Sys_Error("fractalnoise: size must be power of 2\n");
84 for (gridpower = 0;(1 << gridpower) < startgrid;gridpower++);
85 if (startgrid != (1 << gridpower))
86 Sys_Error("fractalnoise: grid must be power of 2\n");
88 startgrid = bound(0, startgrid, size);
90 amplitude = 0xFFFF; // this gets halved before use
91 noisebuf = malloc(size*size*sizeof(int));
92 memset(noisebuf, 0, size*size*sizeof(int));
94 for (g2 = startgrid;g2;g2 >>= 1)
96 // brownian motion (at every smaller level there is random behavior)
98 for (y = 0;y < size;y += g2)
99 for (x = 0;x < size;x += g2)
100 n(x,y) += (rand()&litude);
105 // subdivide, diamond-square algorithm (really this has little to do with squares)
107 for (y = 0;y < size;y += g2)
108 for (x = 0;x < size;x += g2)
109 n(x+g,y+g) = (n(x,y) + n(x+g2,y) + n(x,y+g2) + n(x+g2,y+g2)) >> 2;
111 for (y = 0;y < size;y += g2)
112 for (x = 0;x < size;x += g2)
114 n(x+g,y) = (n(x,y) + n(x+g2,y) + n(x+g,y-g) + n(x+g,y+g)) >> 2;
115 n(x,y+g) = (n(x,y) + n(x,y+g2) + n(x-g,y+g) + n(x+g,y+g)) >> 2;
119 // find range of noise values
121 for (y = 0;y < size;y++)
122 for (x = 0;x < size;x++)
124 if (n(x,y) < min) min = n(x,y);
125 if (n(x,y) > max) max = n(x,y);
129 // normalize noise and copy to output
130 for (y = 0;y < size;y++)
131 for (x = 0;x < size;x++)
132 *noise++ = (qbyte) (((n(x,y) - min) * 256) / max);
136 void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
140 right[0] = forward[2];
141 right[1] = -forward[0];
142 right[2] = forward[1];
144 d = DotProduct(forward, right);
145 right[0] -= d * forward[0];
146 right[1] -= d * forward[1];
147 right[2] -= d * forward[2];
148 VectorNormalizeFast(right);
149 CrossProduct(right, forward, up);
153 extern qboolean PM_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace);
155 trace_t CL_TraceBox (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hitbmodels, int *hitent, int hitsupercontentsmask, qboolean hitplayers)
162 memset (&trace, 0, sizeof(trace));
164 VectorCopy (end, trace.endpos);
166 PM_RecursiveHullCheck (cl.model_precache[1]->hulls, 0, 0, 1, start, end, &trace);
168 RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace);
173 #include "cl_collision.h"
177 #define MAX_PARTICLES 32768 // default max # of particles at one time
178 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
182 PARTICLE_BILLBOARD = 0,
184 PARTICLE_ORIENTED_DOUBLESIDED = 2,
197 typedef struct particletype_s
200 porientation_t orientation;
207 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
211 // must match ptype_t values
212 particletype_t particletype[pt_total] =
214 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
215 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
216 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
217 {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
218 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
219 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
220 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
221 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
222 {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
223 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
224 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
225 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
228 typedef struct particle_s
230 particletype_t *type;
233 vec3_t vel; // velocity of particle, or orientation of decal, or end point of beam
235 float alpha; // 0-255
236 float alphafade; // how much alpha reduces per second
237 float time2; // used for snow fluttering and decal fade
238 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)
239 float gravity; // how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
240 float friction; // how much air friction affects this object (objects with a low mass/size ratio tend to get more air friction)
242 unsigned short owner; // decal stuck to this entity
243 model_t *ownermodel; // model the decal is stuck to (used to make sure the entity is still alive)
244 vec3_t relativeorigin; // decal at this location in entity's coordinate space
245 vec3_t relativedirection; // decal oriented this way relative to entity's coordinate space
249 static int particlepalette[256] =
251 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b,
252 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb,
253 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b,
254 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23,
255 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767,
256 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb,
257 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07,
258 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f,
259 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000,
260 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000,
261 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307,
262 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723,
263 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b,
264 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b,
265 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733,
266 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397,
267 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b,
268 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707,
269 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b,
270 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707,
271 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353,
272 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07,
273 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f,
274 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07,
275 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307,
276 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700,
277 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f,
278 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f,
279 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b,
280 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b,
281 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000,
282 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53
285 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
287 // texture numbers in particle font
288 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
289 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
290 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
291 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
292 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
293 static const int tex_particle = 63;
294 static const int tex_bubble = 62;
295 static const int tex_raindrop = 61;
296 static const int tex_beam = 60;
298 static int cl_maxparticles;
299 static int cl_numparticles;
300 static int cl_freeparticle;
301 static particle_t *particles;
303 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1"};
304 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1"};
305 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1"};
306 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1"};
307 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1"};
308 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5"};
309 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1"};
310 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1"};
311 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1"};
312 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0"};
313 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1"};
314 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0"};
315 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1"};
316 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5"};
317 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55"};
318 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1"};
319 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1"};
320 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0"};
321 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0"};
322 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20"};
324 void CL_Particles_Clear(void)
328 memset(particles, 0, sizeof(particle_t) * cl_maxparticles);
336 void CL_ReadPointFile_f (void);
337 void CL_Particles_Init (void)
341 // COMMANDLINEOPTION: Client: -particles <number> changes maximum number of particles at once, default 32768
342 i = COM_CheckParm ("-particles");
344 if (i && i < com_argc - 1)
346 cl_maxparticles = (int)(atoi(com_argv[i+1]));
347 if (cl_maxparticles < ABSOLUTE_MIN_PARTICLES)
348 cl_maxparticles = ABSOLUTE_MIN_PARTICLES;
351 cl_maxparticles = MAX_PARTICLES;
353 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f);
355 Cvar_RegisterVariable (&cl_particles);
356 Cvar_RegisterVariable (&cl_particles_quality);
357 Cvar_RegisterVariable (&cl_particles_size);
358 Cvar_RegisterVariable (&cl_particles_bloodshowers);
359 Cvar_RegisterVariable (&cl_particles_blood);
360 Cvar_RegisterVariable (&cl_particles_blood_alpha);
361 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
362 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
363 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
364 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
365 Cvar_RegisterVariable (&cl_particles_explosions_shell);
366 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
367 Cvar_RegisterVariable (&cl_particles_smoke);
368 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
369 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
370 Cvar_RegisterVariable (&cl_particles_sparks);
371 Cvar_RegisterVariable (&cl_particles_bubbles);
372 Cvar_RegisterVariable (&cl_decals);
373 Cvar_RegisterVariable (&cl_decals_time);
374 Cvar_RegisterVariable (&cl_decals_fadetime);
377 particles = (particle_t *) Hunk_AllocName(cl_maxparticles * sizeof(particle_t), "particles");
379 particles = (particle_t *) Mem_Alloc(cl_mempool, cl_maxparticles * sizeof(particle_t));
381 CL_Particles_Clear();
384 void CL_Particles_Shutdown (void)
387 // No clue what to do here...
391 // list of all 26 parameters:
392 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
393 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
394 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
395 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
396 // palpha - opacity of particle as 0-255 (can be more than 255)
397 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
398 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
399 // pgravity - how much effect gravity has on the particle (0-1)
400 // 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
401 // px,py,pz - starting origin of particle
402 // pvx,pvy,pvz - starting velocity of particle
403 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
404 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)
407 int ptempcolor, ptempcolor2, pcr1, pcg1, pcb1, pcr2, pcg2, pcb2;
408 ptempcolor = (pcolor1);
409 ptempcolor2 = (pcolor2);
410 pcr2 = ((ptempcolor2) >> 16) & 0xFF;
411 pcg2 = ((ptempcolor2) >> 8) & 0xFF;
412 pcb2 = (ptempcolor2) & 0xFF;
413 if (ptempcolor != ptempcolor2)
415 pcr1 = ((ptempcolor) >> 16) & 0xFF;
416 pcg1 = ((ptempcolor) >> 8) & 0xFF;
417 pcb1 = (ptempcolor) & 0xFF;
418 ptempcolor = rand() & 0xFF;
419 pcr2 = (((pcr2 - pcr1) * ptempcolor) >> 8) + pcr1;
420 pcg2 = (((pcg2 - pcg1) * ptempcolor) >> 8) + pcg1;
421 pcb2 = (((pcb2 - pcb1) * ptempcolor) >> 8) + pcb1;
423 for (;cl_freeparticle < cl_maxparticles && particles[cl_freeparticle].type;cl_freeparticle++);
424 if (cl_freeparticle >= cl_maxparticles)
426 part = &particles[cl_freeparticle++];
427 if (cl_numparticles < cl_freeparticle)
428 cl_numparticles = cl_freeparticle;
429 memset(part, 0, sizeof(*part));
430 part->type = (ptype);
431 part->color[0] = pcr2;
432 part->color[1] = pcg2;
433 part->color[2] = pcb2;
434 part->color[3] = 0xFF;
436 part->size = (psize);
437 part->alpha = (palpha);
438 part->alphafade = (palphafade);
439 part->gravity = (pgravity);
440 part->bounce = (pbounce);
444 part->vel[0] = (pvx);
445 part->vel[1] = (pvy);
446 part->vel[2] = (pvz);
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);
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)
505 float sp, sy, cp, cy;
509 static vec3_t avelocities[NUMVERTEXNORMALS];
510 if (!cl_particles.integer) return;
515 if (!avelocities[0][0])
516 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
517 avelocities[0][i] = (rand()&255) * 0.01;
519 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
521 angle = cl.time * avelocities[i][0];
524 angle = cl.time * avelocities[i][1];
533 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 0, 0, 0, ent->origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0);
535 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 2, 255, 0, 0, 0, ent->render.origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->render.origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->render.origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0, 0);
541 void CL_ReadPointFile_f (void)
545 char *pointfile = NULL, *pointfilepos, *t, tchar;
546 char name[MAX_OSPATH];
551 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
552 strlcat (name, ".pts", sizeof (name));
554 pointfile = COM_LoadTempFile (name);
556 pointfile = FS_LoadFile(name, tempmempool, true);
560 Con_Printf("Could not open %s\n", name);
564 Con_Printf("Reading %s...\n", name);
567 pointfilepos = pointfile;
568 while (*pointfilepos)
570 while (*pointfilepos == '\n' || *pointfilepos == '\r')
575 while (*t && *t != '\n' && *t != '\r')
579 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
585 VectorCopy(org, leakorg);
588 if (cl_numparticles < cl_maxparticles - 3)
591 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);
594 #ifndef WORKINGLQUAKE
597 VectorCopy(leakorg, org);
598 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
600 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);
601 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);
602 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);
607 CL_ParseParticleEffect
609 Parse an effect out of the server message
612 void CL_ParseParticleEffect (void)
615 int i, count, msgcount, color;
617 MSG_ReadVector(org, cl.protocol);
618 for (i=0 ; i<3 ; i++)
619 dir[i] = MSG_ReadChar () * (1.0/16);
620 msgcount = MSG_ReadByte ();
621 color = MSG_ReadByte ();
628 if (cl_particles_blood_bloodhack.integer)
633 CL_BloodPuff(org, dir, count / 2);
639 CL_BloodPuff(org, dir, count / 2);
643 CL_RunParticleEffect (org, dir, color, count);
652 void CL_ParticleExplosion (vec3_t org)
658 if (cl_stainmaps.integer)
659 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
660 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
662 i = CL_PointSuperContents(org);
663 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
665 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
666 for (i = 0;i < 128 * cl_particles_quality.value;i++)
667 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, (1.0f / cl_particles_quality.value) * lhrandom(128, 255), (1.0f / cl_particles_quality.value) * 128, -0.125, 1.5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-96, 96), lhrandom(-96, 96), lhrandom(-96, 96), (1.0 / 16.0));
671 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
673 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
675 for (i = 0;i < 32;i++)
680 v2[0] = lhrandom(-48, 48);
681 v2[1] = lhrandom(-48, 48);
682 v2[2] = lhrandom(-48, 48);
684 for (k = 0;k < 16;k++)
686 v[0] = org[0] + lhrandom(-48, 48);
687 v[1] = org[1] + lhrandom(-48, 48);
688 v[2] = org[2] + lhrandom(-48, 48);
689 trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
690 if (trace.fraction >= 0.1)
693 VectorSubtract(trace.endpos, org, v2);
695 VectorScale(v2, 2.0f, v2);
696 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);
700 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
701 for (i = 0;i < 128 * cl_particles_quality.value;i++)
702 particle(particletype + pt_spark, 0x903010, 0xFFD030, tex_particle, 1.0f, (1.0f / cl_particles_quality.value) * lhrandom(0, 255), (1.0f / cl_particles_quality.value) * 512, 1, 0, org[0], org[1], org[2], lhrandom(-256, 256), lhrandom(-256, 256), lhrandom(-256, 256) + 80, 0.2);
705 if (cl_particles_explosions_shell.integer)
711 CL_ParticleExplosion2
715 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
721 if (!cl_particles.integer) return;
723 for (i = 0;i < 512 * cl_particles_quality.value;i++)
725 VectorRandom (offset);
726 VectorScale (offset, 192, vel);
727 VectorScale (offset, 8, offset);
728 k = particlepalette[colorStart + (i % colorLength)];
729 pscale = lhrandom(0.5, 1.5);
730 particle(particletype + pt_static, k, k, tex_particle, pscale, (1.0f / cl_particles_quality.value) * 255, (1.0f/cl_particles_quality.value)*512, 0, 0, org[0] + offset[0], org[1] + offset[1], org[2] + offset[2], vel[0], vel[1], vel[2], lhrandom(1.5, 3));
740 void CL_BlobExplosion (vec3_t org)
742 CL_ParticleExplosion(org);
751 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
757 CL_ParticleExplosion(org);
760 if (!cl_particles.integer) return;
761 count *= cl_particles_quality.value;
764 k = particlepalette[color + (rand()&7)];
765 if (gamemode == GAME_GOODVSBAD2)
766 particle(particletype + pt_alphastatic, k, k, tex_particle, 5, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 300, 0, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-10, 10), lhrandom(-10, 10), lhrandom(-10, 10), 0);
768 particle(particletype + pt_alphastatic, k, k, tex_particle, 1, (1.0f / cl_particles_quality.value) * 255, (1.0f / cl_particles_quality.value) * 512, 0, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), dir[0] + lhrandom(-15, 15), dir[1] + lhrandom(-15, 15), dir[2] + lhrandom(-15, 15), 0);
772 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
778 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale)
782 if (!cl_particles.integer) return;
784 if (cl_particles_sparks.integer)
787 count *= cl_particles_quality.value;
790 k = particlepalette[0x68 + (rand() & 7)];
791 particle(particletype + pt_spark, k, k, tex_particle, 0.4f, (1.0f / cl_particles_quality.value) * lhrandom(64, 255), (1.0f / cl_particles_quality.value) * 512, gravityscale, 0, org[0], org[1], org[2], lhrandom(-64, 64) + dir[0], lhrandom(-64, 64) + dir[1], lhrandom(0, 128) + dir[2], 0);
796 void CL_Smoke (vec3_t org, vec3_t dir, int count)
802 if (!cl_particles.integer) return;
805 if (cl_particles_smoke.integer)
807 k = count * 0.25 * cl_particles_quality.value;
810 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
811 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
812 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
813 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
814 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], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
819 void CL_BulletMark (vec3_t org)
821 if (cl_stainmaps.integer)
822 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
823 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
826 void CL_PlasmaBurn (vec3_t org)
828 if (cl_stainmaps.integer)
829 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
830 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
833 static float bloodcount = 0;
834 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
839 // bloodcount is used to accumulate counts too small to cause a blood particle
840 if (!cl_particles.integer) return;
841 if (!cl_particles_blood.integer) return;
848 while(bloodcount > 0)
850 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
851 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
852 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
853 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
854 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] + lhrandom(-s, s), vel[1] + lhrandom(-s, s), vel[2] + lhrandom(-s, s), 1);
855 bloodcount -= 16 / cl_particles_quality.value;
859 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
861 vec3_t org, vel, diff, center, velscale;
862 if (!cl_particles.integer) return;
863 if (!cl_particles_bloodshowers.integer) return;
864 if (!cl_particles_blood.integer) return;
866 VectorSubtract(maxs, mins, diff);
867 center[0] = (mins[0] + maxs[0]) * 0.5;
868 center[1] = (mins[1] + maxs[1]) * 0.5;
869 center[2] = (mins[2] + maxs[2]) * 0.5;
870 velscale[0] = velspeed * 2.0 / diff[0];
871 velscale[1] = velspeed * 2.0 / diff[1];
872 velscale[2] = velspeed * 2.0 / diff[2];
874 bloodcount += count * 5.0f;
875 while (bloodcount > 0)
877 org[0] = lhrandom(mins[0], maxs[0]);
878 org[1] = lhrandom(mins[1], maxs[1]);
879 org[2] = lhrandom(mins[2], maxs[2]);
880 vel[0] = (org[0] - center[0]) * velscale[0];
881 vel[1] = (org[1] - center[1]) * velscale[1];
882 vel[2] = (org[2] - center[2]) * velscale[2];
883 bloodcount -= 16 / cl_particles_quality.value;
884 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);
888 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
892 if (!cl_particles.integer) return;
893 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
894 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
895 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
897 count *= cl_particles_quality.value;
900 k = particlepalette[colorbase + (rand()&3)];
901 particle(particletype + pt_alphastatic, k, k, tex_particle, 2, 255 / cl_particles_quality.value, (255 / cl_particles_quality.value) / 2, gravity ? 1 : 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0] + lhrandom(-randomvel, randomvel), dir[1] + lhrandom(-randomvel, randomvel), dir[2] + lhrandom(-randomvel, randomvel), 0);
905 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
908 float t, z, minz, maxz;
910 if (!cl_particles.integer) return;
911 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
912 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
913 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
914 if (dir[2] < 0) // falling
919 minz = z - fabs(dir[2]) * 0.1;
920 maxz = z + fabs(dir[2]) * 0.1;
921 minz = bound(mins[2], minz, maxs[2]);
922 maxz = bound(mins[2], maxz, maxs[2]);
924 count *= cl_particles_quality.value;
929 count *= 4; // ick, this should be in the mod or maps?
933 k = particlepalette[colorbase + (rand()&3)];
934 if (gamemode == GAME_GOODVSBAD2)
935 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);
937 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);
943 k = particlepalette[colorbase + (rand()&3)];
944 if (gamemode == GAME_GOODVSBAD2)
945 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);
947 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);
949 VectorCopy(p->vel, p->relativedirection);
953 Host_Error("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
957 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
962 if (!cl_particles.integer) return;
964 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
965 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
966 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
968 center[0] = (mins[0] + maxs[0]) * 0.5f;
969 center[1] = (mins[1] + maxs[1]) * 0.5f;
970 center[2] = (mins[2] + maxs[2]) * 0.5f;
972 count *= cl_particles_quality.value;
975 k = particlepalette[224 + (rand()&15)];
976 o[0] = lhrandom(mins[0], maxs[0]);
977 o[1] = lhrandom(mins[1], maxs[1]);
978 o[2] = lhrandom(mins[2], maxs[2]);
979 VectorSubtract(o, center, v);
980 VectorNormalizeFast(v);
981 VectorScale(v, 100, v);
982 v[2] += sv_gravity.value * 0.15f;
983 particle(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);
987 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
991 if (!cl_particles.integer) return;
992 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
993 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
994 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
996 count *= cl_particles_quality.value;
999 k = particlepalette[224 + (rand()&15)];
1000 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(0, 64), 1);
1002 particle(particletype + pt_static, 0x303030, 0x606060, tex_smoke[rand()&7], 6, lhrandom(48, 96) / cl_particles_quality.value, 64 / cl_particles_quality.value, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 32), 0);
1006 void CL_Flames (vec3_t org, vec3_t vel, int count)
1009 if (!cl_particles.integer) return;
1011 count *= cl_particles_quality.value;
1014 k = particlepalette[224 + (rand()&15)];
1015 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128) / cl_particles_quality.value, 384 / cl_particles_quality.value, -1, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128), 1);
1027 void CL_LavaSplash (vec3_t origin)
1029 float i, j, inc, vel;
1032 if (!cl_particles.integer) return;
1034 inc = 32 / cl_particles_quality.value;
1035 for (i = -128;i < 128;i += inc)
1037 for (j = -128;j < 128;j += inc)
1039 dir[0] = j + lhrandom(0, 8);
1040 dir[1] = i + lhrandom(0, 8);
1042 org[0] = origin[0] + dir[0];
1043 org[1] = origin[1] + dir[1];
1044 org[2] = origin[2] + lhrandom(0, 64);
1045 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1046 if (gamemode == GAME_GOODVSBAD2)
1048 k = particlepalette[0 + (rand()&255)];
1049 l = particlepalette[0 + (rand()&255)];
1050 particle(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);
1054 k = l = particlepalette[224 + (rand()&7)];
1055 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);
1067 void CL_TeleportSplash (vec3_t org)
1070 if (!cl_particles.integer) return;
1072 inc = 8 / cl_particles_quality.value;
1073 for (i = -16;i < 16;i += inc)
1074 for (j = -16;j < 16;j += inc)
1075 for (k = -24;k < 32;k += inc)
1076 particle(particletype + pt_static, 0xA0A0A0, 0xFFFFFF, tex_particle, 10, inc * lhrandom(8, 16), inc * 32, 0, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-256, 256), 1);
1079 #ifdef WORKINGLQUAKE
1080 void R_RocketTrail (vec3_t start, vec3_t end, int type)
1082 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
1085 vec3_t vec, dir, vel, pos;
1086 float len, dec, speed, qd;
1087 int smoke, blood, bubbles;
1088 #ifdef WORKINGLQUAKE
1092 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
1095 VectorSubtract(end, start, dir);
1096 VectorNormalize(dir);
1098 VectorSubtract (end, start, vec);
1099 #ifdef WORKINGLQUAKE
1100 len = VectorNormalize (vec);
1102 speed = 1.0f / cl.frametime;
1103 VectorSubtract(end, start, vel);
1105 len = VectorNormalizeLength (vec);
1106 dec = -ent->persistent.trail_time;
1107 ent->persistent.trail_time += len;
1108 if (ent->persistent.trail_time < 0.01f)
1111 // if we skip out, leave it reset
1112 ent->persistent.trail_time = 0.0f;
1114 speed = ent->state_current.time - ent->state_previous.time;
1116 speed = 1.0f / speed;
1117 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
1118 color = particlepalette[color];
1120 VectorScale(vel, speed, vel);
1122 // advance into this frame to reach the first puff location
1123 VectorMA(start, dec, vec, pos);
1126 smoke = cl_particles.integer && cl_particles_smoke.integer;
1127 blood = cl_particles.integer && cl_particles_blood.integer;
1128 #ifdef WORKINGLQUAKE
1129 contents = CL_PointQ1Contents(pos);
1130 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
1132 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1134 qd = 1.0f / cl_particles_quality.value;
1140 case 0: // rocket trail
1144 particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*125, qd*cl_particles_smoke_alphafade.value*125, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1145 particle(particletype + pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*288, qd*cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], lhrandom(-20, 20), lhrandom(-20, 20), lhrandom(-20, 20), 0);
1148 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, qd*lhrandom(64, 255), qd*256, -0.25, 1.5, pos[0], pos[1], pos[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16), (1.0 / 16.0));
1151 case 1: // grenade trail
1152 // FIXME: make it gradually stop smoking
1155 particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, qd*cl_particles_smoke_alpha.value*100, qd*cl_particles_smoke_alphafade.value*100, 0, 0, pos[0], pos[1], pos[2], lhrandom(-5, 5), lhrandom(-5, 5), lhrandom(-5, 5), 0);
1160 case 4: // slight blood
1163 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], vel[0] * 0.5f + lhrandom(-64, 64), vel[1] * 0.5f + lhrandom(-64, 64), vel[2] * 0.5f + lhrandom(-64, 64), 1);
1166 case 3: // green tracer
1170 if (gamemode == GAME_GOODVSBAD2)
1171 particle(particletype + pt_static, 0x00002E, 0x000030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1173 particle(particletype + pt_static, 0x002000, 0x003000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1177 case 5: // flame tracer
1180 particle(particletype + pt_static, 0x301000, 0x502000, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1183 case 6: // voor trail
1187 if (gamemode == GAME_GOODVSBAD2)
1188 particle(particletype + pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, qd*255, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1189 else if (gamemode == GAME_PRYDON)
1190 particle(particletype + pt_static, 0x103040, 0x204050, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1192 particle(particletype + pt_static, 0x502030, 0x502030, tex_particle, 6, qd*128, qd*384, 0, 0, pos[0], pos[1], pos[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(-8, 8), 0);
1195 #ifndef WORKINGLQUAKE
1196 case 7: // Nehahra smoke tracer
1199 particle(particletype + pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, qd*64, qd*320, 0, 0, pos[0], pos[1], pos[2], lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(0, 16), 0);
1201 case 8: // Nexuiz plasma trail
1204 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);
1206 case 9: // glow trail
1209 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);
1214 // advance to next time and position
1216 VectorMA (pos, dec, vec, pos);
1218 #ifndef WORKINGLQUAKE
1219 ent->persistent.trail_time = len;
1223 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1225 int tempcolor2, cr, cg, cb;
1229 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1230 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);
1233 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1236 if (!cl_particles.integer) return;
1239 if (cl_particles_smoke.integer)
1240 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1241 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] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count) * 0.5f, dir[1] + lhrandom(-count, count) * 0.5f, dir[2] + lhrandom(-count, count) * 0.5f, 0);
1244 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1247 if (!cl_particles.integer) return;
1249 if (cl_stainmaps.integer)
1250 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1251 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1254 if (cl_particles_smoke.integer)
1255 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1256 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] + 0.125f * lhrandom(-count, count), org[1] + 0.125f * lhrandom (-count, count), org[2] + 0.125f * lhrandom(-count, count), dir[0] + lhrandom(-count, count), dir[1] + lhrandom(-count, count), dir[2] + lhrandom(-count, count), 0);
1259 if (cl_particles_sparks.integer)
1260 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1261 particle(particletype + pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, lhrandom(64, 255) / cl_particles_quality.value, 512 / cl_particles_quality.value, 0, 0, org[0], org[1], org[2], lhrandom(-count, count) * 3.0f + dir[0], lhrandom(-count, count) * 3.0f + dir[1], lhrandom(-count, count) * 3.0f + dir[2], 0);
1269 void CL_MoveParticles (void)
1272 int i, maxparticle, j, a, content;
1273 float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
1277 // LordHavoc: early out condition
1278 if (!cl_numparticles)
1280 cl_freeparticle = 0;
1284 #ifdef WORKINGLQUAKE
1285 frametime = cl.frametime;
1287 frametime = cl.time - cl.oldtime;
1289 gravity = frametime * sv_gravity.value;
1290 dvel = 1+4*frametime;
1291 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1295 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
1302 p->alpha -= p->alphafade * frametime;
1310 if (p->type->orientation != PARTICLE_BEAM)
1312 VectorCopy(p->org, oldorg);
1313 VectorMA(p->org, frametime, p->vel, p->org);
1314 VectorCopy(p->org, org);
1317 if (p->type == particletype + pt_rain)
1319 // raindrop - splash on solid/water/slime/lava
1320 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
1321 if (trace.fraction < 1)
1323 // convert from a raindrop particle to a rainsplash decal
1324 VectorCopy(trace.endpos, p->org);
1325 VectorCopy(trace.plane.normal, p->vel);
1326 VectorAdd(p->org, p->vel, p->org);
1327 p->type = particletype + pt_raindecal;
1328 p->texnum = tex_rainsplash[0];
1330 p->alphafade = p->alpha / 0.4;
1337 else if (p->type == particletype + pt_blood)
1339 // blood - splash on solid
1340 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
1341 if (trace.fraction < 1)
1343 // convert from a blood particle to a blood decal
1344 VectorCopy(trace.endpos, p->org);
1345 VectorCopy(trace.plane.normal, p->vel);
1346 VectorAdd(p->org, p->vel, p->org);
1347 #ifndef WORKINGLQUAKE
1348 if (cl_stainmaps.integer)
1349 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));
1351 if (!cl_decals.integer)
1357 p->type = particletype + pt_decal;
1358 p->texnum = tex_blooddecal[rand()&7];
1359 #ifndef WORKINGLQUAKE
1361 p->ownermodel = cl_entities[hitent].render.model;
1362 Matrix4x4_Transform(&cl_entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1363 Matrix4x4_Transform3x3(&cl_entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1375 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
1376 if (trace.fraction < 1)
1378 VectorCopy(trace.endpos, p->org);
1386 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1387 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1388 if (DotProduct(p->vel, p->vel) < 0.03)
1389 VectorClear(p->vel);
1394 p->vel[2] -= p->gravity * gravity;
1398 f = p->friction * frametime;
1399 #ifdef WORKINGLQUAKE
1400 if (CL_PointQ1Contents(p->org) != CONTENTS_EMPTY)
1402 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1406 VectorScale(p->vel, f, p->vel);
1410 if (p->type != particletype + pt_static)
1412 switch (p->type - particletype)
1414 case pt_entityparticle:
1415 // particle that removes itself after one rendered frame
1422 #ifdef WORKINGLQUAKE
1423 a = CL_PointQ1Contents(p->org);
1424 if (a <= CONTENTS_WATER)
1426 a = CL_PointSuperContents(p->org);
1427 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1430 p->size += frametime * 8;
1431 //p->alpha -= bloodwaterfade;
1434 p->vel[2] -= gravity;
1435 #ifdef WORKINGLQUAKE
1436 if (a == CONTENTS_SOLID || a == CONTENTS_LAVA)
1438 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1443 #ifdef WORKINGLQUAKE
1444 a = CL_PointQ1Contents(p->org);
1445 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1447 a = CL_PointSuperContents(p->org);
1448 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1456 #ifdef WORKINGLQUAKE
1457 a = CL_PointQ1Contents(p->org);
1458 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1460 a = CL_PointSuperContents(p->org);
1461 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1466 if (cl.time > p->time2)
1469 p->time2 = cl.time + (rand() & 3) * 0.1;
1470 p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
1471 p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1472 //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
1474 #ifdef WORKINGLQUAKE
1475 a = CL_PointQ1Contents(p->org);
1476 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1478 a = CL_PointSuperContents(p->org);
1479 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1484 //p->size += frametime * 15;
1487 // FIXME: this has fairly wacky handling of alpha
1488 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1489 #ifndef WORKINGLQUAKE
1490 if (cl_entities[p->owner].render.model == p->ownermodel)
1492 Matrix4x4_Transform(&cl_entities[p->owner].render.matrix, p->relativeorigin, p->org);
1493 Matrix4x4_Transform3x3(&cl_entities[p->owner].render.matrix, p->relativedirection, p->vel);
1500 a = max(0, (cl.time - p->time2) * 40);
1502 p->texnum = tex_rainsplash[a];
1511 cl_numparticles = maxparticle + 1;
1512 cl_freeparticle = 0;
1515 #define MAX_PARTICLETEXTURES 64
1516 // particletexture_t is a rectangle in the particlefonttexture
1519 rtexture_t *texture;
1520 float s1, t1, s2, t2;
1525 static int particlefonttexture;
1527 static rtexturepool_t *particletexturepool;
1528 static rtexture_t *particlefonttexture;
1530 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1532 static cvar_t r_drawparticles = {0, "r_drawparticles", "1"};
1534 #define PARTICLETEXTURESIZE 64
1535 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1537 static qbyte shadebubble(float dx, float dy, vec3_t light)
1541 dz = 1 - (dx*dx+dy*dy);
1542 if (dz > 0) // it does hit the sphere
1546 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1547 VectorNormalize(normal);
1548 dot = DotProduct(normal, light);
1549 if (dot > 0.5) // interior reflection
1550 f += ((dot * 2) - 1);
1551 else if (dot < -0.5) // exterior reflection
1552 f += ((dot * -2) - 1);
1554 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1555 VectorNormalize(normal);
1556 dot = DotProduct(normal, light);
1557 if (dot > 0.5) // interior reflection
1558 f += ((dot * 2) - 1);
1559 else if (dot < -0.5) // exterior reflection
1560 f += ((dot * -2) - 1);
1562 f += 16; // just to give it a haze so you can see the outline
1563 f = bound(0, f, 255);
1570 static void setuptex(int texnum, qbyte *data, qbyte *particletexturedata)
1572 int basex, basey, y;
1573 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1574 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1575 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1576 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1577 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1578 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1579 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1580 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1583 void particletextureblotch(qbyte *data, float radius, float red, float green, float blue, float alpha)
1586 float cx, cy, dx, dy, f, iradius;
1588 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1589 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1590 iradius = 1.0f / radius;
1591 alpha *= (1.0f / 255.0f);
1592 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1594 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1598 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1601 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1602 d[0] += f * (red - d[0]);
1603 d[1] += f * (green - d[1]);
1604 d[2] += f * (blue - d[2]);
1610 void particletextureclamp(qbyte *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1613 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1615 data[0] = bound(minr, data[0], maxr);
1616 data[1] = bound(ming, data[1], maxg);
1617 data[2] = bound(minb, data[2], maxb);
1621 void particletextureinvert(qbyte *data)
1624 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1626 data[0] = 255 - data[0];
1627 data[1] = 255 - data[1];
1628 data[2] = 255 - data[2];
1632 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1633 static void R_InitBloodTextures (qbyte *particletexturedata)
1636 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1639 for (i = 0;i < 8;i++)
1641 memset(&data[0][0][0], 255, sizeof(data));
1642 for (k = 0;k < 24;k++)
1643 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1644 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1645 particletextureinvert(&data[0][0][0]);
1646 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1650 for (i = 0;i < 8;i++)
1652 memset(&data[0][0][0], 255, sizeof(data));
1654 for (j = 1;j < 10;j++)
1655 for (k = min(j, m - 1);k < m;k++)
1656 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1657 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1658 particletextureinvert(&data[0][0][0]);
1659 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1664 static void R_InitParticleTexture (void)
1666 int x, y, d, i, k, m;
1667 float dx, dy, radius, f, f2;
1668 qbyte data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1670 qbyte *particletexturedata;
1672 // a note: decals need to modulate (multiply) the background color to
1673 // properly darken it (stain), and they need to be able to alpha fade,
1674 // this is a very difficult challenge because it means fading to white
1675 // (no change to background) rather than black (darkening everything
1676 // behind the whole decal polygon), and to accomplish this the texture is
1677 // inverted (dark red blood on white background becomes brilliant cyan
1678 // and white on black background) so we can alpha fade it to black, then
1679 // we invert it again during the blendfunc to make it work...
1681 particletexturedata = Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1682 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1685 for (i = 0;i < 8;i++)
1687 memset(&data[0][0][0], 255, sizeof(data));
1690 qbyte noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1692 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1693 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1695 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1697 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1698 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1700 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1701 d = (noise2[y][x] - 128) * 3 + 192;
1703 d = d * (1-(dx*dx+dy*dy));
1704 d = (d * noise1[y][x]) >> 7;
1705 d = bound(0, d, 255);
1706 data[y][x][3] = (qbyte) d;
1713 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1717 for (i = 0;i < 16;i++)
1719 memset(&data[0][0][0], 255, sizeof(data));
1720 radius = i * 3.0f / 4.0f / 16.0f;
1721 f2 = 255.0f * ((15.0f - i) / 15.0f);
1722 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1724 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1725 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1727 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1728 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1729 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1732 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1736 memset(&data[0][0][0], 255, sizeof(data));
1737 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1739 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1740 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1742 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1743 d = 256 * (1 - (dx*dx+dy*dy));
1744 d = bound(0, d, 255);
1745 data[y][x][3] = (qbyte) d;
1748 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1751 memset(&data[0][0][0], 255, sizeof(data));
1752 light[0] = 1;light[1] = 1;light[2] = 1;
1753 VectorNormalize(light);
1754 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1756 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1757 // stretch upper half of bubble by +50% and shrink lower half by -50%
1758 // (this gives an elongated teardrop shape)
1760 dy = (dy - 0.5f) * 2.0f;
1762 dy = (dy - 0.5f) / 1.5f;
1763 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1765 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1766 // shrink bubble width to half
1768 data[y][x][3] = shadebubble(dx, dy, light);
1771 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1774 memset(&data[0][0][0], 255, sizeof(data));
1775 light[0] = 1;light[1] = 1;light[2] = 1;
1776 VectorNormalize(light);
1777 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1779 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1780 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1782 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1783 data[y][x][3] = shadebubble(dx, dy, light);
1786 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1788 // Blood particles and blood decals
1789 R_InitBloodTextures (particletexturedata);
1792 for (i = 0;i < 8;i++)
1794 memset(&data[0][0][0], 255, sizeof(data));
1795 for (k = 0;k < 12;k++)
1796 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1797 for (k = 0;k < 3;k++)
1798 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1799 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1800 particletextureinvert(&data[0][0][0]);
1801 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1805 glBindTexture(GL_TEXTURE_2D, (particlefonttexture = gl_extension_number++));
1806 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1807 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1811 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1814 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1815 if (!particlefonttexture)
1816 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1817 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1818 particletexture[i].texture = particlefonttexture;
1821 fractalnoise(&noise3[0][0], 64, 4);
1823 for (y = 0;y < 64;y++)
1825 dy = (y - 0.5f*64) / (64*0.5f-1);
1826 for (x = 0;x < 16;x++)
1828 dx = (x - 0.5f*16) / (16*0.5f-2);
1829 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1830 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (qbyte) bound(0, d, 255);
1831 data2[y][x][3] = 255;
1836 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1839 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1840 if (!particletexture[tex_beam].texture)
1841 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1842 particletexture[tex_beam].s1 = 0;
1843 particletexture[tex_beam].t1 = 0;
1844 particletexture[tex_beam].s2 = 1;
1845 particletexture[tex_beam].t2 = 1;
1847 Mem_Free(particletexturedata);
1850 static void r_part_start(void)
1852 particletexturepool = R_AllocTexturePool();
1853 R_InitParticleTexture ();
1856 static void r_part_shutdown(void)
1858 R_FreeTexturePool(&particletexturepool);
1861 static void r_part_newmap(void)
1863 cl_numparticles = 0;
1864 cl_freeparticle = 0;
1867 void R_Particles_Init (void)
1869 Cvar_RegisterVariable(&r_drawparticles);
1870 #ifdef WORKINGLQUAKE
1873 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1877 #ifdef WORKINGLQUAKE
1878 void R_InitParticles(void)
1880 CL_Particles_Init();
1885 float particle_vertex3f[12], particle_texcoord2f[8];
1887 #ifdef WORKINGLQUAKE
1888 void R_DrawParticle(particle_t *p)
1891 void R_DrawParticleCallback(const void *calldata1, int calldata2)
1893 const particle_t *p = calldata1;
1897 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, fogvec[3], cr, cg, cb, ca, size;
1898 particletexture_t *tex;
1900 VectorCopy(p->org, org);
1902 blendmode = p->type->blendmode;
1903 tex = &particletexture[p->texnum];
1904 cr = p->color[0] * (1.0f / 255.0f);
1905 cg = p->color[1] * (1.0f / 255.0f);
1906 cb = p->color[2] * (1.0f / 255.0f);
1907 ca = p->alpha * (1.0f / 255.0f);
1908 if (blendmode == PBLEND_MOD)
1918 #ifndef WORKINGLQUAKE
1919 if (p->type->lighting)
1921 float ambient[3], diffuse[3], diffusenormal[3];
1922 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
1923 cr *= (ambient[0] + 0.5 * diffuse[0]);
1924 cg *= (ambient[1] + 0.5 * diffuse[1]);
1925 cb *= (ambient[2] + 0.5 * diffuse[2]);
1929 VectorSubtract(org, r_vieworigin, fogvec);
1930 fog = exp(fogdensity/DotProduct(fogvec,fogvec));
1935 if (blendmode == PBLEND_ALPHA)
1937 cr += fogcolor[0] * fog;
1938 cg += fogcolor[1] * fog;
1939 cb += fogcolor[2] * fog;
1943 R_Mesh_Matrix(&r_identitymatrix);
1945 memset(&m, 0, sizeof(m));
1946 m.tex[0] = R_GetTexture(tex->texture);
1947 m.pointer_texcoord[0] = particle_texcoord2f;
1948 m.pointer_vertex = particle_vertex3f;
1951 GL_Color(cr, cg, cb, ca);
1953 if (blendmode == PBLEND_ALPHA)
1954 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1955 else if (blendmode == PBLEND_ADD)
1956 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1957 else //if (blendmode == PBLEND_MOD)
1958 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1959 GL_DepthMask(false);
1962 size = p->size * cl_particles_size.value;
1963 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1965 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1968 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1970 VectorNegate(p->vel, v);
1971 VectorVectors(v, right, up);
1974 VectorVectors(p->vel, right, up);
1975 VectorScale(right, size, right);
1976 VectorScale(up, size, up);
1980 VectorScale(r_viewleft, -size, right);
1981 VectorScale(r_viewup, size, up);
1983 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1984 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1985 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1986 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1987 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1988 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1989 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1990 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1991 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1992 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1993 particle_vertex3f[10] = org[1] + right[1] - up[1];
1994 particle_vertex3f[11] = org[2] + right[2] - up[2];
1995 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1996 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1997 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1998 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2000 else if (p->type->orientation == PARTICLE_SPARK)
2002 VectorMA(p->org, -0.02, p->vel, v);
2003 VectorMA(p->org, 0.02, p->vel, up2);
2004 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
2005 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
2006 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
2007 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
2008 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
2010 else if (p->type->orientation == PARTICLE_BEAM)
2012 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
2013 VectorSubtract(p->vel, p->org, up);
2014 VectorNormalizeFast(up);
2015 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2016 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2017 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
2018 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
2019 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
2020 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
2023 Host_Error("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
2026 if (blendmode == PBLEND_ALPHA)
2027 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2028 else if (blendmode == PBLEND_ADD)
2029 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
2030 else //if (blendmode == PBLEND_MOD)
2031 glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2032 glColor4f(cr, cg, cb, ca);
2034 glTexCoord2f(particle_texcoord2f[0], particle_texcoord2f[1]);glVertex3f(particle_vertex3f[ 0], particle_vertex3f[ 1], particle_vertex3f[ 2]);
2035 glTexCoord2f(particle_texcoord2f[2], particle_texcoord2f[3]);glVertex3f(particle_vertex3f[ 3], particle_vertex3f[ 4], particle_vertex3f[ 5]);
2036 glTexCoord2f(particle_texcoord2f[4], particle_texcoord2f[5]);glVertex3f(particle_vertex3f[ 6], particle_vertex3f[ 7], particle_vertex3f[ 8]);
2037 glTexCoord2f(particle_texcoord2f[6], particle_texcoord2f[7]);glVertex3f(particle_vertex3f[ 9], particle_vertex3f[10], particle_vertex3f[11]);
2040 R_Mesh_Draw(0, 4, 2, polygonelements);
2044 void R_DrawParticles (void)
2047 float minparticledist;
2050 #ifdef WORKINGLQUAKE
2054 // LordHavoc: early out conditions
2055 if ((!cl_numparticles) || (!r_drawparticles.integer))
2058 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
2060 #ifdef WORKINGLQUAKE
2061 glBindTexture(GL_TEXTURE_2D, particlefonttexture);
2063 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
2065 // LordHavoc: only render if not too close
2066 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2067 if (p->type && DotProduct(p->org, r_viewforward) >= minparticledist)
2070 glDisable(GL_BLEND);
2071 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2073 // LordHavoc: only render if not too close
2074 for (i = 0, p = particles;i < cl_numparticles;i++, p++)
2079 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
2081 if (p->type == particletype + pt_decal)
2082 R_DrawParticleCallback(p, 0);
2084 R_MeshQueue_AddTransparent(p->org, R_DrawParticleCallback, p, 0);