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.
23 #include "cl_collision.h"
27 #define ABSOLUTE_MAX_PARTICLES 1<<24 // upper limit on cl.max_particles
28 #define ABSOLUTE_MAX_DECALS 1<<24 // upper limit on cl.max_decals
30 // must match ptype_t values
31 particletype_t particletype[pt_total] =
33 {0, 0, false}, // pt_dead
34 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
35 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
36 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
37 {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
38 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
39 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
40 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
41 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
42 {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
43 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
44 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
45 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
48 #define PARTICLEEFFECT_UNDERWATER 1
49 #define PARTICLEEFFECT_NOTUNDERWATER 2
51 typedef struct particleeffectinfo_s
53 int effectnameindex; // which effect this belongs to
54 // PARTICLEEFFECT_* bits
56 // blood effects may spawn very few particles, so proper fraction-overflow
57 // handling is very important, this variable keeps track of the fraction
58 double particleaccumulator;
59 // the math is: countabsolute + requestedcount * countmultiplier * quality
60 // absolute number of particles to spawn, often used for decals
61 // (unaffected by quality and requestedcount)
63 // multiplier for the number of particles CL_ParticleEffect was told to
64 // spawn, most effects do not really have a count and hence use 1, so
65 // this is often the actual count to spawn, not merely a multiplier
66 float countmultiplier;
67 // if > 0 this causes the particle to spawn in an evenly spaced line from
68 // originmins to originmaxs (causing them to describe a trail, not a box)
70 // type of particle to spawn (defines some aspects of behavior)
72 // range of colors to choose from in hex RRGGBB (like HTML color tags),
73 // randomly interpolated at spawn
74 unsigned int color[2];
75 // a random texture is chosen in this range (note the second value is one
76 // past the last choosable, so for example 8,16 chooses any from 8 up and
78 // if start and end of the range are the same, no randomization is done
80 // range of size values randomly chosen when spawning, plus size increase over time
82 // range of alpha values randomly chosen when spawning, plus alpha fade
84 // how long the particle should live (note it is also removed if alpha drops to 0)
86 // how much gravity affects this particle (negative makes it fly up!)
88 // how much bounce the particle has when it hits a surface
89 // if negative the particle is removed on impact
91 // if in air this friction is applied
92 // if negative the particle accelerates
94 // if in liquid (water/slime/lava) this friction is applied
95 // if negative the particle accelerates
97 // these offsets are added to the values given to particleeffect(), and
98 // then an ellipsoid-shaped jitter is added as defined by these
99 // (they are the 3 radii)
100 float originoffset[3];
101 float velocityoffset[3];
102 float originjitter[3];
103 float velocityjitter[3];
104 float velocitymultiplier;
105 // an effect can also spawn a dlight
106 float lightradiusstart;
107 float lightradiusfade;
110 qboolean lightshadow;
113 particleeffectinfo_t;
115 #define MAX_PARTICLEEFFECTNAME 256
116 char particleeffectname[MAX_PARTICLEEFFECTNAME][64];
118 #define MAX_PARTICLEEFFECTINFO 4096
120 particleeffectinfo_t particleeffectinfo[MAX_PARTICLEEFFECTINFO];
122 static int particlepalette[256];
124 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b, // 0-7
125 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb, // 8-15
126 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b, // 16-23
127 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23, // 24-31
128 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767, // 32-39
129 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb, // 40-47
130 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07, // 48-55
131 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f, // 56-63
132 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000, // 64-71
133 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000, // 72-79
134 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307, // 80-87
135 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723, // 88-95
136 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b, // 96-103
137 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b, // 104-111
138 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733, // 112-119
139 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397, // 120-127
140 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b, // 128-135
141 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707, // 136-143
142 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b, // 144-151
143 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707, // 152-159
144 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353, // 160-167
145 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07, // 168-175
146 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f, // 176-183
147 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07, // 184-191
148 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307, // 192-199
149 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700, // 200-207
150 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f, // 208-215
151 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f, // 216-223
152 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b, // 224-231
153 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b, // 232-239
154 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000, // 240-247
155 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53 // 248-255
158 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
159 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
160 int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
162 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
164 // texture numbers in particle font
165 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
166 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
167 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
168 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
169 static const int tex_rainsplash = 32;
170 static const int tex_particle = 63;
171 static const int tex_bubble = 62;
172 static const int tex_raindrop = 61;
173 static const int tex_beam = 60;
175 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1", "enables particle effects"};
176 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1", "multiplies number of particles"};
177 cvar_t cl_particles_alpha = {CVAR_SAVE, "cl_particles_alpha", "1", "multiplies opacity of particles"};
178 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1", "multiplies particle size"};
179 cvar_t cl_particles_quake = {CVAR_SAVE, "cl_particles_quake", "0", "makes particle effects look mostly like the ones in Quake"};
180 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1", "enables blood effects"};
181 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "1", "opacity of blood"};
182 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1", "make certain quake particle() calls create blood effects instead"};
183 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1", "enables bulletimpact effects"};
184 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1", "enables sparks from explosions"};
185 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0", "enables polygonal shell from explosions"};
186 cvar_t cl_particles_rain = {CVAR_SAVE, "cl_particles_rain", "1", "enables rain effects"};
187 cvar_t cl_particles_snow = {CVAR_SAVE, "cl_particles_snow", "1", "enables snow effects"};
188 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1", "enables smoke (used by multiple effects)"};
189 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5", "smoke brightness"};
190 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55", "brightness fade per second"};
191 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1", "enables sparks (used by multiple effects)"};
192 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1", "enables bubbles (used by multiple effects)"};
193 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "1", "enables decals (bullet holes, blood, etc)"};
194 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "20", "how long before decals start to fade away"};
195 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "1", "how long decals take to fade away"};
198 void CL_Particles_ParseEffectInfo(const char *textstart, const char *textend)
204 particleeffectinfo_t *info = NULL;
205 const char *text = textstart;
207 effectinfoindex = -1;
208 for (linenumber = 1;;linenumber++)
211 for (arrayindex = 0;arrayindex < 16;arrayindex++)
212 argv[arrayindex][0] = 0;
215 if (!COM_ParseToken_Simple(&text, true, false))
217 if (!strcmp(com_token, "\n"))
221 strlcpy(argv[argc], com_token, sizeof(argv[argc]));
227 #define checkparms(n) if (argc != (n)) {Con_Printf("effectinfo.txt:%i: error while parsing: %s given %i parameters, should be %i parameters\n", linenumber, argv[0], argc, (n));break;}
228 #define readints(array, n) checkparms(n+1);for (arrayindex = 0;arrayindex < argc - 1;arrayindex++) array[arrayindex] = strtol(argv[1+arrayindex], NULL, 0)
229 #define readfloats(array, n) checkparms(n+1);for (arrayindex = 0;arrayindex < argc - 1;arrayindex++) array[arrayindex] = atof(argv[1+arrayindex])
230 #define readint(var) checkparms(2);var = strtol(argv[1], NULL, 0)
231 #define readfloat(var) checkparms(2);var = atof(argv[1])
232 if (!strcmp(argv[0], "effect"))
237 if (effectinfoindex >= MAX_PARTICLEEFFECTINFO)
239 Con_Printf("effectinfo.txt:%i: too many effects!\n", linenumber);
242 for (effectnameindex = 1;effectnameindex < MAX_PARTICLEEFFECTNAME;effectnameindex++)
244 if (particleeffectname[effectnameindex][0])
246 if (!strcmp(particleeffectname[effectnameindex], argv[1]))
251 strlcpy(particleeffectname[effectnameindex], argv[1], sizeof(particleeffectname[effectnameindex]));
255 // if we run out of names, abort
256 if (effectnameindex == MAX_PARTICLEEFFECTNAME)
258 Con_Printf("effectinfo.txt:%i: too many effects!\n", linenumber);
261 info = particleeffectinfo + effectinfoindex;
262 info->effectnameindex = effectnameindex;
263 info->particletype = pt_alphastatic;
264 info->tex[0] = tex_particle;
265 info->tex[1] = tex_particle;
266 info->color[0] = 0xFFFFFF;
267 info->color[1] = 0xFFFFFF;
271 info->alpha[1] = 256;
272 info->alpha[2] = 256;
273 info->time[0] = 9999;
274 info->time[1] = 9999;
275 VectorSet(info->lightcolor, 1, 1, 1);
276 info->lightshadow = true;
277 info->lighttime = 9999;
279 else if (info == NULL)
281 Con_Printf("effectinfo.txt:%i: command %s encountered before effect\n", linenumber, argv[0]);
284 else if (!strcmp(argv[0], "countabsolute")) {readfloat(info->countabsolute);}
285 else if (!strcmp(argv[0], "count")) {readfloat(info->countmultiplier);}
286 else if (!strcmp(argv[0], "type"))
289 if (!strcmp(argv[1], "alphastatic")) info->particletype = pt_alphastatic;
290 else if (!strcmp(argv[1], "static")) info->particletype = pt_static;
291 else if (!strcmp(argv[1], "spark")) info->particletype = pt_spark;
292 else if (!strcmp(argv[1], "beam")) info->particletype = pt_beam;
293 else if (!strcmp(argv[1], "rain")) info->particletype = pt_rain;
294 else if (!strcmp(argv[1], "raindecal")) info->particletype = pt_raindecal;
295 else if (!strcmp(argv[1], "snow")) info->particletype = pt_snow;
296 else if (!strcmp(argv[1], "bubble")) info->particletype = pt_bubble;
297 else if (!strcmp(argv[1], "blood")) info->particletype = pt_blood;
298 else if (!strcmp(argv[1], "smoke")) info->particletype = pt_smoke;
299 else if (!strcmp(argv[1], "decal")) info->particletype = pt_decal;
300 else if (!strcmp(argv[1], "entityparticle")) info->particletype = pt_entityparticle;
301 else Con_Printf("effectinfo.txt:%i: unrecognized particle type %s\n", linenumber, argv[1]);
303 else if (!strcmp(argv[0], "color")) {readints(info->color, 2);}
304 else if (!strcmp(argv[0], "tex")) {readints(info->tex, 2);}
305 else if (!strcmp(argv[0], "size")) {readfloats(info->size, 2);}
306 else if (!strcmp(argv[0], "sizeincrease")) {readfloat(info->size[2]);}
307 else if (!strcmp(argv[0], "alpha")) {readfloats(info->alpha, 3);}
308 else if (!strcmp(argv[0], "time")) {readints(info->time, 2);}
309 else if (!strcmp(argv[0], "gravity")) {readfloat(info->gravity);}
310 else if (!strcmp(argv[0], "bounce")) {readfloat(info->bounce);}
311 else if (!strcmp(argv[0], "airfriction")) {readfloat(info->airfriction);}
312 else if (!strcmp(argv[0], "liquidfriction")) {readfloat(info->liquidfriction);}
313 else if (!strcmp(argv[0], "originoffset")) {readfloats(info->originoffset, 3);}
314 else if (!strcmp(argv[0], "velocityoffset")) {readfloats(info->velocityoffset, 3);}
315 else if (!strcmp(argv[0], "originjitter")) {readfloats(info->originjitter, 3);}
316 else if (!strcmp(argv[0], "velocityjitter")) {readfloats(info->velocityjitter, 3);}
317 else if (!strcmp(argv[0], "velocitymultiplier")) {readfloat(info->velocitymultiplier);}
318 else if (!strcmp(argv[0], "lightradius")) {readfloat(info->lightradiusstart);}
319 else if (!strcmp(argv[0], "lightradiusfade")) {readfloat(info->lightradiusfade);}
320 else if (!strcmp(argv[0], "lighttime")) {readfloat(info->lighttime);}
321 else if (!strcmp(argv[0], "lightcolor")) {readfloats(info->lightcolor, 3);}
322 else if (!strcmp(argv[0], "lightshadow")) {readint(info->lightshadow);}
323 else if (!strcmp(argv[0], "lightcubemapnum")) {readint(info->lightcubemapnum);}
324 else if (!strcmp(argv[0], "underwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_UNDERWATER;}
325 else if (!strcmp(argv[0], "notunderwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_NOTUNDERWATER;}
326 else if (!strcmp(argv[0], "trailspacing")) {readfloat(info->trailspacing);if (info->trailspacing > 0) info->countmultiplier = 1.0f / info->trailspacing;}
328 Con_Printf("effectinfo.txt:%i: skipping unknown command %s\n", linenumber, argv[0]);
337 int CL_ParticleEffectIndexForName(const char *name)
340 for (i = 1;i < MAX_PARTICLEEFFECTNAME && particleeffectname[i][0];i++)
341 if (!strcmp(particleeffectname[i], name))
346 const char *CL_ParticleEffectNameForIndex(int i)
348 if (i < 1 || i >= MAX_PARTICLEEFFECTNAME)
350 return particleeffectname[i];
353 // MUST match effectnameindex_t in client.h
354 static const char *standardeffectnames[EFFECT_TOTAL] =
378 "TE_TEI_BIGEXPLOSION",
394 void CL_Particles_LoadEffectInfo(void)
397 unsigned char *filedata;
398 fs_offset_t filesize;
399 memset(particleeffectinfo, 0, sizeof(particleeffectinfo));
400 memset(particleeffectname, 0, sizeof(particleeffectname));
401 for (i = 0;i < EFFECT_TOTAL;i++)
402 strlcpy(particleeffectname[i], standardeffectnames[i], sizeof(particleeffectname[i]));
403 filedata = FS_LoadFile("effectinfo.txt", tempmempool, true, &filesize);
406 CL_Particles_ParseEffectInfo((const char *)filedata, (const char *)filedata + filesize);
416 void CL_ReadPointFile_f (void);
417 void CL_Particles_Init (void)
419 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f, "display point file produced by qbsp when a leak was detected in the map (a line leading through the leak hole, to an entity inside the level)");
420 Cmd_AddCommand ("cl_particles_reloadeffects", CL_Particles_LoadEffectInfo, "reloads effectinfo.txt");
422 Cvar_RegisterVariable (&cl_particles);
423 Cvar_RegisterVariable (&cl_particles_quality);
424 Cvar_RegisterVariable (&cl_particles_alpha);
425 Cvar_RegisterVariable (&cl_particles_size);
426 Cvar_RegisterVariable (&cl_particles_quake);
427 Cvar_RegisterVariable (&cl_particles_blood);
428 Cvar_RegisterVariable (&cl_particles_blood_alpha);
429 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
430 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
431 Cvar_RegisterVariable (&cl_particles_explosions_shell);
432 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
433 Cvar_RegisterVariable (&cl_particles_rain);
434 Cvar_RegisterVariable (&cl_particles_snow);
435 Cvar_RegisterVariable (&cl_particles_smoke);
436 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
437 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
438 Cvar_RegisterVariable (&cl_particles_sparks);
439 Cvar_RegisterVariable (&cl_particles_bubbles);
440 Cvar_RegisterVariable (&cl_decals);
441 Cvar_RegisterVariable (&cl_decals_time);
442 Cvar_RegisterVariable (&cl_decals_fadetime);
445 void CL_Particles_Shutdown (void)
449 // list of all 26 parameters:
450 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
451 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
452 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
453 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
454 // palpha - opacity of particle as 0-255 (can be more than 255)
455 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
456 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
457 // pgravity - how much effect gravity has on the particle (0-1)
458 // 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
459 // px,py,pz - starting origin of particle
460 // pvx,pvy,pvz - starting velocity of particle
461 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
462 static particle_t *CL_NewParticle(unsigned short ptypeindex, int pcolor1, int pcolor2, int ptex, float psize, float psizeincrease, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pairfriction, float pliquidfriction, float originjitter, float velocityjitter, qboolean pqualityreduction, float lifetime)
467 if (!cl_particles.integer)
469 for (;cl.free_particle < cl.max_particles && cl.particles[cl.free_particle].typeindex;cl.free_particle++);
470 if (cl.free_particle >= cl.max_particles)
473 lifetime = palpha / min(1, palphafade);
474 part = &cl.particles[cl.free_particle++];
475 if (cl.num_particles < cl.free_particle)
476 cl.num_particles = cl.free_particle;
477 memset(part, 0, sizeof(*part));
478 part->typeindex = ptypeindex;
479 l2 = (int)lhrandom(0.5, 256.5);
481 part->color[0] = ((((pcolor1 >> 16) & 0xFF) * l1 + ((pcolor2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
482 part->color[1] = ((((pcolor1 >> 8) & 0xFF) * l1 + ((pcolor2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
483 part->color[2] = ((((pcolor1 >> 0) & 0xFF) * l1 + ((pcolor2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
486 part->sizeincrease = psizeincrease;
487 part->alpha = palpha;
488 part->alphafade = palphafade;
489 part->gravity = pgravity;
490 part->bounce = pbounce;
492 part->org[0] = px + originjitter * v[0];
493 part->org[1] = py + originjitter * v[1];
494 part->org[2] = pz + originjitter * v[2];
495 part->vel[0] = pvx + velocityjitter * v[0];
496 part->vel[1] = pvy + velocityjitter * v[1];
497 part->vel[2] = pvz + velocityjitter * v[2];
499 part->airfriction = pairfriction;
500 part->liquidfriction = pliquidfriction;
501 part->die = cl.time + lifetime;
502 part->delayedcollisions = 0;
503 part->qualityreduction = pqualityreduction;
504 if (part->typeindex == pt_blood)
505 part->gravity += 1; // FIXME: this is a legacy hack, effectinfo.txt doesn't have gravity on blood (nor do the particle calls in the engine)
506 // if it is rain or snow, trace ahead and shut off collisions until an actual collision event needs to occur to improve performance
507 if (part->typeindex == pt_rain)
511 float lifetime = part->die - cl.time;
514 // turn raindrop into simple spark and create delayedspawn splash effect
515 part->typeindex = pt_spark;
517 VectorMA(part->org, lifetime, part->vel, endvec);
518 trace = CL_Move(part->org, vec3_origin, vec3_origin, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK, true, false, NULL, false);
519 part->die = cl.time + lifetime * trace.fraction;
520 part2 = CL_NewParticle(pt_raindecal, pcolor1, pcolor2, tex_rainsplash, part->size, part->size * 20, part->alpha, part->alpha / 0.4, 0, 0, trace.endpos[0] + trace.plane.normal[0], trace.endpos[1] + trace.plane.normal[1], trace.endpos[2] + trace.plane.normal[2], trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2], 0, 0, 0, 0, pqualityreduction, 0);
523 part2->delayedspawn = part->die;
524 part2->die += part->die - cl.time;
525 for (i = rand() & 7;i < 10;i++)
527 part2 = CL_NewParticle(pt_spark, pcolor1, pcolor2, tex_particle, 0.25f, 0, part->alpha * 2, part->alpha * 4, 1, 0, trace.endpos[0] + trace.plane.normal[0], trace.endpos[1] + trace.plane.normal[1], trace.endpos[2] + trace.plane.normal[2], trace.plane.normal[0] * 16, trace.plane.normal[1] * 16, trace.plane.normal[2] * 16 + cl.movevars_gravity * 0.04, 0, 0, 0, 32, pqualityreduction, 0);
530 part2->delayedspawn = part->die;
531 part2->die += part->die - cl.time;
536 else if (part->bounce != 0 && part->gravity == 0 && part->typeindex != pt_snow)
538 float lifetime = part->alpha / (part->alphafade ? part->alphafade : 1);
541 VectorMA(part->org, lifetime, part->vel, endvec);
542 trace = CL_Move(part->org, vec3_origin, vec3_origin, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY, true, false, NULL, false);
543 part->delayedcollisions = cl.time + lifetime * trace.fraction - 0.1;
548 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
552 if (!cl_decals.integer)
554 for (;cl.free_decal < cl.max_decals && cl.decals[cl.free_decal].typeindex;cl.free_decal++);
555 if (cl.free_decal >= cl.max_decals)
557 decal = &cl.decals[cl.free_decal++];
558 if (cl.num_decals < cl.free_decal)
559 cl.num_decals = cl.free_decal;
560 memset(decal, 0, sizeof(*decal));
561 decal->typeindex = pt_decal;
562 decal->texnum = texnum;
563 VectorAdd(org, normal, decal->org);
564 VectorCopy(normal, decal->normal);
566 decal->alpha = alpha;
567 decal->time2 = cl.time;
568 l2 = (int)lhrandom(0.5, 256.5);
570 decal->color[0] = ((((color1 >> 16) & 0xFF) * l1 + ((color2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
571 decal->color[1] = ((((color1 >> 8) & 0xFF) * l1 + ((color2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
572 decal->color[2] = ((((color1 >> 0) & 0xFF) * l1 + ((color2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
573 decal->owner = hitent;
576 // these relative things are only used to regenerate p->org and p->vel if decal->owner is not world (0)
577 decal->ownermodel = cl.entities[decal->owner].render.model;
578 Matrix4x4_Transform(&cl.entities[decal->owner].render.inversematrix, org, decal->relativeorigin);
579 Matrix4x4_Transform3x3(&cl.entities[decal->owner].render.inversematrix, normal, decal->relativenormal);
583 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
586 float bestfrac, bestorg[3], bestnormal[3];
588 int besthitent = 0, hitent;
591 for (i = 0;i < 32;i++)
594 VectorMA(org, maxdist, org2, org2);
595 trace = CL_Move(org, vec3_origin, vec3_origin, org2, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, true, false, &hitent, false);
596 // take the closest trace result that doesn't end up hitting a NOMARKS
597 // surface (sky for example)
598 if (bestfrac > trace.fraction && !(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
600 bestfrac = trace.fraction;
602 VectorCopy(trace.endpos, bestorg);
603 VectorCopy(trace.plane.normal, bestnormal);
607 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
610 static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount);
611 static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount);
612 void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, entity_t *ent, int palettecolor, qboolean spawndlight, qboolean spawnparticles)
615 matrix4x4_t tempmatrix;
616 VectorLerp(originmins, 0.5, originmaxs, center);
617 Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
618 if (effectnameindex == EFFECT_SVC_PARTICLE)
620 if (cl_particles.integer)
622 // bloodhack checks if this effect's color matches regular or lightning blood and if so spawns a blood effect instead
624 CL_ParticleExplosion(center);
625 else if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer && (palettecolor == 73 || palettecolor == 225))
626 CL_ParticleEffect(EFFECT_TE_BLOOD, count / 2.0f, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
629 count *= cl_particles_quality.value;
630 for (;count > 0;count--)
632 int k = particlepalette[(palettecolor & ~7) + (rand()&7)];
633 CL_NewParticle(pt_alphastatic, k, k, tex_particle, 1.5, 0, 255, 0, 0.05, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 8, 0, true, lhrandom(0.1, 0.5));
638 else if (effectnameindex == EFFECT_TE_WIZSPIKE)
639 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 30*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 20);
640 else if (effectnameindex == EFFECT_TE_KNIGHTSPIKE)
641 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 226);
642 else if (effectnameindex == EFFECT_TE_SPIKE)
644 if (cl_particles_bulletimpacts.integer)
646 if (cl_particles_quake.integer)
648 if (cl_particles_smoke.integer)
649 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
653 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
654 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
655 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0);
659 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
660 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
662 else if (effectnameindex == EFFECT_TE_SPIKEQUAD)
664 if (cl_particles_bulletimpacts.integer)
666 if (cl_particles_quake.integer)
668 if (cl_particles_smoke.integer)
669 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
673 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
674 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
675 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0);
679 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
680 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
681 CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
683 else if (effectnameindex == EFFECT_TE_SUPERSPIKE)
685 if (cl_particles_bulletimpacts.integer)
687 if (cl_particles_quake.integer)
689 if (cl_particles_smoke.integer)
690 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
694 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
695 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
696 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0);
700 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
701 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
703 else if (effectnameindex == EFFECT_TE_SUPERSPIKEQUAD)
705 if (cl_particles_bulletimpacts.integer)
707 if (cl_particles_quake.integer)
709 if (cl_particles_smoke.integer)
710 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
714 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
715 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
716 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0);
720 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
721 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
722 CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
724 else if (effectnameindex == EFFECT_TE_BLOOD)
726 if (!cl_particles_blood.integer)
728 if (cl_particles_quake.integer)
729 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 2*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 73);
732 static double bloodaccumulator = 0;
733 //CL_NewParticle(pt_alphastatic, 0x4f0000,0x7f0000, tex_particle, 2.5, 0, 256, 256, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 1, 4, 0, 0, true, 0);
734 bloodaccumulator += count * 0.333 * cl_particles_quality.value;
735 for (;bloodaccumulator > 0;bloodaccumulator--)
736 CL_NewParticle(pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, cl_particles_blood_alpha.value * 768, cl_particles_blood_alpha.value * 384, 0, -1, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 64, true, 0);
739 else if (effectnameindex == EFFECT_TE_SPARK)
740 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, count);
741 else if (effectnameindex == EFFECT_TE_PLASMABURN)
743 // plasma scorch mark
744 R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
745 CL_SpawnDecalParticleForPoint(center, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
746 CL_AllocLightFlash(NULL, &tempmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
748 else if (effectnameindex == EFFECT_TE_GUNSHOT)
750 if (cl_particles_bulletimpacts.integer)
752 if (cl_particles_quake.integer)
753 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
756 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
757 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
758 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0);
762 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
763 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
765 else if (effectnameindex == EFFECT_TE_GUNSHOTQUAD)
767 if (cl_particles_bulletimpacts.integer)
769 if (cl_particles_quake.integer)
770 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
773 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
774 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
775 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0);
779 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
780 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
781 CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
783 else if (effectnameindex == EFFECT_TE_EXPLOSION)
785 CL_ParticleExplosion(center);
786 CL_AllocLightFlash(NULL, &tempmatrix, 350, 4.0f, 2.0f, 0.50f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
788 else if (effectnameindex == EFFECT_TE_EXPLOSIONQUAD)
790 CL_ParticleExplosion(center);
791 CL_AllocLightFlash(NULL, &tempmatrix, 350, 2.5f, 2.0f, 4.0f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
793 else if (effectnameindex == EFFECT_TE_TAREXPLOSION)
795 if (cl_particles_quake.integer)
798 for (i = 0;i < 1024 * cl_particles_quality.value;i++)
801 CL_NewParticle(pt_alphastatic, particlepalette[66], particlepalette[71], tex_particle, 1.5f, 0, 255, 0, 0, 0, center[0], center[1], center[2], 0, 0, 0, -4, -4, 16, 256, true, (rand() & 1) ? 1.4 : 1.0);
803 CL_NewParticle(pt_alphastatic, particlepalette[150], particlepalette[155], tex_particle, 1.5f, 0, 255, 0, 0, 0, center[0], center[1], center[2], 0, 0, lhrandom(-256, 256), 0, 0, 16, 0, true, (rand() & 1) ? 1.4 : 1.0);
807 CL_ParticleExplosion(center);
808 CL_AllocLightFlash(NULL, &tempmatrix, 600, 1.6f, 0.8f, 2.0f, 1200, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
810 else if (effectnameindex == EFFECT_TE_SMALLFLASH)
811 CL_AllocLightFlash(NULL, &tempmatrix, 200, 2, 2, 2, 1000, 0.2, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
812 else if (effectnameindex == EFFECT_TE_FLAMEJET)
814 count *= cl_particles_quality.value;
816 CL_NewParticle(pt_smoke, 0x6f0f00, 0xe3974f, tex_particle, 4, 0, lhrandom(64, 128), 384, -1, 1.1, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 128, true, 0);
818 else if (effectnameindex == EFFECT_TE_LAVASPLASH)
820 float i, j, inc, vel;
823 inc = 8 / cl_particles_quality.value;
824 for (i = -128;i < 128;i += inc)
826 for (j = -128;j < 128;j += inc)
828 dir[0] = j + lhrandom(0, inc);
829 dir[1] = i + lhrandom(0, inc);
831 org[0] = center[0] + dir[0];
832 org[1] = center[1] + dir[1];
833 org[2] = center[2] + lhrandom(0, 64);
834 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
835 CL_NewParticle(pt_alphastatic, particlepalette[224], particlepalette[231], tex_particle, 1.5f, 0, 255, 0, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, true, lhrandom(2, 2.62));
839 else if (effectnameindex == EFFECT_TE_TELEPORT)
841 float i, j, k, inc, vel;
844 if (cl_particles_quake.integer)
845 inc = 4 / cl_particles_quality.value;
847 inc = 8 / cl_particles_quality.value;
848 for (i = -16;i < 16;i += inc)
850 for (j = -16;j < 16;j += inc)
852 for (k = -24;k < 32;k += inc)
854 VectorSet(dir, i*8, j*8, k*8);
855 VectorNormalize(dir);
856 vel = lhrandom(50, 113);
857 if (cl_particles_quake.integer)
858 CL_NewParticle(pt_alphastatic, particlepalette[7], particlepalette[14], tex_particle, 1.5f, 0, 255, 0, 0, 0, center[0] + i + lhrandom(0, inc), center[1] + j + lhrandom(0, inc), center[2] + k + lhrandom(0, inc), dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, true, lhrandom(0.2, 0.34));
860 CL_NewParticle(pt_alphastatic, particlepalette[7], particlepalette[14], tex_particle, 1.5f, 0, inc * lhrandom(37, 63), inc * 187, 0, 0, center[0] + i + lhrandom(0, inc), center[1] + j + lhrandom(0, inc), center[2] + k + lhrandom(0, inc), dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, true, 0);
864 if (!cl_particles_quake.integer)
865 CL_NewParticle(pt_static, 0xffffff, 0xffffff, tex_particle, 30, 0, 256, 512, 0, 0, center[0], center[1], center[2], 0, 0, 0, 0, 0, 0, 0, false, 0);
866 CL_AllocLightFlash(NULL, &tempmatrix, 200, 2.0f, 2.0f, 2.0f, 400, 99.0f, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
868 else if (effectnameindex == EFFECT_TE_TEI_G3)
869 CL_NewParticle(pt_beam, 0xFFFFFF, 0xFFFFFF, tex_beam, 8, 0, 256, 256, 0, 0, originmins[0], originmins[1], originmins[2], originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, 0, false, 0);
870 else if (effectnameindex == EFFECT_TE_TEI_SMOKE)
872 if (cl_particles_smoke.integer)
874 count *= 0.25f * cl_particles_quality.value;
876 CL_NewParticle(pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 0, 255, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 1.5f, 6.0f, true, 0);
879 else if (effectnameindex == EFFECT_TE_TEI_BIGEXPLOSION)
881 CL_ParticleExplosion(center);
882 CL_AllocLightFlash(NULL, &tempmatrix, 500, 2.5f, 2.0f, 1.0f, 500, 9999, 0, -1, true, 1, 0.25, 0.5, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
884 else if (effectnameindex == EFFECT_TE_TEI_PLASMAHIT)
887 R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
888 CL_SpawnDecalParticleForPoint(center, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
889 if (cl_particles_smoke.integer)
890 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
891 CL_NewParticle(pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 0, 255, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 20, 155, true, 0);
892 if (cl_particles_sparks.integer)
893 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
894 CL_NewParticle(pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, 0, lhrandom(64, 255), 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 0, 465, true, 0);
895 CL_AllocLightFlash(NULL, &tempmatrix, 500, 0.6f, 1.2f, 2.0f, 2000, 9999, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
897 else if (effectnameindex == EFFECT_EF_FLAME)
899 count *= 300 * cl_particles_quality.value;
901 CL_NewParticle(pt_smoke, 0x6f0f00, 0xe3974f, tex_particle, 4, 0, lhrandom(64, 128), 384, -1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 16, 128, true, 0);
902 CL_AllocLightFlash(NULL, &tempmatrix, 200, 2.0f, 1.5f, 0.5f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
904 else if (effectnameindex == EFFECT_EF_STARDUST)
906 count *= 200 * cl_particles_quality.value;
908 CL_NewParticle(pt_static, 0x903010, 0xFFD030, tex_particle, 4, 0, lhrandom(64, 128), 128, 1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0.2, 0.8, 16, 128, true, 0);
909 CL_AllocLightFlash(NULL, &tempmatrix, 200, 1.0f, 0.7f, 0.3f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
911 else if (!strncmp(particleeffectname[effectnameindex], "TR_", 3))
915 int smoke, blood, bubbles, r, color;
917 if (spawndlight && r_refdef.scene.numlights < MAX_DLIGHTS)
920 Vector4Set(light, 0, 0, 0, 0);
922 if (effectnameindex == EFFECT_TR_ROCKET)
923 Vector4Set(light, 3.0f, 1.5f, 0.5f, 200);
924 else if (effectnameindex == EFFECT_TR_VORESPIKE)
926 if (gamemode == GAME_PRYDON && !cl_particles_quake.integer)
927 Vector4Set(light, 0.3f, 0.6f, 1.2f, 100);
929 Vector4Set(light, 1.2f, 0.5f, 1.0f, 200);
931 else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
932 Vector4Set(light, 0.75f, 1.5f, 3.0f, 200);
936 matrix4x4_t tempmatrix;
937 Matrix4x4_CreateFromQuakeEntity(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, light[3]);
938 R_RTLight_Update(&r_refdef.scene.lights[r_refdef.scene.numlights++], false, &tempmatrix, light, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
945 if (originmaxs[0] == originmins[0] && originmaxs[1] == originmins[1] && originmaxs[2] == originmins[2])
948 VectorSubtract(originmaxs, originmins, dir);
949 len = VectorNormalizeLength(dir);
952 dec = -ent->persistent.trail_time;
953 ent->persistent.trail_time += len;
954 if (ent->persistent.trail_time < 0.01f)
957 // if we skip out, leave it reset
958 ent->persistent.trail_time = 0.0f;
963 // advance into this frame to reach the first puff location
964 VectorMA(originmins, dec, dir, pos);
967 smoke = cl_particles.integer && cl_particles_smoke.integer;
968 blood = cl_particles.integer && cl_particles_blood.integer;
969 bubbles = cl_particles.integer && cl_particles_bubbles.integer && !cl_particles_quake.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
970 qd = 1.0f / cl_particles_quality.value;
977 if (effectnameindex == EFFECT_TR_BLOOD)
979 if (cl_particles_quake.integer)
981 color = particlepalette[67 + (rand()&3)];
982 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 2);
987 CL_NewParticle(pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 64, true, 0);
990 else if (effectnameindex == EFFECT_TR_SLIGHTBLOOD)
992 if (cl_particles_quake.integer)
995 color = particlepalette[67 + (rand()&3)];
996 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 2);
1001 CL_NewParticle(pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 64, true, 0);
1007 if (effectnameindex == EFFECT_TR_ROCKET)
1009 if (cl_particles_quake.integer)
1012 color = particlepalette[ramp3[r]];
1013 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, -0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0.1372549*(6-r));
1017 CL_NewParticle(pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*62, cl_particles_smoke_alphafade.value*62, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0);
1018 CL_NewParticle(pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*288, cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 20, true, 0);
1021 else if (effectnameindex == EFFECT_TR_GRENADE)
1023 if (cl_particles_quake.integer)
1026 color = particlepalette[ramp3[r]];
1027 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, -0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0.1372549*(6-r));
1031 CL_NewParticle(pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*50, cl_particles_smoke_alphafade.value*75, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0);
1034 else if (effectnameindex == EFFECT_TR_WIZSPIKE)
1036 if (cl_particles_quake.integer)
1039 color = particlepalette[52 + (rand()&7)];
1040 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, true, 0.5);
1041 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, true, 0.5);
1043 else if (gamemode == GAME_GOODVSBAD2)
1046 CL_NewParticle(pt_static, 0x00002E, 0x000030, tex_particle, 6, 0, 128, 384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0);
1050 color = particlepalette[20 + (rand()&7)];
1051 CL_NewParticle(pt_static, color, color, tex_particle, 2, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0);
1054 else if (effectnameindex == EFFECT_TR_KNIGHTSPIKE)
1056 if (cl_particles_quake.integer)
1059 color = particlepalette[230 + (rand()&7)];
1060 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, true, 0.5);
1061 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, true, 0.5);
1065 color = particlepalette[226 + (rand()&7)];
1066 CL_NewParticle(pt_static, color, color, tex_particle, 2, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0);
1069 else if (effectnameindex == EFFECT_TR_VORESPIKE)
1071 if (cl_particles_quake.integer)
1073 color = particlepalette[152 + (rand()&3)];
1074 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 8, 0, true, 0.3);
1076 else if (gamemode == GAME_GOODVSBAD2)
1079 CL_NewParticle(pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, 0, 255, 384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0);
1081 else if (gamemode == GAME_PRYDON)
1084 CL_NewParticle(pt_static, 0x103040, 0x204050, tex_particle, 6, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0);
1087 CL_NewParticle(pt_static, 0x502030, 0x502030, tex_particle, 3, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0);
1089 else if (effectnameindex == EFFECT_TR_NEHAHRASMOKE)
1092 CL_NewParticle(pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, 0, 64, 320, 0, 0, pos[0], pos[1], pos[2], 0, 0, lhrandom(4, 12), 0, 0, 0, 4, false, 0);
1094 else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
1097 CL_NewParticle(pt_static, 0x283880, 0x283880, tex_particle, 4, 0, 255, 1024, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 16, true, 0);
1099 else if (effectnameindex == EFFECT_TR_GLOWTRAIL)
1100 CL_NewParticle(pt_alphastatic, particlepalette[palettecolor], particlepalette[palettecolor], tex_particle, 5, 0, 128, 320, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0);
1104 if (effectnameindex == EFFECT_TR_ROCKET)
1105 CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(128, 512), 512, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, 0.0625, 0.25, 0, 16, true, 0);
1106 else if (effectnameindex == EFFECT_TR_GRENADE)
1107 CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(128, 512), 512, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, 0.0625, 0.25, 0, 16, true, 0);
1109 // advance to next time and position
1112 VectorMA (pos, dec, dir, pos);
1115 ent->persistent.trail_time = len;
1117 else if (developer.integer >= 1)
1118 Con_Printf("CL_ParticleEffect_Fallback: no fallback found for effect %s\n", particleeffectname[effectnameindex]);
1121 // this is also called on point effects with spawndlight = true and
1122 // spawnparticles = true
1123 // it is called CL_ParticleTrail because most code does not want to supply
1124 // these parameters, only trail handling does
1125 void CL_ParticleTrail(int effectnameindex, float pcount, const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, entity_t *ent, int palettecolor, qboolean spawndlight, qboolean spawnparticles)
1128 qboolean found = false;
1129 if (effectnameindex < 1 || effectnameindex >= MAX_PARTICLEEFFECTNAME || !particleeffectname[effectnameindex][0])
1131 Con_DPrintf("Unknown effect number %i received from server\n", effectnameindex);
1132 return; // no such effect
1134 VectorLerp(originmins, 0.5, originmaxs, center);
1135 if (!cl_particles_quake.integer && particleeffectinfo[0].effectnameindex)
1137 int effectinfoindex;
1140 particleeffectinfo_t *info;
1142 vec3_t centervelocity;
1148 qboolean underwater;
1149 // note this runs multiple effects with the same name, each one spawns only one kind of particle, so some effects need more than one
1150 VectorLerp(originmins, 0.5, originmaxs, center);
1151 VectorLerp(velocitymins, 0.5, velocitymaxs, centervelocity);
1152 supercontents = CL_PointSuperContents(center);
1153 underwater = (supercontents & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)) != 0;
1154 VectorSubtract(originmaxs, originmins, traildir);
1155 traillen = VectorLength(traildir);
1156 VectorNormalize(traildir);
1157 for (effectinfoindex = 0, info = particleeffectinfo;effectinfoindex < MAX_PARTICLEEFFECTINFO && info->effectnameindex;effectinfoindex++, info++)
1159 if (info->effectnameindex == effectnameindex)
1162 if ((info->flags & PARTICLEEFFECT_UNDERWATER) && !underwater)
1164 if ((info->flags & PARTICLEEFFECT_NOTUNDERWATER) && underwater)
1167 // spawn a dlight if requested
1168 if (info->lightradiusstart > 0 && spawndlight)
1170 matrix4x4_t tempmatrix;
1171 if (info->trailspacing > 0)
1172 Matrix4x4_CreateTranslate(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2]);
1174 Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
1175 if (info->lighttime > 0 && info->lightradiusfade > 0)
1177 // light flash (explosion, etc)
1178 // called when effect starts
1179 CL_AllocLightFlash(NULL, &tempmatrix, info->lightradiusstart, info->lightcolor[0], info->lightcolor[1], info->lightcolor[2], info->lightradiusfade, info->lighttime, info->lightcubemapnum, -1, info->lightshadow, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1184 // called by CL_LinkNetworkEntity
1185 Matrix4x4_Scale(&tempmatrix, info->lightradiusstart, 1);
1186 R_RTLight_Update(&r_refdef.scene.lights[r_refdef.scene.numlights++], false, &tempmatrix, info->lightcolor, -1, info->lightcubemapnum > 0 ? va("cubemaps/%i", info->lightcubemapnum) : NULL, info->lightshadow, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1190 if (!spawnparticles)
1195 if (info->tex[1] > info->tex[0])
1197 tex = (int)lhrandom(info->tex[0], info->tex[1]);
1198 tex = min(tex, info->tex[1] - 1);
1200 if (info->particletype == pt_decal)
1201 CL_SpawnDecalParticleForPoint(center, info->originjitter[0], lhrandom(info->size[0], info->size[1]), lhrandom(info->alpha[0], info->alpha[1]), tex, info->color[0], info->color[1]);
1202 else if (info->particletype == pt_beam)
1203 CL_NewParticle(info->particletype, info->color[0], info->color[1], tex, lhrandom(info->size[0], info->size[1]), info->size[2], lhrandom(info->alpha[0], info->alpha[1]), info->alpha[2], 0, 0, originmins[0], originmins[1], originmins[2], originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, 0, false, lhrandom(info->time[0], info->time[1]));
1206 if (!cl_particles.integer)
1208 switch (info->particletype)
1210 case pt_smoke: if (!cl_particles_smoke.integer) continue;break;
1211 case pt_spark: if (!cl_particles_sparks.integer) continue;break;
1212 case pt_bubble: if (!cl_particles_bubbles.integer) continue;break;
1213 case pt_blood: if (!cl_particles_blood.integer) continue;break;
1214 case pt_rain: if (!cl_particles_rain.integer) continue;break;
1215 case pt_snow: if (!cl_particles_snow.integer) continue;break;
1218 VectorCopy(originmins, trailpos);
1219 if (info->trailspacing > 0)
1221 info->particleaccumulator += traillen / info->trailspacing * cl_particles_quality.value;
1222 trailstep = info->trailspacing / cl_particles_quality.value;
1226 info->particleaccumulator += info->countabsolute + pcount * info->countmultiplier * cl_particles_quality.value;
1229 info->particleaccumulator = bound(0, info->particleaccumulator, 16384);
1230 for (;info->particleaccumulator >= 1;info->particleaccumulator--)
1232 if (info->tex[1] > info->tex[0])
1234 tex = (int)lhrandom(info->tex[0], info->tex[1]);
1235 tex = min(tex, info->tex[1] - 1);
1239 trailpos[0] = lhrandom(originmins[0], originmaxs[0]);
1240 trailpos[1] = lhrandom(originmins[1], originmaxs[1]);
1241 trailpos[2] = lhrandom(originmins[2], originmaxs[2]);
1244 CL_NewParticle(info->particletype, info->color[0], info->color[1], tex, lhrandom(info->size[0], info->size[1]), info->size[2], lhrandom(info->alpha[0], info->alpha[1]), info->alpha[2], info->gravity, info->bounce, trailpos[0] + info->originoffset[0] + info->originjitter[0] * rvec[0], trailpos[1] + info->originoffset[1] + info->originjitter[1] * rvec[1], trailpos[2] + info->originoffset[2] + info->originjitter[2] * rvec[2], lhrandom(velocitymins[0], velocitymaxs[0]) * info->velocitymultiplier + info->velocityoffset[0] + info->velocityjitter[0] * rvec[0], lhrandom(velocitymins[1], velocitymaxs[1]) * info->velocitymultiplier + info->velocityoffset[1] + info->velocityjitter[1] * rvec[1], lhrandom(velocitymins[2], velocitymaxs[2]) * info->velocitymultiplier + info->velocityoffset[2] + info->velocityjitter[2] * rvec[2], info->airfriction, info->liquidfriction, 0, 0, info->countabsolute <= 0, lhrandom(info->time[0], info->time[1]));
1246 VectorMA(trailpos, trailstep, traildir, trailpos);
1253 CL_ParticleEffect_Fallback(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, spawndlight, spawnparticles);
1256 void CL_ParticleEffect(int effectnameindex, float pcount, const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, entity_t *ent, int palettecolor)
1258 CL_ParticleTrail(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, true, true);
1266 void CL_EntityParticles (const entity_t *ent)
1269 float pitch, yaw, dist = 64, beamlength = 16, org[3], v[3];
1270 static vec3_t avelocities[NUMVERTEXNORMALS];
1271 if (!cl_particles.integer) return;
1272 if (cl.time <= cl.oldtime) return; // don't spawn new entity particles while paused
1274 Matrix4x4_OriginFromMatrix(&ent->render.matrix, org);
1276 if (!avelocities[0][0])
1277 for (i = 0;i < NUMVERTEXNORMALS * 3;i++)
1278 avelocities[0][i] = lhrandom(0, 2.55);
1280 for (i = 0;i < NUMVERTEXNORMALS;i++)
1282 yaw = cl.time * avelocities[i][0];
1283 pitch = cl.time * avelocities[i][1];
1284 v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
1285 v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
1286 v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
1287 CL_NewParticle(pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 1, 0, 255, 0, 0, 0, v[0], v[1], v[2], 0, 0, 0, 0, 0, 0, 0, true, 0);
1292 void CL_ReadPointFile_f (void)
1294 vec3_t org, leakorg;
1296 char *pointfile = NULL, *pointfilepos, *t, tchar;
1297 char name[MAX_OSPATH];
1302 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
1303 strlcat (name, ".pts", sizeof (name));
1304 pointfile = (char *)FS_LoadFile(name, tempmempool, true, NULL);
1307 Con_Printf("Could not open %s\n", name);
1311 Con_Printf("Reading %s...\n", name);
1312 VectorClear(leakorg);
1315 pointfilepos = pointfile;
1316 while (*pointfilepos)
1318 while (*pointfilepos == '\n' || *pointfilepos == '\r')
1323 while (*t && *t != '\n' && *t != '\r')
1327 #if _MSC_VER >= 1400
1328 #define sscanf sscanf_s
1330 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
1336 VectorCopy(org, leakorg);
1339 if (cl.num_particles < cl.max_particles - 3)
1342 CL_NewParticle(pt_alphastatic, particlepalette[(-c)&15], particlepalette[(-c)&15], tex_particle, 2, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0, 0, 0, 0, true, 1<<30);
1345 Mem_Free(pointfile);
1346 VectorCopy(leakorg, org);
1347 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
1349 CL_NewParticle(pt_beam, 0xFF0000, 0xFF0000, tex_beam, 64, 0, 255, 0, 0, 0, org[0] - 4096, org[1], org[2], org[0] + 4096, org[1], org[2], 0, 0, 0, 0, false, 1<<30);
1350 CL_NewParticle(pt_beam, 0x00FF00, 0x00FF00, tex_beam, 64, 0, 255, 0, 0, 0, org[0], org[1] - 4096, org[2], org[0], org[1] + 4096, org[2], 0, 0, 0, 0, false, 1<<30);
1351 CL_NewParticle(pt_beam, 0x0000FF, 0x0000FF, tex_beam, 64, 0, 255, 0, 0, 0, org[0], org[1], org[2] - 4096, org[0], org[1], org[2] + 4096, 0, 0, 0, 0, false, 1<<30);
1356 CL_ParseParticleEffect
1358 Parse an effect out of the server message
1361 void CL_ParseParticleEffect (void)
1364 int i, count, msgcount, color;
1366 MSG_ReadVector(org, cls.protocol);
1367 for (i=0 ; i<3 ; i++)
1368 dir[i] = MSG_ReadChar () * (1.0 / 16.0);
1369 msgcount = MSG_ReadByte ();
1370 color = MSG_ReadByte ();
1372 if (msgcount == 255)
1377 CL_ParticleEffect(EFFECT_SVC_PARTICLE, count, org, org, dir, dir, NULL, color);
1382 CL_ParticleExplosion
1386 void CL_ParticleExplosion (const vec3_t org)
1392 R_Stain(org, 96, 40, 40, 40, 64, 88, 88, 88, 64);
1393 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1395 if (cl_particles_quake.integer)
1397 for (i = 0;i < 1024;i++)
1403 color = particlepalette[ramp1[r]];
1404 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256, true, 0.1006 * (8 - r));
1408 color = particlepalette[ramp2[r]];
1409 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 1, 1, 16, 256, true, 0.0669 * (8 - r));
1415 i = CL_PointSuperContents(org);
1416 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
1418 if (cl_particles.integer && cl_particles_bubbles.integer)
1419 for (i = 0;i < 128 * cl_particles_quality.value;i++)
1420 CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(128, 255), 128, -0.125, 1.5, org[0], org[1], org[2], 0, 0, 0, 0.0625, 0.25, 16, 96, true, 0);
1424 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
1426 for (i = 0;i < 512 * cl_particles_quality.value;i++)
1430 for (k = 0;k < 16;k++)
1433 VectorMA(org, 128, v2, v);
1434 trace = CL_Move(org, vec3_origin, vec3_origin, v, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
1435 if (trace.fraction >= 0.1)
1438 VectorSubtract(trace.endpos, org, v2);
1439 VectorScale(v2, 2.0f, v2);
1440 CL_NewParticle(pt_spark, 0x903010, 0xFFD030, tex_particle, 1.0f, 0, lhrandom(0, 255), 512, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0, 0, 0, 0, true, 0);
1446 if (cl_particles_explosions_shell.integer)
1447 R_NewExplosion(org);
1452 CL_ParticleExplosion2
1456 void CL_ParticleExplosion2 (const vec3_t org, int colorStart, int colorLength)
1459 if (!cl_particles.integer) return;
1461 for (i = 0;i < 512 * cl_particles_quality.value;i++)
1463 k = particlepalette[colorStart + (i % colorLength)];
1464 if (cl_particles_quake.integer)
1465 CL_NewParticle(pt_alphastatic, k, k, tex_particle, 1, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256, true, 0.3);
1467 CL_NewParticle(pt_alphastatic, k, k, tex_particle, lhrandom(0.5, 1.5), 0, 255, 512, 0, 0, org[0], org[1], org[2], 0, 0, 0, lhrandom(1.5, 3), lhrandom(1.5, 3), 8, 192, true, 0);
1471 static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount)
1473 if (cl_particles_sparks.integer)
1475 sparkcount *= cl_particles_quality.value;
1476 while(sparkcount-- > 0)
1477 CL_NewParticle(pt_spark, particlepalette[0x68], particlepalette[0x6f], tex_particle, 0.5f, 0, lhrandom(64, 255), 512, 1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]) + cl.movevars_gravity * 0.1f, 0, 0, 0, 64, true, 0);
1481 static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount)
1483 if (cl_particles_smoke.integer)
1485 smokecount *= cl_particles_quality.value;
1486 while(smokecount-- > 0)
1487 CL_NewParticle(pt_smoke, 0x101010, 0x101010, tex_smoke[rand()&7], 2, 2, 255, 256, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 0, smokecount > 0 ? 16 : 0, true, 0);
1491 void CL_ParticleCube (const vec3_t mins, const vec3_t maxs, const vec3_t dir, int count, int colorbase, vec_t gravity, vec_t randomvel)
1494 if (!cl_particles.integer) return;
1496 count = (int)(count * cl_particles_quality.value);
1499 k = particlepalette[colorbase + (rand()&3)];
1500 CL_NewParticle(pt_alphastatic, k, k, tex_particle, 2, 0, 255, 128, gravity, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0], dir[1], dir[2], 0, 0, 0, randomvel, true, 0);
1504 void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, int count, int colorbase, int type)
1507 float minz, maxz, lifetime = 30;
1508 if (!cl_particles.integer) return;
1509 if (dir[2] < 0) // falling
1511 minz = maxs[2] + dir[2] * 0.1;
1514 lifetime = (maxz - cl.worldmodel->normalmins[2]) / max(1, -dir[2]);
1519 maxz = maxs[2] + dir[2] * 0.1;
1521 lifetime = (cl.worldmodel->normalmaxs[2] - minz) / max(1, dir[2]);
1524 count = (int)(count * cl_particles_quality.value);
1529 if (!cl_particles_rain.integer) break;
1530 count *= 4; // ick, this should be in the mod or maps?
1534 k = particlepalette[colorbase + (rand()&3)];
1535 if (gamemode == GAME_GOODVSBAD2)
1536 CL_NewParticle(pt_rain, k, k, tex_particle, 20, 0, lhrandom(32, 64), 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, 0, true, lifetime);
1538 CL_NewParticle(pt_rain, k, k, tex_particle, 0.5, 0, lhrandom(32, 64), 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, 0, true, lifetime);
1542 if (!cl_particles_snow.integer) break;
1545 k = particlepalette[colorbase + (rand()&3)];
1546 if (gamemode == GAME_GOODVSBAD2)
1547 CL_NewParticle(pt_snow, k, k, tex_particle, 20, 0, lhrandom(64, 128), 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, 0, true, lifetime);
1549 CL_NewParticle(pt_snow, k, k, tex_particle, 1, 0, lhrandom(64, 128), 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, 0, true, lifetime);
1553 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
1557 #define MAX_PARTICLETEXTURES 64
1558 // particletexture_t is a rectangle in the particlefonttexture
1559 typedef struct particletexture_s
1561 rtexture_t *texture;
1562 float s1, t1, s2, t2;
1566 static rtexturepool_t *particletexturepool;
1567 static rtexture_t *particlefonttexture;
1568 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1570 static cvar_t r_drawparticles = {0, "r_drawparticles", "1", "enables drawing of particles"};
1571 static cvar_t r_drawparticles_drawdistance = {CVAR_SAVE, "r_drawparticles_drawdistance", "2000", "particles further than drawdistance*size will not be drawn"};
1572 static cvar_t r_drawdecals = {0, "r_drawdecals", "1", "enables drawing of decals"};
1573 static cvar_t r_drawdecals_drawdistance = {CVAR_SAVE, "r_drawdecals_drawdistance", "500", "decals further than drawdistance*size will not be drawn"};
1575 #define PARTICLETEXTURESIZE 64
1576 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1578 static unsigned char shadebubble(float dx, float dy, vec3_t light)
1582 dz = 1 - (dx*dx+dy*dy);
1583 if (dz > 0) // it does hit the sphere
1587 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1588 VectorNormalize(normal);
1589 dot = DotProduct(normal, light);
1590 if (dot > 0.5) // interior reflection
1591 f += ((dot * 2) - 1);
1592 else if (dot < -0.5) // exterior reflection
1593 f += ((dot * -2) - 1);
1595 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1596 VectorNormalize(normal);
1597 dot = DotProduct(normal, light);
1598 if (dot > 0.5) // interior reflection
1599 f += ((dot * 2) - 1);
1600 else if (dot < -0.5) // exterior reflection
1601 f += ((dot * -2) - 1);
1603 f += 16; // just to give it a haze so you can see the outline
1604 f = bound(0, f, 255);
1605 return (unsigned char) f;
1611 static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
1613 int basex, basey, y;
1614 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1615 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1616 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1617 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1620 void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
1623 float cx, cy, dx, dy, f, iradius;
1625 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1626 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1627 iradius = 1.0f / radius;
1628 alpha *= (1.0f / 255.0f);
1629 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1631 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1635 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1640 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1641 d[0] += (int)(f * (blue - d[0]));
1642 d[1] += (int)(f * (green - d[1]));
1643 d[2] += (int)(f * (red - d[2]));
1649 void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1652 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1654 data[0] = bound(minb, data[0], maxb);
1655 data[1] = bound(ming, data[1], maxg);
1656 data[2] = bound(minr, data[2], maxr);
1660 void particletextureinvert(unsigned char *data)
1663 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1665 data[0] = 255 - data[0];
1666 data[1] = 255 - data[1];
1667 data[2] = 255 - data[2];
1671 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1672 static void R_InitBloodTextures (unsigned char *particletexturedata)
1675 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1678 for (i = 0;i < 8;i++)
1680 memset(&data[0][0][0], 255, sizeof(data));
1681 for (k = 0;k < 24;k++)
1682 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1683 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1684 particletextureinvert(&data[0][0][0]);
1685 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1689 for (i = 0;i < 8;i++)
1691 memset(&data[0][0][0], 255, sizeof(data));
1693 for (j = 1;j < 10;j++)
1694 for (k = min(j, m - 1);k < m;k++)
1695 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 320 - j * 8);
1696 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1697 particletextureinvert(&data[0][0][0]);
1698 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1703 //uncomment this to make engine save out particle font to a tga file when run
1704 //#define DUMPPARTICLEFONT
1706 static void R_InitParticleTexture (void)
1708 int x, y, d, i, k, m;
1712 // a note: decals need to modulate (multiply) the background color to
1713 // properly darken it (stain), and they need to be able to alpha fade,
1714 // this is a very difficult challenge because it means fading to white
1715 // (no change to background) rather than black (darkening everything
1716 // behind the whole decal polygon), and to accomplish this the texture is
1717 // inverted (dark red blood on white background becomes brilliant cyan
1718 // and white on black background) so we can alpha fade it to black, then
1719 // we invert it again during the blendfunc to make it work...
1721 #ifndef DUMPPARTICLEFONT
1722 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", false, TEXF_ALPHA | TEXF_PRECACHE, true);
1723 if (!particlefonttexture)
1726 unsigned char *particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1727 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1728 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1731 for (i = 0;i < 8;i++)
1733 memset(&data[0][0][0], 255, sizeof(data));
1736 unsigned char noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1738 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1739 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1741 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1743 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1744 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1746 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1747 d = (noise2[y][x] - 128) * 3 + 192;
1749 d = (int)(d * (1-(dx*dx+dy*dy)));
1750 d = (d * noise1[y][x]) >> 7;
1751 d = bound(0, d, 255);
1752 data[y][x][3] = (unsigned char) d;
1759 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1763 memset(&data[0][0][0], 255, sizeof(data));
1764 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1766 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1767 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1769 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1770 f = 255.0f * (1.0 - 4.0f * fabs(10.0f - sqrt(dx*dx+dy*dy)));
1771 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1774 setuptex(tex_rainsplash, &data[0][0][0], particletexturedata);
1777 memset(&data[0][0][0], 255, sizeof(data));
1778 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1780 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1781 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1783 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1784 d = (int)(256 * (1 - (dx*dx+dy*dy)));
1785 d = bound(0, d, 255);
1786 data[y][x][3] = (unsigned char) d;
1789 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1792 memset(&data[0][0][0], 255, sizeof(data));
1793 light[0] = 1;light[1] = 1;light[2] = 1;
1794 VectorNormalize(light);
1795 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1797 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1798 // stretch upper half of bubble by +50% and shrink lower half by -50%
1799 // (this gives an elongated teardrop shape)
1801 dy = (dy - 0.5f) * 2.0f;
1803 dy = (dy - 0.5f) / 1.5f;
1804 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1806 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1807 // shrink bubble width to half
1809 data[y][x][3] = shadebubble(dx, dy, light);
1812 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1815 memset(&data[0][0][0], 255, sizeof(data));
1816 light[0] = 1;light[1] = 1;light[2] = 1;
1817 VectorNormalize(light);
1818 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1820 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1821 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1823 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1824 data[y][x][3] = shadebubble(dx, dy, light);
1827 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1829 // Blood particles and blood decals
1830 R_InitBloodTextures (particletexturedata);
1833 for (i = 0;i < 8;i++)
1835 memset(&data[0][0][0], 255, sizeof(data));
1836 for (k = 0;k < 12;k++)
1837 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1838 for (k = 0;k < 3;k++)
1839 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1840 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1841 particletextureinvert(&data[0][0][0]);
1842 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1845 #ifdef DUMPPARTICLEFONT
1846 Image_WriteTGABGRA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1849 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1851 Mem_Free(particletexturedata);
1853 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1855 int basex = ((i >> 0) & 7) * PARTICLETEXTURESIZE;
1856 int basey = ((i >> 3) & 7) * PARTICLETEXTURESIZE;
1857 particletexture[i].texture = particlefonttexture;
1858 particletexture[i].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1859 particletexture[i].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1860 particletexture[i].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1861 particletexture[i].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1864 #ifndef DUMPPARTICLEFONT
1865 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", false, TEXF_ALPHA | TEXF_PRECACHE, true);
1866 if (!particletexture[tex_beam].texture)
1869 unsigned char noise3[64][64], data2[64][16][4];
1871 fractalnoise(&noise3[0][0], 64, 4);
1873 for (y = 0;y < 64;y++)
1875 dy = (y - 0.5f*64) / (64*0.5f-1);
1876 for (x = 0;x < 16;x++)
1878 dx = (x - 0.5f*16) / (16*0.5f-2);
1879 d = (int)((1 - sqrt(fabs(dx))) * noise3[y][x]);
1880 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
1881 data2[y][x][3] = 255;
1885 #ifdef DUMPPARTICLEFONT
1886 Image_WriteTGABGRA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1888 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_BGRA, TEXF_PRECACHE, NULL);
1890 particletexture[tex_beam].s1 = 0;
1891 particletexture[tex_beam].t1 = 0;
1892 particletexture[tex_beam].s2 = 1;
1893 particletexture[tex_beam].t2 = 1;
1896 static void r_part_start(void)
1899 // generate particlepalette for convenience from the main one
1900 for (i = 0;i < 256;i++)
1901 particlepalette[i] = palette_rgb[i][0] * 65536 + palette_rgb[i][1] * 256 + palette_rgb[i][2];
1902 particletexturepool = R_AllocTexturePool();
1903 R_InitParticleTexture ();
1904 CL_Particles_LoadEffectInfo();
1907 static void r_part_shutdown(void)
1909 R_FreeTexturePool(&particletexturepool);
1912 static void r_part_newmap(void)
1914 CL_Particles_LoadEffectInfo();
1917 #define BATCHSIZE 256
1918 unsigned short particle_elements[BATCHSIZE*6];
1920 void R_Particles_Init (void)
1923 for (i = 0;i < BATCHSIZE;i++)
1925 particle_elements[i*6+0] = i*4+0;
1926 particle_elements[i*6+1] = i*4+1;
1927 particle_elements[i*6+2] = i*4+2;
1928 particle_elements[i*6+3] = i*4+0;
1929 particle_elements[i*6+4] = i*4+2;
1930 particle_elements[i*6+5] = i*4+3;
1933 Cvar_RegisterVariable(&r_drawparticles);
1934 Cvar_RegisterVariable(&r_drawparticles_drawdistance);
1935 Cvar_RegisterVariable(&r_drawdecals);
1936 Cvar_RegisterVariable(&r_drawdecals_drawdistance);
1937 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1940 void R_DrawDecal_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
1942 int surfacelistindex;
1944 float *v3f, *t2f, *c4f;
1945 particletexture_t *tex;
1946 float right[3], up[3], size, ca;
1947 float alphascale = (1.0f / 65536.0f) * cl_particles_alpha.value * r_refdef.view.colorscale;
1948 float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16];
1950 r_refdef.stats.decals += numsurfaces;
1951 R_Mesh_Matrix(&identitymatrix);
1952 R_Mesh_ResetTextureState();
1953 R_Mesh_VertexPointer(particle_vertex3f, 0, 0);
1954 R_Mesh_TexCoordPointer(0, 2, particle_texcoord2f, 0, 0);
1955 R_Mesh_ColorPointer(particle_color4f, 0, 0);
1956 R_SetupGenericShader(true);
1957 GL_DepthMask(false);
1958 GL_DepthRange(0, 1);
1959 GL_PolygonOffset(0, 0);
1961 GL_CullFace(GL_NONE);
1963 // generate all the vertices at once
1964 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
1966 d = cl.decals + surfacelist[surfacelistindex];
1969 c4f = particle_color4f + 16*surfacelistindex;
1970 ca = d->alpha * alphascale;
1971 if (r_refdef.fogenabled)
1972 ca *= FogPoint_World(d->org);
1973 Vector4Set(c4f, d->color[0] * ca, d->color[1] * ca, d->color[2] * ca, 1);
1974 Vector4Copy(c4f, c4f + 4);
1975 Vector4Copy(c4f, c4f + 8);
1976 Vector4Copy(c4f, c4f + 12);
1978 // calculate vertex positions
1979 size = d->size * cl_particles_size.value;
1980 VectorVectors(d->normal, right, up);
1981 VectorScale(right, size, right);
1982 VectorScale(up, size, up);
1983 v3f = particle_vertex3f + 12*surfacelistindex;
1984 v3f[ 0] = d->org[0] - right[0] - up[0];
1985 v3f[ 1] = d->org[1] - right[1] - up[1];
1986 v3f[ 2] = d->org[2] - right[2] - up[2];
1987 v3f[ 3] = d->org[0] - right[0] + up[0];
1988 v3f[ 4] = d->org[1] - right[1] + up[1];
1989 v3f[ 5] = d->org[2] - right[2] + up[2];
1990 v3f[ 6] = d->org[0] + right[0] + up[0];
1991 v3f[ 7] = d->org[1] + right[1] + up[1];
1992 v3f[ 8] = d->org[2] + right[2] + up[2];
1993 v3f[ 9] = d->org[0] + right[0] - up[0];
1994 v3f[10] = d->org[1] + right[1] - up[1];
1995 v3f[11] = d->org[2] + right[2] - up[2];
1997 // calculate texcoords
1998 tex = &particletexture[d->texnum];
1999 t2f = particle_texcoord2f + 8*surfacelistindex;
2000 t2f[0] = tex->s1;t2f[1] = tex->t2;
2001 t2f[2] = tex->s1;t2f[3] = tex->t1;
2002 t2f[4] = tex->s2;t2f[5] = tex->t1;
2003 t2f[6] = tex->s2;t2f[7] = tex->t2;
2006 // now render the decals all at once
2007 // (this assumes they all use one particle font texture!)
2008 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2009 R_Mesh_TexBind(0, R_GetTexture(particletexture[63].texture));
2010 GL_LockArrays(0, numsurfaces*4);
2011 R_Mesh_Draw(0, numsurfaces * 4, 0, numsurfaces * 2, NULL, particle_elements, 0, 0);
2012 GL_LockArrays(0, 0);
2015 void R_DrawDecals (void)
2023 frametime = bound(0, cl.time - cl.decals_updatetime, 1);
2024 cl.decals_updatetime = bound(cl.time - 1, cl.decals_updatetime + frametime, cl.time + 1);
2026 // LordHavoc: early out conditions
2027 if ((!cl.num_decals) || (!r_drawdecals.integer))
2030 decalfade = frametime * 256 / cl_decals_fadetime.value;
2031 drawdist2 = r_drawdecals_drawdistance.value * r_refdef.view.quality;
2032 drawdist2 = drawdist2*drawdist2;
2034 for (i = 0, decal = cl.decals;i < cl.num_decals;i++, decal++)
2036 if (!decal->typeindex)
2039 if (cl.time > decal->time2 + cl_decals_time.value)
2041 decal->alpha -= decalfade;
2042 if (decal->alpha <= 0)
2048 if (cl.entities[decal->owner].render.model == decal->ownermodel)
2050 Matrix4x4_Transform(&cl.entities[decal->owner].render.matrix, decal->relativeorigin, decal->org);
2051 Matrix4x4_Transform3x3(&cl.entities[decal->owner].render.matrix, decal->relativenormal, decal->normal);
2057 if (DotProduct(r_refdef.view.origin, decal->normal) > DotProduct(decal->org, decal->normal) && VectorDistance2(decal->org, r_refdef.view.origin) < drawdist2 * (decal->size * decal->size))
2058 R_MeshQueue_AddTransparent(decal->org, R_DrawDecal_TransparentCallback, NULL, i, NULL);
2061 decal->typeindex = 0;
2062 if (cl.free_decal > i)
2066 // reduce cl.num_decals if possible
2067 while (cl.num_decals > 0 && cl.decals[cl.num_decals - 1].typeindex == 0)
2070 if (cl.num_decals == cl.max_decals && cl.max_decals < ABSOLUTE_MAX_DECALS)
2072 decal_t *olddecals = cl.decals;
2073 cl.max_decals = min(cl.max_decals * 2, ABSOLUTE_MAX_DECALS);
2074 cl.decals = (decal_t *) Mem_Alloc(cls.levelmempool, cl.max_decals * sizeof(decal_t));
2075 memcpy(cl.decals, olddecals, cl.num_decals * sizeof(decal_t));
2076 Mem_Free(olddecals);
2080 void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2082 int surfacelistindex;
2083 int batchstart, batchcount;
2084 const particle_t *p;
2086 rtexture_t *texture;
2087 float *v3f, *t2f, *c4f;
2088 particletexture_t *tex;
2089 float up2[3], v[3], right[3], up[3], fog, ifog, size;
2090 float ambient[3], diffuse[3], diffusenormal[3];
2091 vec4_t colormultiplier;
2092 float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16];
2094 Vector4Set(colormultiplier, r_refdef.view.colorscale * (1.0 / 256.0f), r_refdef.view.colorscale * (1.0 / 256.0f), r_refdef.view.colorscale * (1.0 / 256.0f), cl_particles_alpha.value * (1.0 / 256.0f));
2096 r_refdef.stats.particles += numsurfaces;
2097 R_Mesh_Matrix(&identitymatrix);
2098 R_Mesh_ResetTextureState();
2099 R_Mesh_VertexPointer(particle_vertex3f, 0, 0);
2100 R_Mesh_TexCoordPointer(0, 2, particle_texcoord2f, 0, 0);
2101 R_Mesh_ColorPointer(particle_color4f, 0, 0);
2102 R_SetupGenericShader(true);
2103 GL_DepthMask(false);
2104 GL_DepthRange(0, 1);
2105 GL_PolygonOffset(0, 0);
2107 GL_CullFace(GL_NONE);
2109 // first generate all the vertices at once
2110 for (surfacelistindex = 0, v3f = particle_vertex3f, t2f = particle_texcoord2f, c4f = particle_color4f;surfacelistindex < numsurfaces;surfacelistindex++, v3f += 3*4, t2f += 2*4, c4f += 4*4)
2112 p = cl.particles + surfacelist[surfacelistindex];
2114 blendmode = particletype[p->typeindex].blendmode;
2116 c4f[0] = p->color[0] * colormultiplier[0];
2117 c4f[1] = p->color[1] * colormultiplier[1];
2118 c4f[2] = p->color[2] * colormultiplier[2];
2119 c4f[3] = p->alpha * colormultiplier[3];
2124 // additive and modulate can just fade out in fog (this is correct)
2125 if (r_refdef.fogenabled)
2126 c4f[3] *= FogPoint_World(p->org);
2127 // collapse alpha into color for these blends (so that the particlefont does not need alpha on most textures)
2134 // note: lighting is not cheap!
2135 if (particletype[p->typeindex].lighting)
2137 R_CompleteLightPoint(ambient, diffuse, diffusenormal, p->org, true);
2138 c4f[0] *= (ambient[0] + 0.5 * diffuse[0]);
2139 c4f[1] *= (ambient[1] + 0.5 * diffuse[1]);
2140 c4f[2] *= (ambient[2] + 0.5 * diffuse[2]);
2142 // mix in the fog color
2143 if (r_refdef.fogenabled)
2145 fog = FogPoint_World(p->org);
2147 c4f[0] = c4f[0] * fog + r_refdef.fogcolor[0] * ifog;
2148 c4f[1] = c4f[1] * fog + r_refdef.fogcolor[1] * ifog;
2149 c4f[2] = c4f[2] * fog + r_refdef.fogcolor[2] * ifog;
2153 // copy the color into the other three vertices
2154 Vector4Copy(c4f, c4f + 4);
2155 Vector4Copy(c4f, c4f + 8);
2156 Vector4Copy(c4f, c4f + 12);
2158 size = p->size * cl_particles_size.value;
2159 tex = &particletexture[p->texnum];
2160 switch(particletype[p->typeindex].orientation)
2162 case PARTICLE_BILLBOARD:
2163 VectorScale(r_refdef.view.left, -size, right);
2164 VectorScale(r_refdef.view.up, size, up);
2165 v3f[ 0] = p->org[0] - right[0] - up[0];
2166 v3f[ 1] = p->org[1] - right[1] - up[1];
2167 v3f[ 2] = p->org[2] - right[2] - up[2];
2168 v3f[ 3] = p->org[0] - right[0] + up[0];
2169 v3f[ 4] = p->org[1] - right[1] + up[1];
2170 v3f[ 5] = p->org[2] - right[2] + up[2];
2171 v3f[ 6] = p->org[0] + right[0] + up[0];
2172 v3f[ 7] = p->org[1] + right[1] + up[1];
2173 v3f[ 8] = p->org[2] + right[2] + up[2];
2174 v3f[ 9] = p->org[0] + right[0] - up[0];
2175 v3f[10] = p->org[1] + right[1] - up[1];
2176 v3f[11] = p->org[2] + right[2] - up[2];
2177 t2f[0] = tex->s1;t2f[1] = tex->t2;
2178 t2f[2] = tex->s1;t2f[3] = tex->t1;
2179 t2f[4] = tex->s2;t2f[5] = tex->t1;
2180 t2f[6] = tex->s2;t2f[7] = tex->t2;
2182 case PARTICLE_ORIENTED_DOUBLESIDED:
2183 VectorVectors(p->vel, right, up);
2184 VectorScale(right, size, right);
2185 VectorScale(up, size, up);
2186 v3f[ 0] = p->org[0] - right[0] - up[0];
2187 v3f[ 1] = p->org[1] - right[1] - up[1];
2188 v3f[ 2] = p->org[2] - right[2] - up[2];
2189 v3f[ 3] = p->org[0] - right[0] + up[0];
2190 v3f[ 4] = p->org[1] - right[1] + up[1];
2191 v3f[ 5] = p->org[2] - right[2] + up[2];
2192 v3f[ 6] = p->org[0] + right[0] + up[0];
2193 v3f[ 7] = p->org[1] + right[1] + up[1];
2194 v3f[ 8] = p->org[2] + right[2] + up[2];
2195 v3f[ 9] = p->org[0] + right[0] - up[0];
2196 v3f[10] = p->org[1] + right[1] - up[1];
2197 v3f[11] = p->org[2] + right[2] - up[2];
2198 t2f[0] = tex->s1;t2f[1] = tex->t2;
2199 t2f[2] = tex->s1;t2f[3] = tex->t1;
2200 t2f[4] = tex->s2;t2f[5] = tex->t1;
2201 t2f[6] = tex->s2;t2f[7] = tex->t2;
2203 case PARTICLE_SPARK:
2204 VectorMA(p->org, -0.04, p->vel, v);
2205 VectorMA(p->org, 0.04, p->vel, up2);
2206 R_CalcBeam_Vertex3f(v3f, v, up2, size);
2207 t2f[0] = tex->s1;t2f[1] = tex->t2;
2208 t2f[2] = tex->s1;t2f[3] = tex->t1;
2209 t2f[4] = tex->s2;t2f[5] = tex->t1;
2210 t2f[6] = tex->s2;t2f[7] = tex->t2;
2213 R_CalcBeam_Vertex3f(v3f, p->org, p->vel, size);
2214 VectorSubtract(p->vel, p->org, up);
2215 VectorNormalize(up);
2216 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
2217 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
2218 t2f[0] = 1;t2f[1] = v[0];
2219 t2f[2] = 0;t2f[3] = v[0];
2220 t2f[4] = 0;t2f[5] = v[1];
2221 t2f[6] = 1;t2f[7] = v[1];
2226 // now render batches of particles based on blendmode and texture
2229 GL_LockArrays(0, numsurfaces*4);
2232 for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
2234 p = cl.particles + surfacelist[surfacelistindex];
2236 if (blendmode != particletype[p->typeindex].blendmode)
2238 blendmode = particletype[p->typeindex].blendmode;
2242 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2245 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2248 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2252 if (texture != particletexture[p->texnum].texture)
2254 texture = particletexture[p->texnum].texture;
2255 R_Mesh_TexBind(0, R_GetTexture(texture));
2258 // iterate until we find a change in settings
2259 batchstart = surfacelistindex++;
2260 for (;surfacelistindex < numsurfaces;surfacelistindex++)
2262 p = cl.particles + surfacelist[surfacelistindex];
2263 if (blendmode != particletype[p->typeindex].blendmode || texture != particletexture[p->texnum].texture)
2267 batchcount = surfacelistindex - batchstart;
2268 R_Mesh_Draw(batchstart * 4, batchcount * 4, batchstart * 2, batchcount * 2, NULL, particle_elements, 0, 0);
2270 GL_LockArrays(0, 0);
2273 void R_DrawParticles (void)
2276 float minparticledist;
2278 float gravity, dvel, decalfade, frametime, f, dist, oldorg[3];
2284 frametime = bound(0, cl.time - cl.particles_updatetime, 1);
2285 cl.particles_updatetime = bound(cl.time - 1, cl.particles_updatetime + frametime, cl.time + 1);
2287 // LordHavoc: early out conditions
2288 if ((!cl.num_particles) || (!r_drawparticles.integer))
2291 minparticledist = DotProduct(r_refdef.view.origin, r_refdef.view.forward) + 4.0f;
2292 gravity = frametime * cl.movevars_gravity;
2293 dvel = 1+4*frametime;
2294 decalfade = frametime * 255 / cl_decals_fadetime.value;
2295 update = frametime > 0;
2296 drawdist2 = r_drawparticles_drawdistance.value * r_refdef.view.quality;
2297 drawdist2 = drawdist2*drawdist2;
2299 for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++)
2303 if (cl.free_particle > i)
2304 cl.free_particle = i;
2310 if (p->delayedspawn > cl.time)
2312 p->delayedspawn = 0;
2316 p->size += p->sizeincrease * frametime;
2317 p->alpha -= p->alphafade * frametime;
2319 if (p->alpha <= 0 || p->die <= cl.time)
2322 if (particletype[p->typeindex].orientation != PARTICLE_BEAM && frametime > 0)
2324 if (p->liquidfriction && (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK))
2326 if (p->typeindex == pt_blood)
2327 p->size += frametime * 8;
2329 p->vel[2] -= p->gravity * gravity;
2330 f = 1.0f - min(p->liquidfriction * frametime, 1);
2331 VectorScale(p->vel, f, p->vel);
2335 p->vel[2] -= p->gravity * gravity;
2338 f = 1.0f - min(p->airfriction * frametime, 1);
2339 VectorScale(p->vel, f, p->vel);
2343 VectorCopy(p->org, oldorg);
2344 VectorMA(p->org, frametime, p->vel, p->org);
2345 if (p->bounce && cl.time >= p->delayedcollisions)
2347 trace = CL_Move(oldorg, vec3_origin, vec3_origin, p->org, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | ((p->typeindex == pt_rain || p->typeindex == pt_snow) ? SUPERCONTENTS_LIQUIDSMASK : 0), true, false, &hitent, false);
2348 // if the trace started in or hit something of SUPERCONTENTS_NODROP
2349 // or if the trace hit something flagged as NOIMPACT
2350 // then remove the particle
2351 if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT || ((trace.startsupercontents | trace.hitsupercontents) & SUPERCONTENTS_NODROP) || (trace.startsupercontents & SUPERCONTENTS_SOLID))
2353 VectorCopy(trace.endpos, p->org);
2354 // react if the particle hit something
2355 if (trace.fraction < 1)
2357 VectorCopy(trace.endpos, p->org);
2358 if (p->typeindex == pt_blood)
2360 // blood - splash on solid
2361 if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS)
2363 R_Stain(p->org, 16, 64, 16, 16, (int)(p->alpha * p->size * (1.0f / 80.0f)), 64, 32, 32, (int)(p->alpha * p->size * (1.0f / 80.0f)));
2364 if (cl_decals.integer)
2366 // create a decal for the blood splat
2367 CL_SpawnDecalParticleForSurface(hitent, p->org, trace.plane.normal, p->color[0] * 65536 + p->color[1] * 256 + p->color[2], p->color[0] * 65536 + p->color[1] * 256 + p->color[2], tex_blooddecal[rand()&7], p->size * 2, p->alpha);
2371 else if (p->bounce < 0)
2373 // bounce -1 means remove on impact
2378 // anything else - bounce off solid
2379 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
2380 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
2381 if (DotProduct(p->vel, p->vel) < 0.03)
2382 VectorClear(p->vel);
2388 if (p->typeindex != pt_static)
2390 switch (p->typeindex)
2392 case pt_entityparticle:
2393 // particle that removes itself after one rendered frame
2400 a = CL_PointSuperContents(p->org);
2401 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
2405 a = CL_PointSuperContents(p->org);
2406 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
2410 a = CL_PointSuperContents(p->org);
2411 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
2415 if (cl.time > p->time2)
2418 p->time2 = cl.time + (rand() & 3) * 0.1;
2419 p->vel[0] = p->vel[0] * 0.9f + lhrandom(-32, 32);
2420 p->vel[1] = p->vel[0] * 0.9f + lhrandom(-32, 32);
2422 a = CL_PointSuperContents(p->org);
2423 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
2431 else if (p->delayedspawn)
2434 // don't render particles too close to the view (they chew fillrate)
2435 // also don't render particles behind the view (useless)
2436 // further checks to cull to the frustum would be too slow here
2437 switch(p->typeindex)
2440 // beams have no culling
2441 R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL);
2444 // anything else just has to be in front of the viewer and visible at this distance
2445 if (DotProduct(p->org, r_refdef.view.forward) >= minparticledist && VectorDistance2(p->org, r_refdef.view.origin) < drawdist2 * (p->size * p->size))
2446 R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL);
2453 if (cl.free_particle > i)
2454 cl.free_particle = i;
2457 // reduce cl.num_particles if possible
2458 while (cl.num_particles > 0 && cl.particles[cl.num_particles - 1].typeindex == 0)
2461 if (cl.num_particles == cl.max_particles && cl.max_particles < ABSOLUTE_MAX_PARTICLES)
2463 particle_t *oldparticles = cl.particles;
2464 cl.max_particles = min(cl.max_particles * 2, ABSOLUTE_MAX_PARTICLES);
2465 cl.particles = (particle_t *) Mem_Alloc(cls.levelmempool, cl.max_particles * sizeof(particle_t));
2466 memcpy(cl.particles, oldparticles, cl.num_particles * sizeof(particle_t));
2467 Mem_Free(oldparticles);