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 // must match ptype_t values
28 particletype_t particletype[pt_total] =
30 {PBLEND_INVALID, PARTICLE_INVALID, false}, //pt_dead (should never happen)
31 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
32 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
33 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
34 {PBLEND_ADD, PARTICLE_HBEAM, false}, //pt_beam
35 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
36 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
37 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
38 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
39 {PBLEND_INVMOD, PARTICLE_BILLBOARD, false}, //pt_blood
40 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
41 {PBLEND_INVMOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
42 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
45 #define PARTICLEEFFECT_UNDERWATER 1
46 #define PARTICLEEFFECT_NOTUNDERWATER 2
48 typedef struct particleeffectinfo_s
50 int effectnameindex; // which effect this belongs to
51 // PARTICLEEFFECT_* bits
53 // blood effects may spawn very few particles, so proper fraction-overflow
54 // handling is very important, this variable keeps track of the fraction
55 double particleaccumulator;
56 // the math is: countabsolute + requestedcount * countmultiplier * quality
57 // absolute number of particles to spawn, often used for decals
58 // (unaffected by quality and requestedcount)
60 // multiplier for the number of particles CL_ParticleEffect was told to
61 // spawn, most effects do not really have a count and hence use 1, so
62 // this is often the actual count to spawn, not merely a multiplier
63 float countmultiplier;
64 // if > 0 this causes the particle to spawn in an evenly spaced line from
65 // originmins to originmaxs (causing them to describe a trail, not a box)
67 // type of particle to spawn (defines some aspects of behavior)
69 // blending mode used on this particle type
71 // orientation of this particle type (BILLBOARD, SPARK, BEAM, etc)
72 porientation_t orientation;
73 // range of colors to choose from in hex RRGGBB (like HTML color tags),
74 // randomly interpolated at spawn
75 unsigned int color[2];
76 // a random texture is chosen in this range (note the second value is one
77 // past the last choosable, so for example 8,16 chooses any from 8 up and
79 // if start and end of the range are the same, no randomization is done
81 // range of size values randomly chosen when spawning, plus size increase over time
83 // range of alpha values randomly chosen when spawning, plus alpha fade
85 // how long the particle should live (note it is also removed if alpha drops to 0)
87 // how much gravity affects this particle (negative makes it fly up!)
89 // how much bounce the particle has when it hits a surface
90 // if negative the particle is removed on impact
92 // if in air this friction is applied
93 // if negative the particle accelerates
95 // if in liquid (water/slime/lava) this friction is applied
96 // if negative the particle accelerates
98 // these offsets are added to the values given to particleeffect(), and
99 // then an ellipsoid-shaped jitter is added as defined by these
100 // (they are the 3 radii)
102 // stretch velocity factor (used for sparks)
103 float originoffset[3];
104 float velocityoffset[3];
105 float originjitter[3];
106 float velocityjitter[3];
107 float velocitymultiplier;
108 // an effect can also spawn a dlight
109 float lightradiusstart;
110 float lightradiusfade;
113 qboolean lightshadow;
115 unsigned int staincolor[2]; // note: 0x808080 = neutral (particle's own color), these are modding factors for the particle's original color!
120 particleeffectinfo_t;
122 char particleeffectname[MAX_PARTICLEEFFECTNAME][64];
125 particleeffectinfo_t particleeffectinfo[MAX_PARTICLEEFFECTINFO];
127 static int particlepalette[256];
129 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b, // 0-7
130 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb, // 8-15
131 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b, // 16-23
132 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23, // 24-31
133 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767, // 32-39
134 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb, // 40-47
135 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07, // 48-55
136 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f, // 56-63
137 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000, // 64-71
138 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000, // 72-79
139 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307, // 80-87
140 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723, // 88-95
141 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b, // 96-103
142 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b, // 104-111
143 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733, // 112-119
144 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397, // 120-127
145 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b, // 128-135
146 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707, // 136-143
147 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b, // 144-151
148 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707, // 152-159
149 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353, // 160-167
150 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07, // 168-175
151 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f, // 176-183
152 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07, // 184-191
153 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307, // 192-199
154 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700, // 200-207
155 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f, // 208-215
156 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f, // 216-223
157 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b, // 224-231
158 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b, // 232-239
159 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000, // 240-247
160 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53 // 248-255
163 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
164 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
165 int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
167 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
169 // particletexture_t is a rectangle in the particlefonttexture
170 typedef struct particletexture_s
173 float s1, t1, s2, t2;
177 static rtexturepool_t *particletexturepool;
178 static rtexture_t *particlefonttexture;
179 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
180 skinframe_t *decalskinframe;
182 // texture numbers in particle font
183 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
184 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
185 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
186 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
187 static const int tex_rainsplash = 32;
188 static const int tex_particle = 63;
189 static const int tex_bubble = 62;
190 static const int tex_raindrop = 61;
191 static const int tex_beam = 60;
193 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1", "enables particle effects"};
194 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1", "multiplies number of particles"};
195 cvar_t cl_particles_alpha = {CVAR_SAVE, "cl_particles_alpha", "1", "multiplies opacity of particles"};
196 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1", "multiplies particle size"};
197 cvar_t cl_particles_quake = {CVAR_SAVE, "cl_particles_quake", "0", "makes particle effects look mostly like the ones in Quake"};
198 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1", "enables blood effects"};
199 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "1", "opacity of blood, does not affect decals"};
200 cvar_t cl_particles_blood_decal_alpha = {CVAR_SAVE, "cl_particles_blood_decal_alpha", "1", "opacity of blood decal"};
201 cvar_t cl_particles_blood_decal_scalemin = {CVAR_SAVE, "cl_particles_blood_decal_scalemin", "1.5", "minimal random scale of decal"};
202 cvar_t cl_particles_blood_decal_scalemax = {CVAR_SAVE, "cl_particles_blood_decal_scalemax", "2", "maximal random scale of decal"};
203 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1", "make certain quake particle() calls create blood effects instead"};
204 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1", "enables bulletimpact effects"};
205 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1", "enables sparks from explosions"};
206 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0", "enables polygonal shell from explosions"};
207 cvar_t cl_particles_rain = {CVAR_SAVE, "cl_particles_rain", "1", "enables rain effects"};
208 cvar_t cl_particles_snow = {CVAR_SAVE, "cl_particles_snow", "1", "enables snow effects"};
209 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1", "enables smoke (used by multiple effects)"};
210 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5", "smoke brightness"};
211 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55", "brightness fade per second"};
212 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1", "enables sparks (used by multiple effects)"};
213 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1", "enables bubbles (used by multiple effects)"};
214 cvar_t cl_particles_visculling = {CVAR_SAVE, "cl_particles_visculling", "0", "perform a costly check if each particle is visible before drawing"};
215 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "1", "enables decals (bullet holes, blood, etc)"};
216 cvar_t cl_decals_visculling = {CVAR_SAVE, "cl_decals_visculling", "1", "perform a very cheap check if each decal is visible before drawing"};
217 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "20", "how long before decals start to fade away"};
218 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "1", "how long decals take to fade away"};
219 cvar_t cl_decals_newsystem = {CVAR_SAVE, "cl_decals_newsystem", "0", "enables new advanced decal system"};
220 cvar_t cl_decals_newsystem_intensitymultiplier = {CVAR_SAVE, "cl_decals_newsystem_intensitymultiplier", "2", "boosts intensity of decals (because the distance fade can make them hard to see otherwise)"};
221 cvar_t cl_decals_models = {CVAR_SAVE, "cl_decals_models", "0", "enables decals on animated models (if newsystem is also 1)"};
222 cvar_t cl_decals_bias = {CVAR_SAVE, "cl_decals_bias", "0.125", "distance to bias decals from surface to prevent depth fighting"};
223 cvar_t cl_decals_max = {CVAR_SAVE, "cl_decals_max", "4096", "maximum number of decals allowed to exist in the world at once"};
226 void CL_Particles_ParseEffectInfo(const char *textstart, const char *textend, const char *filename)
232 particleeffectinfo_t *info = NULL;
233 const char *text = textstart;
235 effectinfoindex = -1;
236 for (linenumber = 1;;linenumber++)
239 for (arrayindex = 0;arrayindex < 16;arrayindex++)
240 argv[arrayindex][0] = 0;
243 if (!COM_ParseToken_Simple(&text, true, false))
245 if (!strcmp(com_token, "\n"))
249 strlcpy(argv[argc], com_token, sizeof(argv[argc]));
255 #define checkparms(n) if (argc != (n)) {Con_Printf("%s:%i: error while parsing: %s given %i parameters, should be %i parameters\n", filename, linenumber, argv[0], argc, (n));break;}
256 #define readints(array, n) checkparms(n+1);for (arrayindex = 0;arrayindex < argc - 1;arrayindex++) array[arrayindex] = strtol(argv[1+arrayindex], NULL, 0)
257 #define readfloats(array, n) checkparms(n+1);for (arrayindex = 0;arrayindex < argc - 1;arrayindex++) array[arrayindex] = atof(argv[1+arrayindex])
258 #define readint(var) checkparms(2);var = strtol(argv[1], NULL, 0)
259 #define readfloat(var) checkparms(2);var = atof(argv[1])
260 #define readbool(var) checkparms(2);var = strtol(argv[1], NULL, 0) != 0
261 if (!strcmp(argv[0], "effect"))
266 if (effectinfoindex >= MAX_PARTICLEEFFECTINFO)
268 Con_Printf("%s:%i: too many effects!\n", filename, linenumber);
271 for (effectnameindex = 1;effectnameindex < MAX_PARTICLEEFFECTNAME;effectnameindex++)
273 if (particleeffectname[effectnameindex][0])
275 if (!strcmp(particleeffectname[effectnameindex], argv[1]))
280 strlcpy(particleeffectname[effectnameindex], argv[1], sizeof(particleeffectname[effectnameindex]));
284 // if we run out of names, abort
285 if (effectnameindex == MAX_PARTICLEEFFECTNAME)
287 Con_Printf("%s:%i: too many effects!\n", filename, linenumber);
290 info = particleeffectinfo + effectinfoindex;
291 info->effectnameindex = effectnameindex;
292 info->particletype = pt_alphastatic;
293 info->blendmode = particletype[info->particletype].blendmode;
294 info->orientation = particletype[info->particletype].orientation;
295 info->tex[0] = tex_particle;
296 info->tex[1] = tex_particle;
297 info->color[0] = 0xFFFFFF;
298 info->color[1] = 0xFFFFFF;
302 info->alpha[1] = 256;
303 info->alpha[2] = 256;
304 info->time[0] = 9999;
305 info->time[1] = 9999;
306 VectorSet(info->lightcolor, 1, 1, 1);
307 info->lightshadow = true;
308 info->lighttime = 9999;
309 info->stretchfactor = 1;
310 info->staincolor[0] = (unsigned int)-1;
311 info->staincolor[1] = (unsigned int)-1;
312 info->staintex[0] = -1;
313 info->staintex[1] = -1;
314 info->stainalpha[0] = 1;
315 info->stainalpha[1] = 1;
316 info->stainsize[0] = 2;
317 info->stainsize[1] = 2;
319 else if (info == NULL)
321 Con_Printf("%s:%i: command %s encountered before effect\n", filename, linenumber, argv[0]);
324 else if (!strcmp(argv[0], "countabsolute")) {readfloat(info->countabsolute);}
325 else if (!strcmp(argv[0], "count")) {readfloat(info->countmultiplier);}
326 else if (!strcmp(argv[0], "type"))
329 if (!strcmp(argv[1], "alphastatic")) info->particletype = pt_alphastatic;
330 else if (!strcmp(argv[1], "static")) info->particletype = pt_static;
331 else if (!strcmp(argv[1], "spark")) info->particletype = pt_spark;
332 else if (!strcmp(argv[1], "beam")) info->particletype = pt_beam;
333 else if (!strcmp(argv[1], "rain")) info->particletype = pt_rain;
334 else if (!strcmp(argv[1], "raindecal")) info->particletype = pt_raindecal;
335 else if (!strcmp(argv[1], "snow")) info->particletype = pt_snow;
336 else if (!strcmp(argv[1], "bubble")) info->particletype = pt_bubble;
337 else if (!strcmp(argv[1], "blood")) {info->particletype = pt_blood;info->gravity = 1;}
338 else if (!strcmp(argv[1], "smoke")) info->particletype = pt_smoke;
339 else if (!strcmp(argv[1], "decal")) info->particletype = pt_decal;
340 else if (!strcmp(argv[1], "entityparticle")) info->particletype = pt_entityparticle;
341 else Con_Printf("%s:%i: unrecognized particle type %s\n", filename, linenumber, argv[1]);
342 info->blendmode = particletype[info->particletype].blendmode;
343 info->orientation = particletype[info->particletype].orientation;
345 else if (!strcmp(argv[0], "blend"))
348 if (!strcmp(argv[1], "alpha")) info->blendmode = PBLEND_ALPHA;
349 else if (!strcmp(argv[1], "add")) info->blendmode = PBLEND_ADD;
350 else if (!strcmp(argv[1], "invmod")) info->blendmode = PBLEND_INVMOD;
351 else Con_Printf("%s:%i: unrecognized blendmode %s\n", filename, linenumber, argv[1]);
353 else if (!strcmp(argv[0], "orientation"))
356 if (!strcmp(argv[1], "billboard")) info->orientation = PARTICLE_BILLBOARD;
357 else if (!strcmp(argv[1], "spark")) info->orientation = PARTICLE_SPARK;
358 else if (!strcmp(argv[1], "oriented")) info->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
359 else if (!strcmp(argv[1], "beam")) info->orientation = PARTICLE_HBEAM;
360 else Con_Printf("%s:%i: unrecognized orientation %s\n", filename, linenumber, argv[1]);
362 else if (!strcmp(argv[0], "color")) {readints(info->color, 2);}
363 else if (!strcmp(argv[0], "tex")) {readints(info->tex, 2);}
364 else if (!strcmp(argv[0], "size")) {readfloats(info->size, 2);}
365 else if (!strcmp(argv[0], "sizeincrease")) {readfloat(info->size[2]);}
366 else if (!strcmp(argv[0], "alpha")) {readfloats(info->alpha, 3);}
367 else if (!strcmp(argv[0], "time")) {readfloats(info->time, 2);}
368 else if (!strcmp(argv[0], "gravity")) {readfloat(info->gravity);}
369 else if (!strcmp(argv[0], "bounce")) {readfloat(info->bounce);}
370 else if (!strcmp(argv[0], "airfriction")) {readfloat(info->airfriction);}
371 else if (!strcmp(argv[0], "liquidfriction")) {readfloat(info->liquidfriction);}
372 else if (!strcmp(argv[0], "originoffset")) {readfloats(info->originoffset, 3);}
373 else if (!strcmp(argv[0], "velocityoffset")) {readfloats(info->velocityoffset, 3);}
374 else if (!strcmp(argv[0], "originjitter")) {readfloats(info->originjitter, 3);}
375 else if (!strcmp(argv[0], "velocityjitter")) {readfloats(info->velocityjitter, 3);}
376 else if (!strcmp(argv[0], "velocitymultiplier")) {readfloat(info->velocitymultiplier);}
377 else if (!strcmp(argv[0], "lightradius")) {readfloat(info->lightradiusstart);}
378 else if (!strcmp(argv[0], "lightradiusfade")) {readfloat(info->lightradiusfade);}
379 else if (!strcmp(argv[0], "lighttime")) {readfloat(info->lighttime);}
380 else if (!strcmp(argv[0], "lightcolor")) {readfloats(info->lightcolor, 3);}
381 else if (!strcmp(argv[0], "lightshadow")) {readbool(info->lightshadow);}
382 else if (!strcmp(argv[0], "lightcubemapnum")) {readint(info->lightcubemapnum);}
383 else if (!strcmp(argv[0], "underwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_UNDERWATER;}
384 else if (!strcmp(argv[0], "notunderwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_NOTUNDERWATER;}
385 else if (!strcmp(argv[0], "trailspacing")) {readfloat(info->trailspacing);if (info->trailspacing > 0) info->countmultiplier = 1.0f / info->trailspacing;}
386 else if (!strcmp(argv[0], "stretchfactor")) {readfloat(info->stretchfactor);}
387 else if (!strcmp(argv[0], "staincolor")) {readints(info->staincolor, 2);}
388 else if (!strcmp(argv[0], "stainalpha")) {readfloats(info->stainalpha, 2);}
389 else if (!strcmp(argv[0], "stainsize")) {readfloats(info->stainsize, 2);}
390 else if (!strcmp(argv[0], "staintex")) {readints(info->staintex, 2);}
391 else if (!strcmp(argv[0], "stainless")) {info->staintex[0] = -2; info->staincolor[0] = (unsigned int)-1; info->staincolor[1] = (unsigned int)-1; info->stainalpha[0] = 1; info->stainalpha[1] = 1; info->stainsize[0] = 2; info->stainsize[1] = 2; }
393 Con_Printf("%s:%i: skipping unknown command %s\n", filename, linenumber, argv[0]);
402 int CL_ParticleEffectIndexForName(const char *name)
405 for (i = 1;i < MAX_PARTICLEEFFECTNAME && particleeffectname[i][0];i++)
406 if (!strcmp(particleeffectname[i], name))
411 const char *CL_ParticleEffectNameForIndex(int i)
413 if (i < 1 || i >= MAX_PARTICLEEFFECTNAME)
415 return particleeffectname[i];
418 // MUST match effectnameindex_t in client.h
419 static const char *standardeffectnames[EFFECT_TOTAL] =
443 "TE_TEI_BIGEXPLOSION",
459 void CL_Particles_LoadEffectInfo(void)
463 unsigned char *filedata;
464 fs_offset_t filesize;
465 char filename[MAX_QPATH];
466 memset(particleeffectinfo, 0, sizeof(particleeffectinfo));
467 memset(particleeffectname, 0, sizeof(particleeffectname));
468 for (i = 0;i < EFFECT_TOTAL;i++)
469 strlcpy(particleeffectname[i], standardeffectnames[i], sizeof(particleeffectname[i]));
470 for (filepass = 0;;filepass++)
473 dpsnprintf(filename, sizeof(filename), "effectinfo.txt");
474 else if (filepass == 1)
475 dpsnprintf(filename, sizeof(filename), "maps/%s_effectinfo.txt", cl.levelname);
478 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
481 CL_Particles_ParseEffectInfo((const char *)filedata, (const char *)filedata + filesize, filename);
491 void CL_ReadPointFile_f (void);
492 void CL_Particles_Init (void)
494 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)");
495 Cmd_AddCommand ("cl_particles_reloadeffects", CL_Particles_LoadEffectInfo, "reloads effectinfo.txt and maps/levelname_effectinfo.txt (where levelname is the current map)");
497 Cvar_RegisterVariable (&cl_particles);
498 Cvar_RegisterVariable (&cl_particles_quality);
499 Cvar_RegisterVariable (&cl_particles_alpha);
500 Cvar_RegisterVariable (&cl_particles_size);
501 Cvar_RegisterVariable (&cl_particles_quake);
502 Cvar_RegisterVariable (&cl_particles_blood);
503 Cvar_RegisterVariable (&cl_particles_blood_alpha);
504 Cvar_RegisterVariable (&cl_particles_blood_decal_alpha);
505 Cvar_RegisterVariable (&cl_particles_blood_decal_scalemin);
506 Cvar_RegisterVariable (&cl_particles_blood_decal_scalemax);
507 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
508 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
509 Cvar_RegisterVariable (&cl_particles_explosions_shell);
510 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
511 Cvar_RegisterVariable (&cl_particles_rain);
512 Cvar_RegisterVariable (&cl_particles_snow);
513 Cvar_RegisterVariable (&cl_particles_smoke);
514 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
515 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
516 Cvar_RegisterVariable (&cl_particles_sparks);
517 Cvar_RegisterVariable (&cl_particles_bubbles);
518 Cvar_RegisterVariable (&cl_particles_visculling);
519 Cvar_RegisterVariable (&cl_decals);
520 Cvar_RegisterVariable (&cl_decals_visculling);
521 Cvar_RegisterVariable (&cl_decals_time);
522 Cvar_RegisterVariable (&cl_decals_fadetime);
523 Cvar_RegisterVariable (&cl_decals_newsystem);
524 Cvar_RegisterVariable (&cl_decals_newsystem_intensitymultiplier);
525 Cvar_RegisterVariable (&cl_decals_models);
526 Cvar_RegisterVariable (&cl_decals_bias);
527 Cvar_RegisterVariable (&cl_decals_max);
530 void CL_Particles_Shutdown (void)
534 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha);
535 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2);
537 // list of all 26 parameters:
538 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
539 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
540 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
541 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_*BEAM)
542 // palpha - opacity of particle as 0-255 (can be more than 255)
543 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
544 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
545 // pgravity - how much effect gravity has on the particle (0-1)
546 // 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
547 // px,py,pz - starting origin of particle
548 // pvx,pvy,pvz - starting velocity of particle
549 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
550 // blendmode - one of the PBLEND_ values
551 // orientation - one of the PARTICLE_ values
552 // staincolor1, staincolor2: minimum and maximum ranges of stain color, randomly interpolated to decide stain color (-1 to use none)
553 // staintex: any of the tex_ values such as tex_smoke[rand()&7] or tex_particle (-1 to use none)
554 // stainalpha: opacity of the stain as factor for alpha
555 // stainsize: size of the stain as factor for palpha
556 particle_t *CL_NewParticle(const vec3_t sortorigin, 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, float stretch, pblend_t blendmode, porientation_t orientation, int staincolor1, int staincolor2, int staintex, float stainalpha, float stainsize)
561 if (!cl_particles.integer)
563 for (;cl.free_particle < cl.max_particles && cl.particles[cl.free_particle].typeindex;cl.free_particle++);
564 if (cl.free_particle >= cl.max_particles)
567 lifetime = palpha / min(1, palphafade);
568 part = &cl.particles[cl.free_particle++];
569 if (cl.num_particles < cl.free_particle)
570 cl.num_particles = cl.free_particle;
571 memset(part, 0, sizeof(*part));
572 VectorCopy(sortorigin, part->sortorigin);
573 part->typeindex = ptypeindex;
574 part->blendmode = blendmode;
575 if(orientation == PARTICLE_HBEAM || orientation == PARTICLE_VBEAM)
577 particletexture_t *tex = &particletexture[ptex];
578 if(tex->t1 == 0 && tex->t2 == 1) // full height of texture?
579 part->orientation = PARTICLE_VBEAM;
581 part->orientation = PARTICLE_HBEAM;
584 part->orientation = orientation;
585 l2 = (int)lhrandom(0.5, 256.5);
587 part->color[0] = ((((pcolor1 >> 16) & 0xFF) * l1 + ((pcolor2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
588 part->color[1] = ((((pcolor1 >> 8) & 0xFF) * l1 + ((pcolor2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
589 part->color[2] = ((((pcolor1 >> 0) & 0xFF) * l1 + ((pcolor2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
590 part->staintexnum = staintex;
591 if(staincolor1 >= 0 && staincolor2 >= 0)
593 l2 = (int)lhrandom(0.5, 256.5);
595 if(blendmode == PBLEND_INVMOD)
597 r = ((((staincolor1 >> 16) & 0xFF) * l1 + ((staincolor2 >> 16) & 0xFF) * l2) * (255 - part->color[0])) / 0x8000; // staincolor 0x808080 keeps color invariant
598 g = ((((staincolor1 >> 8) & 0xFF) * l1 + ((staincolor2 >> 8) & 0xFF) * l2) * (255 - part->color[1])) / 0x8000;
599 b = ((((staincolor1 >> 0) & 0xFF) * l1 + ((staincolor2 >> 0) & 0xFF) * l2) * (255 - part->color[2])) / 0x8000;
603 r = ((((staincolor1 >> 16) & 0xFF) * l1 + ((staincolor2 >> 16) & 0xFF) * l2) * part->color[0]) / 0x8000; // staincolor 0x808080 keeps color invariant
604 g = ((((staincolor1 >> 8) & 0xFF) * l1 + ((staincolor2 >> 8) & 0xFF) * l2) * part->color[1]) / 0x8000;
605 b = ((((staincolor1 >> 0) & 0xFF) * l1 + ((staincolor2 >> 0) & 0xFF) * l2) * part->color[2]) / 0x8000;
607 if(r > 0xFF) r = 0xFF;
608 if(g > 0xFF) g = 0xFF;
609 if(b > 0xFF) b = 0xFF;
613 r = part->color[0]; // -1 is shorthand for stain = particle color
617 part->staincolor[0] = r;
618 part->staincolor[1] = g;
619 part->staincolor[2] = b;
620 part->stainalpha = palpha * stainalpha / 256;
621 part->stainsize = psize * stainsize;
624 part->sizeincrease = psizeincrease;
625 part->alpha = palpha;
626 part->alphafade = palphafade;
627 part->gravity = pgravity;
628 part->bounce = pbounce;
629 part->stretch = stretch;
631 part->org[0] = px + originjitter * v[0];
632 part->org[1] = py + originjitter * v[1];
633 part->org[2] = pz + originjitter * v[2];
634 part->vel[0] = pvx + velocityjitter * v[0];
635 part->vel[1] = pvy + velocityjitter * v[1];
636 part->vel[2] = pvz + velocityjitter * v[2];
638 part->airfriction = pairfriction;
639 part->liquidfriction = pliquidfriction;
640 part->die = cl.time + lifetime;
641 // part->delayedcollisions = 0;
642 part->qualityreduction = pqualityreduction;
643 // if it is rain or snow, trace ahead and shut off collisions until an actual collision event needs to occur to improve performance
644 if (part->typeindex == pt_rain)
648 float lifetime = part->die - cl.time;
651 // turn raindrop into simple spark and create delayedspawn splash effect
652 part->typeindex = pt_spark;
654 VectorMA(part->org, lifetime, part->vel, endvec);
655 trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK, true, false, NULL, false);
656 part->die = cl.time + lifetime * trace.fraction;
657 part2 = CL_NewParticle(endvec, 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, 1, PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, -1, -1, -1, 1, 1);
660 part2->delayedspawn = part->die;
661 part2->die += part->die - cl.time;
662 for (i = rand() & 7;i < 10;i++)
664 part2 = CL_NewParticle(endvec, 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, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1, 1, 1);
667 part2->delayedspawn = part->die;
668 part2->die += part->die - cl.time;
674 else if (part->bounce != 0 && part->gravity == 0 && part->typeindex != pt_snow)
676 float lifetime = part->alpha / (part->alphafade ? part->alphafade : 1);
679 VectorMA(part->org, lifetime, part->vel, endvec);
680 trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY, true, false, NULL, false);
681 part->delayedcollisions = cl.time + lifetime * trace.fraction - 0.1;
688 static void CL_ImmediateBloodStain(particle_t *part)
693 // blood creates a splash at spawn, not just at impact, this makes monsters bloody where they are shot
694 if (part->staintexnum >= 0 && cl_decals_newsystem.integer && cl_decals.integer)
696 VectorCopy(part->vel, v);
698 staintex = part->staintexnum;
699 R_DecalSystem_SplatEntities(part->org, v, 1-part->staincolor[0]*(1.0f/255.0f), 1-part->staincolor[1]*(1.0f/255.0f), 1-part->staincolor[2]*(1.0f/255.0f), part->stainalpha*(1.0f/255.0f), particletexture[staintex].s1, particletexture[staintex].t1, particletexture[staintex].s2, particletexture[staintex].t2, part->stainsize);
702 // blood creates a splash at spawn, not just at impact, this makes monsters bloody where they are shot
703 if (part->typeindex == pt_blood && cl_decals_newsystem.integer && cl_decals.integer)
705 VectorCopy(part->vel, v);
707 staintex = tex_blooddecal[rand()&7];
708 R_DecalSystem_SplatEntities(part->org, v, part->color[0]*(1.0f/255.0f), part->color[1]*(1.0f/255.0f), part->color[2]*(1.0f/255.0f), part->alpha*(1.0f/255.0f), particletexture[staintex].s1, particletexture[staintex].t1, particletexture[staintex].s2, particletexture[staintex].t2, part->size * 2);
712 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
716 entity_render_t *ent = &cl.entities[hitent].render;
717 unsigned char color[3];
718 if (!cl_decals.integer)
720 if (!ent->allowdecals)
723 l2 = (int)lhrandom(0.5, 256.5);
725 color[0] = ((((color1 >> 16) & 0xFF) * l1 + ((color2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
726 color[1] = ((((color1 >> 8) & 0xFF) * l1 + ((color2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
727 color[2] = ((((color1 >> 0) & 0xFF) * l1 + ((color2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
729 if (cl_decals_newsystem.integer)
731 R_DecalSystem_SplatEntities(org, normal, color[0]*(1.0f/255.0f), color[1]*(1.0f/255.0f), color[2]*(1.0f/255.0f), alpha*(1.0f/255.0f), particletexture[texnum].s1, particletexture[texnum].t1, particletexture[texnum].s2, particletexture[texnum].t2, size);
735 for (;cl.free_decal < cl.max_decals && cl.decals[cl.free_decal].typeindex;cl.free_decal++);
736 if (cl.free_decal >= cl.max_decals)
738 decal = &cl.decals[cl.free_decal++];
739 if (cl.num_decals < cl.free_decal)
740 cl.num_decals = cl.free_decal;
741 memset(decal, 0, sizeof(*decal));
742 decal->decalsequence = cl.decalsequence++;
743 decal->typeindex = pt_decal;
744 decal->texnum = texnum;
745 VectorMA(org, cl_decals_bias.value, normal, decal->org);
746 VectorCopy(normal, decal->normal);
748 decal->alpha = alpha;
749 decal->time2 = cl.time;
750 decal->color[0] = color[0];
751 decal->color[1] = color[1];
752 decal->color[2] = color[2];
753 decal->owner = hitent;
754 decal->clusterindex = -1000; // no vis culling unless we're sure
757 // these relative things are only used to regenerate p->org and p->vel if decal->owner is not world (0)
758 decal->ownermodel = cl.entities[decal->owner].render.model;
759 Matrix4x4_Transform(&cl.entities[decal->owner].render.inversematrix, org, decal->relativeorigin);
760 Matrix4x4_Transform3x3(&cl.entities[decal->owner].render.inversematrix, normal, decal->relativenormal);
764 if(r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.PointInLeaf)
766 mleaf_t *leaf = r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, decal->org);
768 decal->clusterindex = leaf->clusterindex;
773 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
776 float bestfrac, bestorg[3], bestnormal[3];
778 int besthitent = 0, hitent;
781 for (i = 0;i < 32;i++)
784 VectorMA(org, maxdist, org2, org2);
785 trace = CL_TraceLine(org, org2, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, true, false, &hitent, false);
786 // take the closest trace result that doesn't end up hitting a NOMARKS
787 // surface (sky for example)
788 if (bestfrac > trace.fraction && !(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
790 bestfrac = trace.fraction;
792 VectorCopy(trace.endpos, bestorg);
793 VectorCopy(trace.plane.normal, bestnormal);
797 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
800 static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount);
801 static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount);
802 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)
805 matrix4x4_t tempmatrix;
807 VectorLerp(originmins, 0.5, originmaxs, center);
808 Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
809 if (effectnameindex == EFFECT_SVC_PARTICLE)
811 if (cl_particles.integer)
813 // bloodhack checks if this effect's color matches regular or lightning blood and if so spawns a blood effect instead
815 CL_ParticleExplosion(center);
816 else if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer && (palettecolor == 73 || palettecolor == 225))
817 CL_ParticleEffect(EFFECT_TE_BLOOD, count / 2.0f, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
820 count *= cl_particles_quality.value;
821 for (;count > 0;count--)
823 int k = particlepalette[(palettecolor & ~7) + (rand()&7)];
824 CL_NewParticle(center, 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), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
829 else if (effectnameindex == EFFECT_TE_WIZSPIKE)
830 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 30*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 20);
831 else if (effectnameindex == EFFECT_TE_KNIGHTSPIKE)
832 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 226);
833 else if (effectnameindex == EFFECT_TE_SPIKE)
835 if (cl_particles_bulletimpacts.integer)
837 if (cl_particles_quake.integer)
839 if (cl_particles_smoke.integer)
840 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
844 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
845 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
846 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
850 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
851 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
853 else if (effectnameindex == EFFECT_TE_SPIKEQUAD)
855 if (cl_particles_bulletimpacts.integer)
857 if (cl_particles_quake.integer)
859 if (cl_particles_smoke.integer)
860 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
864 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
865 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
866 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
870 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
871 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
872 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);
874 else if (effectnameindex == EFFECT_TE_SUPERSPIKE)
876 if (cl_particles_bulletimpacts.integer)
878 if (cl_particles_quake.integer)
880 if (cl_particles_smoke.integer)
881 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
885 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
886 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
887 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
891 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
892 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
894 else if (effectnameindex == EFFECT_TE_SUPERSPIKEQUAD)
896 if (cl_particles_bulletimpacts.integer)
898 if (cl_particles_quake.integer)
900 if (cl_particles_smoke.integer)
901 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
905 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
906 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
907 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
911 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
912 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
913 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);
915 else if (effectnameindex == EFFECT_TE_BLOOD)
917 if (!cl_particles_blood.integer)
919 if (cl_particles_quake.integer)
920 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 2*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 73);
923 static double bloodaccumulator = 0;
924 qboolean immediatebloodstain = true;
925 //CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD);
926 bloodaccumulator += count * 0.333 * cl_particles_quality.value;
927 for (;bloodaccumulator > 0;bloodaccumulator--)
929 part = CL_NewParticle(center, pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, cl_particles_blood_alpha.value * 768, cl_particles_blood_alpha.value * 384, 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, 64, true, 0, 1, PBLEND_INVMOD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
930 if (immediatebloodstain && part)
932 immediatebloodstain = false;
933 CL_ImmediateBloodStain(part);
938 else if (effectnameindex == EFFECT_TE_SPARK)
939 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, count);
940 else if (effectnameindex == EFFECT_TE_PLASMABURN)
942 // plasma scorch mark
943 R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
944 CL_SpawnDecalParticleForPoint(center, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
945 CL_AllocLightFlash(NULL, &tempmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
947 else if (effectnameindex == EFFECT_TE_GUNSHOT)
949 if (cl_particles_bulletimpacts.integer)
951 if (cl_particles_quake.integer)
952 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
955 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
956 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
957 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
961 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
962 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
964 else if (effectnameindex == EFFECT_TE_GUNSHOTQUAD)
966 if (cl_particles_bulletimpacts.integer)
968 if (cl_particles_quake.integer)
969 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
972 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
973 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
974 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
978 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
979 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
980 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);
982 else if (effectnameindex == EFFECT_TE_EXPLOSION)
984 CL_ParticleExplosion(center);
985 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);
987 else if (effectnameindex == EFFECT_TE_EXPLOSIONQUAD)
989 CL_ParticleExplosion(center);
990 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);
992 else if (effectnameindex == EFFECT_TE_TAREXPLOSION)
994 if (cl_particles_quake.integer)
997 for (i = 0;i < 1024 * cl_particles_quality.value;i++)
1000 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1002 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1006 CL_ParticleExplosion(center);
1007 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);
1009 else if (effectnameindex == EFFECT_TE_SMALLFLASH)
1010 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);
1011 else if (effectnameindex == EFFECT_TE_FLAMEJET)
1013 count *= cl_particles_quality.value;
1015 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1017 else if (effectnameindex == EFFECT_TE_LAVASPLASH)
1019 float i, j, inc, vel;
1022 inc = 8 / cl_particles_quality.value;
1023 for (i = -128;i < 128;i += inc)
1025 for (j = -128;j < 128;j += inc)
1027 dir[0] = j + lhrandom(0, inc);
1028 dir[1] = i + lhrandom(0, inc);
1030 org[0] = center[0] + dir[0];
1031 org[1] = center[1] + dir[1];
1032 org[2] = center[2] + lhrandom(0, 64);
1033 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1034 CL_NewParticle(center, 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), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1038 else if (effectnameindex == EFFECT_TE_TELEPORT)
1040 float i, j, k, inc, vel;
1043 if (cl_particles_quake.integer)
1044 inc = 4 / cl_particles_quality.value;
1046 inc = 8 / cl_particles_quality.value;
1047 for (i = -16;i < 16;i += inc)
1049 for (j = -16;j < 16;j += inc)
1051 for (k = -24;k < 32;k += inc)
1053 VectorSet(dir, i*8, j*8, k*8);
1054 VectorNormalize(dir);
1055 vel = lhrandom(50, 113);
1056 if (cl_particles_quake.integer)
1057 CL_NewParticle(center, 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), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1059 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1063 if (!cl_particles_quake.integer)
1064 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1065 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);
1067 else if (effectnameindex == EFFECT_TE_TEI_G3)
1068 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1, 1, 1);
1069 else if (effectnameindex == EFFECT_TE_TEI_SMOKE)
1071 if (cl_particles_smoke.integer)
1073 count *= 0.25f * cl_particles_quality.value;
1075 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1078 else if (effectnameindex == EFFECT_TE_TEI_BIGEXPLOSION)
1080 CL_ParticleExplosion(center);
1081 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);
1083 else if (effectnameindex == EFFECT_TE_TEI_PLASMAHIT)
1086 R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
1087 CL_SpawnDecalParticleForPoint(center, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1088 if (cl_particles_smoke.integer)
1089 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1090 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1091 if (cl_particles_sparks.integer)
1092 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1093 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1, 1, 1);
1094 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);
1096 else if (effectnameindex == EFFECT_EF_FLAME)
1098 count *= 300 * cl_particles_quality.value;
1100 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1101 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);
1103 else if (effectnameindex == EFFECT_EF_STARDUST)
1105 count *= 200 * cl_particles_quality.value;
1107 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1108 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);
1110 else if (!strncmp(particleeffectname[effectnameindex], "TR_", 3))
1114 int smoke, blood, bubbles, r, color;
1116 if (spawndlight && r_refdef.scene.numlights < MAX_DLIGHTS)
1119 Vector4Set(light, 0, 0, 0, 0);
1121 if (effectnameindex == EFFECT_TR_ROCKET)
1122 Vector4Set(light, 3.0f, 1.5f, 0.5f, 200);
1123 else if (effectnameindex == EFFECT_TR_VORESPIKE)
1125 if (gamemode == GAME_PRYDON && !cl_particles_quake.integer)
1126 Vector4Set(light, 0.3f, 0.6f, 1.2f, 100);
1128 Vector4Set(light, 1.2f, 0.5f, 1.0f, 200);
1130 else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
1131 Vector4Set(light, 0.75f, 1.5f, 3.0f, 200);
1135 matrix4x4_t tempmatrix;
1136 Matrix4x4_CreateFromQuakeEntity(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, light[3]);
1137 R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &tempmatrix, light, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1138 r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
1142 if (!spawnparticles)
1145 if (originmaxs[0] == originmins[0] && originmaxs[1] == originmins[1] && originmaxs[2] == originmins[2])
1148 VectorSubtract(originmaxs, originmins, dir);
1149 len = VectorNormalizeLength(dir);
1152 dec = -ent->persistent.trail_time;
1153 ent->persistent.trail_time += len;
1154 if (ent->persistent.trail_time < 0.01f)
1157 // if we skip out, leave it reset
1158 ent->persistent.trail_time = 0.0f;
1163 // advance into this frame to reach the first puff location
1164 VectorMA(originmins, dec, dir, pos);
1167 smoke = cl_particles.integer && cl_particles_smoke.integer;
1168 blood = cl_particles.integer && cl_particles_blood.integer;
1169 bubbles = cl_particles.integer && cl_particles_bubbles.integer && !cl_particles_quake.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1170 qd = 1.0f / cl_particles_quality.value;
1177 if (effectnameindex == EFFECT_TR_BLOOD)
1179 if (cl_particles_quake.integer)
1181 color = particlepalette[67 + (rand()&3)];
1182 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1187 CL_NewParticle(center, 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, 1, -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, 1, PBLEND_INVMOD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1190 else if (effectnameindex == EFFECT_TR_SLIGHTBLOOD)
1192 if (cl_particles_quake.integer)
1195 color = particlepalette[67 + (rand()&3)];
1196 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1201 CL_NewParticle(center, 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, 1, -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, 1, PBLEND_INVMOD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1207 if (effectnameindex == EFFECT_TR_ROCKET)
1209 if (cl_particles_quake.integer)
1212 color = particlepalette[ramp3[r]];
1213 CL_NewParticle(center, 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), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1217 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1218 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1221 else if (effectnameindex == EFFECT_TR_GRENADE)
1223 if (cl_particles_quake.integer)
1226 color = particlepalette[ramp3[r]];
1227 CL_NewParticle(center, 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), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1231 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1234 else if (effectnameindex == EFFECT_TR_WIZSPIKE)
1236 if (cl_particles_quake.integer)
1239 color = particlepalette[52 + (rand()&7)];
1240 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1241 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1243 else if (gamemode == GAME_GOODVSBAD2)
1246 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1250 color = particlepalette[20 + (rand()&7)];
1251 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1254 else if (effectnameindex == EFFECT_TR_KNIGHTSPIKE)
1256 if (cl_particles_quake.integer)
1259 color = particlepalette[230 + (rand()&7)];
1260 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1261 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1265 color = particlepalette[226 + (rand()&7)];
1266 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1269 else if (effectnameindex == EFFECT_TR_VORESPIKE)
1271 if (cl_particles_quake.integer)
1273 color = particlepalette[152 + (rand()&3)];
1274 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1276 else if (gamemode == GAME_GOODVSBAD2)
1279 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1281 else if (gamemode == GAME_PRYDON)
1284 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1287 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1289 else if (effectnameindex == EFFECT_TR_NEHAHRASMOKE)
1292 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1294 else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
1297 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1299 else if (effectnameindex == EFFECT_TR_GLOWTRAIL)
1300 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1304 if (effectnameindex == EFFECT_TR_ROCKET)
1305 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1306 else if (effectnameindex == EFFECT_TR_GRENADE)
1307 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1309 // advance to next time and position
1312 VectorMA (pos, dec, dir, pos);
1315 ent->persistent.trail_time = len;
1318 Con_DPrintf("CL_ParticleEffect_Fallback: no fallback found for effect %s\n", particleeffectname[effectnameindex]);
1321 // this is also called on point effects with spawndlight = true and
1322 // spawnparticles = true
1323 // it is called CL_ParticleTrail because most code does not want to supply
1324 // these parameters, only trail handling does
1325 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)
1327 qboolean found = false;
1328 if (effectnameindex < 1 || effectnameindex >= MAX_PARTICLEEFFECTNAME || !particleeffectname[effectnameindex][0])
1330 Con_DPrintf("Unknown effect number %i received from server\n", effectnameindex);
1331 return; // no such effect
1333 if (!cl_particles_quake.integer && particleeffectinfo[0].effectnameindex)
1335 int effectinfoindex;
1338 particleeffectinfo_t *info;
1345 qboolean underwater;
1346 qboolean immediatebloodstain;
1348 // note this runs multiple effects with the same name, each one spawns only one kind of particle, so some effects need more than one
1349 VectorLerp(originmins, 0.5, originmaxs, center);
1350 supercontents = CL_PointSuperContents(center);
1351 underwater = (supercontents & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)) != 0;
1352 VectorSubtract(originmaxs, originmins, traildir);
1353 traillen = VectorLength(traildir);
1354 VectorNormalize(traildir);
1355 for (effectinfoindex = 0, info = particleeffectinfo;effectinfoindex < MAX_PARTICLEEFFECTINFO && info->effectnameindex;effectinfoindex++, info++)
1357 if (info->effectnameindex == effectnameindex)
1360 if ((info->flags & PARTICLEEFFECT_UNDERWATER) && !underwater)
1362 if ((info->flags & PARTICLEEFFECT_NOTUNDERWATER) && underwater)
1365 // spawn a dlight if requested
1366 if (info->lightradiusstart > 0 && spawndlight)
1368 matrix4x4_t tempmatrix;
1369 if (info->trailspacing > 0)
1370 Matrix4x4_CreateTranslate(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2]);
1372 Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
1373 if (info->lighttime > 0 && info->lightradiusfade > 0)
1375 // light flash (explosion, etc)
1376 // called when effect starts
1377 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);
1382 // called by CL_LinkNetworkEntity
1383 Matrix4x4_Scale(&tempmatrix, info->lightradiusstart, 1);
1384 R_RTLight_Update(&r_refdef.scene.templights[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);
1385 r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
1389 if (!spawnparticles)
1394 if (info->tex[1] > info->tex[0])
1396 tex = (int)lhrandom(info->tex[0], info->tex[1]);
1397 tex = min(tex, info->tex[1] - 1);
1399 if(info->staintex[0] < 0)
1400 staintex = info->staintex[0];
1403 staintex = (int)lhrandom(info->staintex[0], info->staintex[1]);
1404 staintex = min(staintex, info->staintex[1] - 1);
1406 if (info->particletype == pt_decal)
1407 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]);
1408 else if (info->orientation == PARTICLE_HBEAM)
1409 CL_NewParticle(center, 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]), info->stretchfactor, info->blendmode, info->orientation, info->staincolor[0], info->staincolor[1], staintex, lhrandom(info->stainalpha[0], info->stainalpha[1]), lhrandom(info->stainsize[0], info->stainsize[1]));
1412 if (!cl_particles.integer)
1414 switch (info->particletype)
1416 case pt_smoke: if (!cl_particles_smoke.integer) continue;break;
1417 case pt_spark: if (!cl_particles_sparks.integer) continue;break;
1418 case pt_bubble: if (!cl_particles_bubbles.integer) continue;break;
1419 case pt_blood: if (!cl_particles_blood.integer) continue;break;
1420 case pt_rain: if (!cl_particles_rain.integer) continue;break;
1421 case pt_snow: if (!cl_particles_snow.integer) continue;break;
1424 VectorCopy(originmins, trailpos);
1425 if (info->trailspacing > 0)
1427 info->particleaccumulator += traillen / info->trailspacing * cl_particles_quality.value;
1428 trailstep = info->trailspacing / cl_particles_quality.value;
1429 immediatebloodstain = false;
1433 info->particleaccumulator += info->countabsolute + pcount * info->countmultiplier * cl_particles_quality.value;
1435 immediatebloodstain = info->particletype == pt_blood || staintex;
1437 info->particleaccumulator = bound(0, info->particleaccumulator, 16384);
1438 for (;info->particleaccumulator >= 1;info->particleaccumulator--)
1440 if (info->tex[1] > info->tex[0])
1442 tex = (int)lhrandom(info->tex[0], info->tex[1]);
1443 tex = min(tex, info->tex[1] - 1);
1447 trailpos[0] = lhrandom(originmins[0], originmaxs[0]);
1448 trailpos[1] = lhrandom(originmins[1], originmaxs[1]);
1449 trailpos[2] = lhrandom(originmins[2], originmaxs[2]);
1452 part = CL_NewParticle(center, 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]), info->stretchfactor, info->blendmode, info->orientation, info->staincolor[0], info->staincolor[1], staintex, lhrandom(info->stainalpha[0], info->stainalpha[1]), lhrandom(info->stainsize[0], info->stainsize[1]));
1453 if (immediatebloodstain && part)
1455 immediatebloodstain = false;
1456 CL_ImmediateBloodStain(part);
1459 VectorMA(trailpos, trailstep, traildir, trailpos);
1466 CL_ParticleEffect_Fallback(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, spawndlight, spawnparticles);
1469 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)
1471 CL_ParticleTrail(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, true, true);
1479 void CL_EntityParticles (const entity_t *ent)
1482 float pitch, yaw, dist = 64, beamlength = 16, org[3], v[3];
1483 static vec3_t avelocities[NUMVERTEXNORMALS];
1484 if (!cl_particles.integer) return;
1485 if (cl.time <= cl.oldtime) return; // don't spawn new entity particles while paused
1487 Matrix4x4_OriginFromMatrix(&ent->render.matrix, org);
1489 if (!avelocities[0][0])
1490 for (i = 0;i < NUMVERTEXNORMALS * 3;i++)
1491 avelocities[0][i] = lhrandom(0, 2.55);
1493 for (i = 0;i < NUMVERTEXNORMALS;i++)
1495 yaw = cl.time * avelocities[i][0];
1496 pitch = cl.time * avelocities[i][1];
1497 v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
1498 v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
1499 v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
1500 CL_NewParticle(org, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1505 void CL_ReadPointFile_f (void)
1507 vec3_t org, leakorg;
1509 char *pointfile = NULL, *pointfilepos, *t, tchar;
1510 char name[MAX_OSPATH];
1515 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
1516 strlcat (name, ".pts", sizeof (name));
1517 pointfile = (char *)FS_LoadFile(name, tempmempool, true, NULL);
1520 Con_Printf("Could not open %s\n", name);
1524 Con_Printf("Reading %s...\n", name);
1525 VectorClear(leakorg);
1528 pointfilepos = pointfile;
1529 while (*pointfilepos)
1531 while (*pointfilepos == '\n' || *pointfilepos == '\r')
1536 while (*t && *t != '\n' && *t != '\r')
1540 #if _MSC_VER >= 1400
1541 #define sscanf sscanf_s
1543 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
1549 VectorCopy(org, leakorg);
1552 if (cl.num_particles < cl.max_particles - 3)
1555 CL_NewParticle(org, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1558 Mem_Free(pointfile);
1559 VectorCopy(leakorg, org);
1560 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
1562 CL_NewParticle(org, 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, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1, 1, 1);
1563 CL_NewParticle(org, 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, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1, 1, 1);
1564 CL_NewParticle(org, 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, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1, 1, 1);
1569 CL_ParseParticleEffect
1571 Parse an effect out of the server message
1574 void CL_ParseParticleEffect (void)
1577 int i, count, msgcount, color;
1579 MSG_ReadVector(org, cls.protocol);
1580 for (i=0 ; i<3 ; i++)
1581 dir[i] = MSG_ReadChar () * (1.0 / 16.0);
1582 msgcount = MSG_ReadByte ();
1583 color = MSG_ReadByte ();
1585 if (msgcount == 255)
1590 CL_ParticleEffect(EFFECT_SVC_PARTICLE, count, org, org, dir, dir, NULL, color);
1595 CL_ParticleExplosion
1599 void CL_ParticleExplosion (const vec3_t org)
1605 R_Stain(org, 96, 40, 40, 40, 64, 88, 88, 88, 64);
1606 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1608 if (cl_particles_quake.integer)
1610 for (i = 0;i < 1024;i++)
1616 color = particlepalette[ramp1[r]];
1617 CL_NewParticle(org, 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), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1621 color = particlepalette[ramp2[r]];
1622 CL_NewParticle(org, 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), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1628 i = CL_PointSuperContents(org);
1629 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
1631 if (cl_particles.integer && cl_particles_bubbles.integer)
1632 for (i = 0;i < 128 * cl_particles_quality.value;i++)
1633 CL_NewParticle(org, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1637 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
1639 for (i = 0;i < 512 * cl_particles_quality.value;i++)
1643 for (k = 0;k < 16;k++)
1646 VectorMA(org, 128, v2, v);
1647 trace = CL_TraceLine(org, v, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
1648 if (trace.fraction >= 0.1)
1651 VectorSubtract(trace.endpos, org, v2);
1652 VectorScale(v2, 2.0f, v2);
1653 CL_NewParticle(org, 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, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1, 1, 1);
1659 if (cl_particles_explosions_shell.integer)
1660 R_NewExplosion(org);
1665 CL_ParticleExplosion2
1669 void CL_ParticleExplosion2 (const vec3_t org, int colorStart, int colorLength)
1672 if (!cl_particles.integer) return;
1674 for (i = 0;i < 512 * cl_particles_quality.value;i++)
1676 k = particlepalette[colorStart + (i % colorLength)];
1677 if (cl_particles_quake.integer)
1678 CL_NewParticle(org, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1680 CL_NewParticle(org, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1684 static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount)
1687 VectorMAM(0.5f, originmins, 0.5f, originmaxs, center);
1688 if (cl_particles_sparks.integer)
1690 sparkcount *= cl_particles_quality.value;
1691 while(sparkcount-- > 0)
1692 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1, 1, 1);
1696 static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount)
1699 VectorMAM(0.5f, originmins, 0.5f, originmaxs, center);
1700 if (cl_particles_smoke.integer)
1702 smokecount *= cl_particles_quality.value;
1703 while(smokecount-- > 0)
1704 CL_NewParticle(center, 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, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1708 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)
1712 if (!cl_particles.integer) return;
1713 VectorMAM(0.5f, mins, 0.5f, maxs, center);
1715 count = (int)(count * cl_particles_quality.value);
1718 k = particlepalette[colorbase + (rand()&3)];
1719 CL_NewParticle(center, 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, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1723 void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, int count, int colorbase, int type)
1726 float minz, maxz, lifetime = 30;
1728 if (!cl_particles.integer) return;
1729 if (dir[2] < 0) // falling
1731 minz = maxs[2] + dir[2] * 0.1;
1734 lifetime = (maxz - cl.worldmodel->normalmins[2]) / max(1, -dir[2]);
1739 maxz = maxs[2] + dir[2] * 0.1;
1741 lifetime = (cl.worldmodel->normalmaxs[2] - minz) / max(1, dir[2]);
1744 count = (int)(count * cl_particles_quality.value);
1749 if (!cl_particles_rain.integer) break;
1750 count *= 4; // ick, this should be in the mod or maps?
1754 k = particlepalette[colorbase + (rand()&3)];
1755 VectorSet(org, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz));
1756 if (gamemode == GAME_GOODVSBAD2)
1757 CL_NewParticle(org, pt_rain, k, k, tex_particle, 20, 0, lhrandom(32, 64), 0, 0, -1, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1, 1, 1);
1759 CL_NewParticle(org, pt_rain, k, k, tex_particle, 0.5, 0, lhrandom(32, 64), 0, 0, -1, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1, 1, 1);
1763 if (!cl_particles_snow.integer) break;
1766 k = particlepalette[colorbase + (rand()&3)];
1767 VectorSet(org, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz));
1768 if (gamemode == GAME_GOODVSBAD2)
1769 CL_NewParticle(org, pt_snow, k, k, tex_particle, 20, 0, lhrandom(64, 128), 0, 0, -1, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1771 CL_NewParticle(org, pt_snow, k, k, tex_particle, 1, 0, lhrandom(64, 128), 0, 0, -1, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1, 1, 1);
1775 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
1779 static cvar_t r_drawparticles = {0, "r_drawparticles", "1", "enables drawing of particles"};
1780 static cvar_t r_drawparticles_drawdistance = {CVAR_SAVE, "r_drawparticles_drawdistance", "2000", "particles further than drawdistance*size will not be drawn"};
1781 static cvar_t r_drawdecals = {0, "r_drawdecals", "1", "enables drawing of decals"};
1782 static cvar_t r_drawdecals_drawdistance = {CVAR_SAVE, "r_drawdecals_drawdistance", "500", "decals further than drawdistance*size will not be drawn"};
1784 #define PARTICLETEXTURESIZE 64
1785 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1787 static unsigned char shadebubble(float dx, float dy, vec3_t light)
1791 dz = 1 - (dx*dx+dy*dy);
1792 if (dz > 0) // it does hit the sphere
1796 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1797 VectorNormalize(normal);
1798 dot = DotProduct(normal, light);
1799 if (dot > 0.5) // interior reflection
1800 f += ((dot * 2) - 1);
1801 else if (dot < -0.5) // exterior reflection
1802 f += ((dot * -2) - 1);
1804 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1805 VectorNormalize(normal);
1806 dot = DotProduct(normal, light);
1807 if (dot > 0.5) // interior reflection
1808 f += ((dot * 2) - 1);
1809 else if (dot < -0.5) // exterior reflection
1810 f += ((dot * -2) - 1);
1812 f += 16; // just to give it a haze so you can see the outline
1813 f = bound(0, f, 255);
1814 return (unsigned char) f;
1820 int particlefontwidth, particlefontheight, particlefontcellwidth, particlefontcellheight, particlefontrows, particlefontcols;
1821 void CL_Particle_PixelCoordsForTexnum(int texnum, int *basex, int *basey, int *width, int *height)
1823 *basex = (texnum % particlefontcols) * particlefontcellwidth;
1824 *basey = ((texnum / particlefontcols) % particlefontrows) * particlefontcellheight;
1825 *width = particlefontcellwidth;
1826 *height = particlefontcellheight;
1829 static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
1831 int basex, basey, w, h, y;
1832 CL_Particle_PixelCoordsForTexnum(texnum, &basex, &basey, &w, &h);
1833 if(w != PARTICLETEXTURESIZE || h != PARTICLETEXTURESIZE)
1834 Sys_Error("invalid particle texture size for autogenerating");
1835 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1836 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1839 void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
1842 float cx, cy, dx, dy, f, iradius;
1844 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1845 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1846 iradius = 1.0f / radius;
1847 alpha *= (1.0f / 255.0f);
1848 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1850 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1854 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1859 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1860 d[0] += (int)(f * (blue - d[0]));
1861 d[1] += (int)(f * (green - d[1]));
1862 d[2] += (int)(f * (red - d[2]));
1868 void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1871 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1873 data[0] = bound(minb, data[0], maxb);
1874 data[1] = bound(ming, data[1], maxg);
1875 data[2] = bound(minr, data[2], maxr);
1879 void particletextureinvert(unsigned char *data)
1882 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1884 data[0] = 255 - data[0];
1885 data[1] = 255 - data[1];
1886 data[2] = 255 - data[2];
1890 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1891 static void R_InitBloodTextures (unsigned char *particletexturedata)
1894 size_t datasize = PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4;
1895 unsigned char *data = Mem_Alloc(tempmempool, datasize);
1898 for (i = 0;i < 8;i++)
1900 memset(data, 255, datasize);
1901 for (k = 0;k < 24;k++)
1902 particletextureblotch(data, PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1903 //particletextureclamp(data, 32, 32, 32, 255, 255, 255);
1904 particletextureinvert(data);
1905 setuptex(tex_bloodparticle[i], data, particletexturedata);
1909 for (i = 0;i < 8;i++)
1911 memset(data, 255, datasize);
1913 for (j = 1;j < 10;j++)
1914 for (k = min(j, m - 1);k < m;k++)
1915 particletextureblotch(data, (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 320 - j * 8);
1916 //particletextureclamp(data, 32, 32, 32, 255, 255, 255);
1917 particletextureinvert(data);
1918 setuptex(tex_blooddecal[i], data, particletexturedata);
1924 //uncomment this to make engine save out particle font to a tga file when run
1925 //#define DUMPPARTICLEFONT
1927 static void R_InitParticleTexture (void)
1929 int x, y, d, i, k, m;
1930 int basex, basey, w, h;
1931 float dx, dy, f, s1, t1, s2, t2;
1934 fs_offset_t filesize;
1935 char texturename[MAX_QPATH];
1937 // a note: decals need to modulate (multiply) the background color to
1938 // properly darken it (stain), and they need to be able to alpha fade,
1939 // this is a very difficult challenge because it means fading to white
1940 // (no change to background) rather than black (darkening everything
1941 // behind the whole decal polygon), and to accomplish this the texture is
1942 // inverted (dark red blood on white background becomes brilliant cyan
1943 // and white on black background) so we can alpha fade it to black, then
1944 // we invert it again during the blendfunc to make it work...
1946 #ifndef DUMPPARTICLEFONT
1947 decalskinframe = R_SkinFrame_LoadExternal("particles/particlefont.tga", TEXF_ALPHA | TEXF_FORCELINEAR, false);
1950 particlefonttexture = decalskinframe->base;
1951 // TODO maybe allow custom grid size?
1952 particlefontwidth = image_width;
1953 particlefontheight = image_height;
1954 particlefontcellwidth = image_width / 8;
1955 particlefontcellheight = image_height / 8;
1956 particlefontcols = 8;
1957 particlefontrows = 8;
1962 unsigned char *particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1963 size_t datasize = PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4;
1964 unsigned char *data = (unsigned char *)Mem_Alloc(tempmempool, datasize);
1965 unsigned char *noise1 = (unsigned char *)Mem_Alloc(tempmempool, PARTICLETEXTURESIZE*2*PARTICLETEXTURESIZE*2);
1966 unsigned char *noise2 = (unsigned char *)Mem_Alloc(tempmempool, PARTICLETEXTURESIZE*2*PARTICLETEXTURESIZE*2);
1968 particlefontwidth = particlefontheight = PARTICLEFONTSIZE;
1969 particlefontcellwidth = particlefontcellheight = PARTICLETEXTURESIZE;
1970 particlefontcols = 8;
1971 particlefontrows = 8;
1973 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1976 for (i = 0;i < 8;i++)
1978 memset(data, 255, datasize);
1981 fractalnoise(noise1, PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1982 fractalnoise(noise2, PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1984 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1986 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1987 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1989 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1990 d = (noise2[y*PARTICLETEXTURESIZE*2+x] - 128) * 3 + 192;
1992 d = (int)(d * (1-(dx*dx+dy*dy)));
1993 d = (d * noise1[y*PARTICLETEXTURESIZE*2+x]) >> 7;
1994 d = bound(0, d, 255);
1995 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (unsigned char) d;
2002 setuptex(tex_smoke[i], data, particletexturedata);
2006 memset(data, 255, datasize);
2007 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2009 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2010 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2012 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2013 f = 255.0f * (1.0 - 4.0f * fabs(10.0f - sqrt(dx*dx+dy*dy)));
2014 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (int) (bound(0.0f, f, 255.0f));
2017 setuptex(tex_rainsplash, data, particletexturedata);
2020 memset(data, 255, datasize);
2021 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2023 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2024 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2026 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2027 d = (int)(256 * (1 - (dx*dx+dy*dy)));
2028 d = bound(0, d, 255);
2029 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (unsigned char) d;
2032 setuptex(tex_particle, data, particletexturedata);
2035 memset(data, 255, datasize);
2036 light[0] = 1;light[1] = 1;light[2] = 1;
2037 VectorNormalize(light);
2038 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2040 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2041 // stretch upper half of bubble by +50% and shrink lower half by -50%
2042 // (this gives an elongated teardrop shape)
2044 dy = (dy - 0.5f) * 2.0f;
2046 dy = (dy - 0.5f) / 1.5f;
2047 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2049 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2050 // shrink bubble width to half
2052 data[(y*PARTICLETEXTURESIZE+x)*4+3] = shadebubble(dx, dy, light);
2055 setuptex(tex_raindrop, data, particletexturedata);
2058 memset(data, 255, datasize);
2059 light[0] = 1;light[1] = 1;light[2] = 1;
2060 VectorNormalize(light);
2061 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2063 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2064 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2066 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2067 data[(y*PARTICLETEXTURESIZE+x)*4+3] = shadebubble(dx, dy, light);
2070 setuptex(tex_bubble, data, particletexturedata);
2072 // Blood particles and blood decals
2073 R_InitBloodTextures (particletexturedata);
2076 for (i = 0;i < 8;i++)
2078 memset(data, 255, datasize);
2079 for (k = 0;k < 12;k++)
2080 particletextureblotch(data, PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
2081 for (k = 0;k < 3;k++)
2082 particletextureblotch(data, PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
2083 //particletextureclamp(data, 64, 64, 64, 255, 255, 255);
2084 particletextureinvert(data);
2085 setuptex(tex_bulletdecal[i], data, particletexturedata);
2088 #ifdef DUMPPARTICLEFONT
2089 Image_WriteTGABGRA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
2092 decalskinframe = R_SkinFrame_LoadInternalBGRA("particlefont", TEXF_ALPHA | TEXF_FORCELINEAR, particletexturedata, PARTICLEFONTSIZE, PARTICLEFONTSIZE);
2093 particlefonttexture = decalskinframe->base;
2095 Mem_Free(particletexturedata);
2100 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
2102 CL_Particle_PixelCoordsForTexnum(i, &basex, &basey, &w, &h);
2103 particletexture[i].texture = particlefonttexture;
2104 particletexture[i].s1 = (basex + 1) / (float)particlefontwidth;
2105 particletexture[i].t1 = (basey + 1) / (float)particlefontheight;
2106 particletexture[i].s2 = (basex + w - 1) / (float)particlefontwidth;
2107 particletexture[i].t2 = (basey + h - 1) / (float)particlefontheight;
2110 #ifndef DUMPPARTICLEFONT
2111 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", false, TEXF_ALPHA | TEXF_FORCELINEAR, true, r_texture_convertsRGB_particles.integer);
2112 if (!particletexture[tex_beam].texture)
2115 unsigned char noise3[64][64], data2[64][16][4];
2117 fractalnoise(&noise3[0][0], 64, 4);
2119 for (y = 0;y < 64;y++)
2121 dy = (y - 0.5f*64) / (64*0.5f-1);
2122 for (x = 0;x < 16;x++)
2124 dx = (x - 0.5f*16) / (16*0.5f-2);
2125 d = (int)((1 - sqrt(fabs(dx))) * noise3[y][x]);
2126 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
2127 data2[y][x][3] = 255;
2131 #ifdef DUMPPARTICLEFONT
2132 Image_WriteTGABGRA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
2134 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_FORCELINEAR, NULL);
2136 particletexture[tex_beam].s1 = 0;
2137 particletexture[tex_beam].t1 = 0;
2138 particletexture[tex_beam].s2 = 1;
2139 particletexture[tex_beam].t2 = 1;
2141 // now load an texcoord/texture override file
2142 buf = (char *) FS_LoadFile("particles/particlefont.txt", tempmempool, false, &filesize);
2149 if(!COM_ParseToken_Simple(&bufptr, true, false))
2151 if(!strcmp(com_token, "\n"))
2152 continue; // empty line
2153 i = atoi(com_token);
2161 if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
2163 s1 = atof(com_token);
2164 if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
2166 t1 = atof(com_token);
2167 if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
2169 s2 = atof(com_token);
2170 if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
2172 t2 = atof(com_token);
2173 strlcpy(texturename, "particles/particlefont.tga", sizeof(texturename));
2174 if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
2175 strlcpy(texturename, com_token, sizeof(texturename));
2182 strlcpy(texturename, com_token, sizeof(texturename));
2185 if (!texturename[0])
2187 Con_Printf("particles/particlefont.txt: syntax should be texnum x1 y1 x2 y2 texturename or texnum x1 y1 x2 y2 or texnum texturename\n");
2190 if (i < 0 || i >= MAX_PARTICLETEXTURES)
2192 Con_Printf("particles/particlefont.txt: texnum %i outside valid range (0 to %i)\n", i, MAX_PARTICLETEXTURES);
2195 particletexture[i].texture = R_SkinFrame_LoadExternal(texturename, TEXF_ALPHA | TEXF_FORCELINEAR, false)->base;
2196 particletexture[i].s1 = s1;
2197 particletexture[i].t1 = t1;
2198 particletexture[i].s2 = s2;
2199 particletexture[i].t2 = t2;
2205 static void r_part_start(void)
2208 // generate particlepalette for convenience from the main one
2209 for (i = 0;i < 256;i++)
2210 particlepalette[i] = palette_rgb[i][0] * 65536 + palette_rgb[i][1] * 256 + palette_rgb[i][2];
2211 particletexturepool = R_AllocTexturePool();
2212 R_InitParticleTexture ();
2213 CL_Particles_LoadEffectInfo();
2216 static void r_part_shutdown(void)
2218 R_FreeTexturePool(&particletexturepool);
2221 static void r_part_newmap(void)
2224 R_SkinFrame_MarkUsed(decalskinframe);
2225 CL_Particles_LoadEffectInfo();
2228 #define BATCHSIZE 256
2229 unsigned short particle_elements[BATCHSIZE*6];
2230 float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16];
2232 void R_Particles_Init (void)
2235 for (i = 0;i < BATCHSIZE;i++)
2237 particle_elements[i*6+0] = i*4+0;
2238 particle_elements[i*6+1] = i*4+1;
2239 particle_elements[i*6+2] = i*4+2;
2240 particle_elements[i*6+3] = i*4+0;
2241 particle_elements[i*6+4] = i*4+2;
2242 particle_elements[i*6+5] = i*4+3;
2245 Cvar_RegisterVariable(&r_drawparticles);
2246 Cvar_RegisterVariable(&r_drawparticles_drawdistance);
2247 Cvar_RegisterVariable(&r_drawdecals);
2248 Cvar_RegisterVariable(&r_drawdecals_drawdistance);
2249 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
2252 void R_DrawDecal_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2254 int surfacelistindex;
2256 float *v3f, *t2f, *c4f;
2257 particletexture_t *tex;
2258 float right[3], up[3], size, ca;
2259 float alphascale = (1.0f / 65536.0f) * cl_particles_alpha.value;
2260 float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16];
2262 RSurf_ActiveWorldEntity();
2264 r_refdef.stats.drawndecals += numsurfaces;
2265 R_Mesh_ResetTextureState();
2266 R_Mesh_VertexPointer(particle_vertex3f, 0, 0);
2267 R_Mesh_TexCoordPointer(0, 2, particle_texcoord2f, 0, 0);
2268 R_Mesh_ColorPointer(particle_color4f, 0, 0);
2269 GL_DepthMask(false);
2270 GL_DepthRange(0, 1);
2271 GL_PolygonOffset(0, 0);
2273 GL_CullFace(GL_NONE);
2275 // generate all the vertices at once
2276 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
2278 d = cl.decals + surfacelist[surfacelistindex];
2281 c4f = particle_color4f + 16*surfacelistindex;
2282 ca = d->alpha * alphascale;
2283 // ensure alpha multiplier saturates properly
2284 if (ca > 1.0f / 256.0f)
2286 if (r_refdef.fogenabled)
2287 ca *= RSurf_FogVertex(d->org);
2288 Vector4Set(c4f, d->color[0] * ca, d->color[1] * ca, d->color[2] * ca, 1);
2289 Vector4Copy(c4f, c4f + 4);
2290 Vector4Copy(c4f, c4f + 8);
2291 Vector4Copy(c4f, c4f + 12);
2293 // calculate vertex positions
2294 size = d->size * cl_particles_size.value;
2295 VectorVectors(d->normal, right, up);
2296 VectorScale(right, size, right);
2297 VectorScale(up, size, up);
2298 v3f = particle_vertex3f + 12*surfacelistindex;
2299 v3f[ 0] = d->org[0] - right[0] - up[0];
2300 v3f[ 1] = d->org[1] - right[1] - up[1];
2301 v3f[ 2] = d->org[2] - right[2] - up[2];
2302 v3f[ 3] = d->org[0] - right[0] + up[0];
2303 v3f[ 4] = d->org[1] - right[1] + up[1];
2304 v3f[ 5] = d->org[2] - right[2] + up[2];
2305 v3f[ 6] = d->org[0] + right[0] + up[0];
2306 v3f[ 7] = d->org[1] + right[1] + up[1];
2307 v3f[ 8] = d->org[2] + right[2] + up[2];
2308 v3f[ 9] = d->org[0] + right[0] - up[0];
2309 v3f[10] = d->org[1] + right[1] - up[1];
2310 v3f[11] = d->org[2] + right[2] - up[2];
2312 // calculate texcoords
2313 tex = &particletexture[d->texnum];
2314 t2f = particle_texcoord2f + 8*surfacelistindex;
2315 t2f[0] = tex->s1;t2f[1] = tex->t2;
2316 t2f[2] = tex->s1;t2f[3] = tex->t1;
2317 t2f[4] = tex->s2;t2f[5] = tex->t1;
2318 t2f[6] = tex->s2;t2f[7] = tex->t2;
2321 // now render the decals all at once
2322 // (this assumes they all use one particle font texture!)
2323 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2324 R_SetupShader_Generic(particletexture[63].texture, NULL, GL_MODULATE, 1);
2325 R_Mesh_Draw(0, numsurfaces * 4, 0, numsurfaces * 2, NULL, particle_elements, 0, 0);
2328 void R_DrawDecals (void)
2331 int drawdecals = r_drawdecals.integer;
2336 int killsequence = cl.decalsequence - max(0, cl_decals_max.integer);
2338 frametime = bound(0, cl.time - cl.decals_updatetime, 1);
2339 cl.decals_updatetime = bound(cl.time - 1, cl.decals_updatetime + frametime, cl.time + 1);
2341 // LordHavoc: early out conditions
2345 decalfade = frametime * 256 / cl_decals_fadetime.value;
2346 drawdist2 = r_drawdecals_drawdistance.value * r_refdef.view.quality;
2347 drawdist2 = drawdist2*drawdist2;
2349 for (i = 0, decal = cl.decals;i < cl.num_decals;i++, decal++)
2351 if (!decal->typeindex)
2354 if (killsequence - decal->decalsequence > 0)
2357 if (cl.time > decal->time2 + cl_decals_time.value)
2359 decal->alpha -= decalfade;
2360 if (decal->alpha <= 0)
2366 if (cl.entities[decal->owner].render.model == decal->ownermodel)
2368 Matrix4x4_Transform(&cl.entities[decal->owner].render.matrix, decal->relativeorigin, decal->org);
2369 Matrix4x4_Transform3x3(&cl.entities[decal->owner].render.matrix, decal->relativenormal, decal->normal);
2375 if(cl_decals_visculling.integer && decal->clusterindex > -1000 && !CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, decal->clusterindex))
2381 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))
2382 R_MeshQueue_AddTransparent(decal->org, R_DrawDecal_TransparentCallback, NULL, i, NULL);
2385 decal->typeindex = 0;
2386 if (cl.free_decal > i)
2390 // reduce cl.num_decals if possible
2391 while (cl.num_decals > 0 && cl.decals[cl.num_decals - 1].typeindex == 0)
2394 if (cl.num_decals == cl.max_decals && cl.max_decals < MAX_DECALS)
2396 decal_t *olddecals = cl.decals;
2397 cl.max_decals = min(cl.max_decals * 2, MAX_DECALS);
2398 cl.decals = (decal_t *) Mem_Alloc(cls.levelmempool, cl.max_decals * sizeof(decal_t));
2399 memcpy(cl.decals, olddecals, cl.num_decals * sizeof(decal_t));
2400 Mem_Free(olddecals);
2403 r_refdef.stats.totaldecals = cl.num_decals;
2406 void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2408 int surfacelistindex;
2409 int batchstart, batchcount;
2410 const particle_t *p;
2412 rtexture_t *texture;
2413 float *v3f, *t2f, *c4f;
2414 particletexture_t *tex;
2415 float up2[3], v[3], right[3], up[3], fog, ifog, size, len, lenfactor, alpha;
2416 float ambient[3], diffuse[3], diffusenormal[3];
2417 vec4_t colormultiplier;
2419 RSurf_ActiveWorldEntity();
2421 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));
2423 r_refdef.stats.particles += numsurfaces;
2424 R_Mesh_ResetTextureState();
2425 R_Mesh_VertexPointer(particle_vertex3f, 0, 0);
2426 R_Mesh_TexCoordPointer(0, 2, particle_texcoord2f, 0, 0);
2427 R_Mesh_ColorPointer(particle_color4f, 0, 0);
2428 GL_DepthMask(false);
2429 GL_DepthRange(0, 1);
2430 GL_PolygonOffset(0, 0);
2432 GL_AlphaTest(false);
2433 GL_CullFace(GL_NONE);
2435 // first generate all the vertices at once
2436 for (surfacelistindex = 0, v3f = particle_vertex3f, t2f = particle_texcoord2f, c4f = particle_color4f;surfacelistindex < numsurfaces;surfacelistindex++, v3f += 3*4, t2f += 2*4, c4f += 4*4)
2438 p = cl.particles + surfacelist[surfacelistindex];
2440 blendmode = p->blendmode;
2444 case PBLEND_INVALID:
2446 alpha = p->alpha * colormultiplier[3];
2447 // ensure alpha multiplier saturates properly
2450 // additive and modulate can just fade out in fog (this is correct)
2451 if (r_refdef.fogenabled)
2452 alpha *= RSurf_FogVertex(p->org);
2453 // collapse alpha into color for these blends (so that the particlefont does not need alpha on most textures)
2454 alpha *= 1.0f / 256.0f;
2455 c4f[0] = p->color[0] * alpha;
2456 c4f[1] = p->color[1] * alpha;
2457 c4f[2] = p->color[2] * alpha;
2461 alpha = p->alpha * colormultiplier[3];
2462 // ensure alpha multiplier saturates properly
2465 // additive and modulate can just fade out in fog (this is correct)
2466 if (r_refdef.fogenabled)
2467 alpha *= RSurf_FogVertex(p->org);
2468 // collapse alpha into color for these blends (so that the particlefont does not need alpha on most textures)
2469 c4f[0] = p->color[0] * colormultiplier[0] * alpha;
2470 c4f[1] = p->color[1] * colormultiplier[1] * alpha;
2471 c4f[2] = p->color[2] * colormultiplier[2] * alpha;
2475 c4f[0] = p->color[0] * colormultiplier[0];
2476 c4f[1] = p->color[1] * colormultiplier[1];
2477 c4f[2] = p->color[2] * colormultiplier[2];
2478 c4f[3] = p->alpha * colormultiplier[3];
2479 // note: lighting is not cheap!
2480 if (particletype[p->typeindex].lighting)
2482 R_CompleteLightPoint(ambient, diffuse, diffusenormal, p->org, true);
2483 c4f[0] *= (ambient[0] + 0.5 * diffuse[0]);
2484 c4f[1] *= (ambient[1] + 0.5 * diffuse[1]);
2485 c4f[2] *= (ambient[2] + 0.5 * diffuse[2]);
2487 // mix in the fog color
2488 if (r_refdef.fogenabled)
2490 fog = RSurf_FogVertex(p->org);
2492 c4f[0] = c4f[0] * fog + r_refdef.fogcolor[0] * ifog;
2493 c4f[1] = c4f[1] * fog + r_refdef.fogcolor[1] * ifog;
2494 c4f[2] = c4f[2] * fog + r_refdef.fogcolor[2] * ifog;
2498 // copy the color into the other three vertices
2499 Vector4Copy(c4f, c4f + 4);
2500 Vector4Copy(c4f, c4f + 8);
2501 Vector4Copy(c4f, c4f + 12);
2503 size = p->size * cl_particles_size.value;
2504 tex = &particletexture[p->texnum];
2505 switch(p->orientation)
2507 // case PARTICLE_INVALID:
2508 case PARTICLE_BILLBOARD:
2509 VectorScale(r_refdef.view.left, -size * p->stretch, right);
2510 VectorScale(r_refdef.view.up, size, up);
2511 v3f[ 0] = p->org[0] - right[0] - up[0];
2512 v3f[ 1] = p->org[1] - right[1] - up[1];
2513 v3f[ 2] = p->org[2] - right[2] - up[2];
2514 v3f[ 3] = p->org[0] - right[0] + up[0];
2515 v3f[ 4] = p->org[1] - right[1] + up[1];
2516 v3f[ 5] = p->org[2] - right[2] + up[2];
2517 v3f[ 6] = p->org[0] + right[0] + up[0];
2518 v3f[ 7] = p->org[1] + right[1] + up[1];
2519 v3f[ 8] = p->org[2] + right[2] + up[2];
2520 v3f[ 9] = p->org[0] + right[0] - up[0];
2521 v3f[10] = p->org[1] + right[1] - up[1];
2522 v3f[11] = p->org[2] + right[2] - up[2];
2523 t2f[0] = tex->s1;t2f[1] = tex->t2;
2524 t2f[2] = tex->s1;t2f[3] = tex->t1;
2525 t2f[4] = tex->s2;t2f[5] = tex->t1;
2526 t2f[6] = tex->s2;t2f[7] = tex->t2;
2528 case PARTICLE_ORIENTED_DOUBLESIDED:
2529 VectorVectors(p->vel, right, up);
2530 VectorScale(right, size * p->stretch, right);
2531 VectorScale(up, size, up);
2532 v3f[ 0] = p->org[0] - right[0] - up[0];
2533 v3f[ 1] = p->org[1] - right[1] - up[1];
2534 v3f[ 2] = p->org[2] - right[2] - up[2];
2535 v3f[ 3] = p->org[0] - right[0] + up[0];
2536 v3f[ 4] = p->org[1] - right[1] + up[1];
2537 v3f[ 5] = p->org[2] - right[2] + up[2];
2538 v3f[ 6] = p->org[0] + right[0] + up[0];
2539 v3f[ 7] = p->org[1] + right[1] + up[1];
2540 v3f[ 8] = p->org[2] + right[2] + up[2];
2541 v3f[ 9] = p->org[0] + right[0] - up[0];
2542 v3f[10] = p->org[1] + right[1] - up[1];
2543 v3f[11] = p->org[2] + right[2] - up[2];
2544 t2f[0] = tex->s1;t2f[1] = tex->t2;
2545 t2f[2] = tex->s1;t2f[3] = tex->t1;
2546 t2f[4] = tex->s2;t2f[5] = tex->t1;
2547 t2f[6] = tex->s2;t2f[7] = tex->t2;
2549 case PARTICLE_SPARK:
2550 len = VectorLength(p->vel);
2551 VectorNormalize2(p->vel, up);
2552 lenfactor = p->stretch * 0.04 * len;
2553 if(lenfactor < size * 0.5)
2554 lenfactor = size * 0.5;
2555 VectorMA(p->org, -lenfactor, up, v);
2556 VectorMA(p->org, lenfactor, up, up2);
2557 R_CalcBeam_Vertex3f(v3f, v, up2, size);
2558 t2f[0] = tex->s1;t2f[1] = tex->t2;
2559 t2f[2] = tex->s1;t2f[3] = tex->t1;
2560 t2f[4] = tex->s2;t2f[5] = tex->t1;
2561 t2f[6] = tex->s2;t2f[7] = tex->t2;
2563 case PARTICLE_VBEAM:
2564 R_CalcBeam_Vertex3f(v3f, p->org, p->vel, size);
2565 VectorSubtract(p->vel, p->org, up);
2566 VectorNormalize(up);
2567 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) * p->stretch;
2568 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f) * p->stretch;
2569 t2f[0] = tex->s2;t2f[1] = v[0];
2570 t2f[2] = tex->s1;t2f[3] = v[0];
2571 t2f[4] = tex->s1;t2f[5] = v[1];
2572 t2f[6] = tex->s2;t2f[7] = v[1];
2574 case PARTICLE_HBEAM:
2575 R_CalcBeam_Vertex3f(v3f, p->org, p->vel, size);
2576 VectorSubtract(p->vel, p->org, up);
2577 VectorNormalize(up);
2578 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) * p->stretch;
2579 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f) * p->stretch;
2580 t2f[0] = v[0];t2f[1] = tex->t1;
2581 t2f[2] = v[0];t2f[3] = tex->t2;
2582 t2f[4] = v[1];t2f[5] = tex->t2;
2583 t2f[6] = v[1];t2f[7] = tex->t1;
2588 // now render batches of particles based on blendmode and texture
2589 blendmode = PBLEND_INVALID;
2593 for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
2595 p = cl.particles + surfacelist[surfacelistindex];
2597 if (blendmode != p->blendmode)
2599 blendmode = p->blendmode;
2603 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2605 case PBLEND_INVALID:
2607 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2610 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2614 if (texture != particletexture[p->texnum].texture)
2616 texture = particletexture[p->texnum].texture;
2617 R_SetupShader_Generic(texture, NULL, GL_MODULATE, 1);
2620 // iterate until we find a change in settings
2621 batchstart = surfacelistindex++;
2622 for (;surfacelistindex < numsurfaces;surfacelistindex++)
2624 p = cl.particles + surfacelist[surfacelistindex];
2625 if (blendmode != p->blendmode || texture != particletexture[p->texnum].texture)
2629 batchcount = surfacelistindex - batchstart;
2630 R_Mesh_Draw(batchstart * 4, batchcount * 4, batchstart * 2, batchcount * 2, NULL, particle_elements, 0, 0);
2634 void R_DrawParticles (void)
2637 int drawparticles = r_drawparticles.integer;
2638 float minparticledist;
2640 float gravity, frametime, f, dist, oldorg[3];
2646 frametime = bound(0, cl.time - cl.particles_updatetime, 1);
2647 cl.particles_updatetime = bound(cl.time - 1, cl.particles_updatetime + frametime, cl.time + 1);
2649 // LordHavoc: early out conditions
2650 if (!cl.num_particles)
2653 minparticledist = DotProduct(r_refdef.view.origin, r_refdef.view.forward) + 4.0f;
2654 gravity = frametime * cl.movevars_gravity;
2655 update = frametime > 0;
2656 drawdist2 = r_drawparticles_drawdistance.value * r_refdef.view.quality;
2657 drawdist2 = drawdist2*drawdist2;
2659 for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++)
2663 if (cl.free_particle > i)
2664 cl.free_particle = i;
2670 if (p->delayedspawn > cl.time)
2672 p->delayedspawn = 0;
2674 p->size += p->sizeincrease * frametime;
2675 p->alpha -= p->alphafade * frametime;
2677 if (p->alpha <= 0 || p->die <= cl.time)
2680 if (p->orientation != PARTICLE_VBEAM && p->orientation != PARTICLE_HBEAM && frametime > 0)
2682 if (p->liquidfriction && (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK))
2684 if (p->typeindex == pt_blood)
2685 p->size += frametime * 8;
2687 p->vel[2] -= p->gravity * gravity;
2688 f = 1.0f - min(p->liquidfriction * frametime, 1);
2689 VectorScale(p->vel, f, p->vel);
2693 p->vel[2] -= p->gravity * gravity;
2696 f = 1.0f - min(p->airfriction * frametime, 1);
2697 VectorScale(p->vel, f, p->vel);
2701 VectorCopy(p->org, oldorg);
2702 VectorMA(p->org, frametime, p->vel, p->org);
2703 // if (p->bounce && cl.time >= p->delayedcollisions)
2706 trace = CL_TraceLine(oldorg, p->org, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | ((p->typeindex == pt_rain || p->typeindex == pt_snow) ? SUPERCONTENTS_LIQUIDSMASK : 0), true, false, &hitent, false);
2707 // if the trace started in or hit something of SUPERCONTENTS_NODROP
2708 // or if the trace hit something flagged as NOIMPACT
2709 // then remove the particle
2710 if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT || ((trace.startsupercontents | trace.hitsupercontents) & SUPERCONTENTS_NODROP) || (trace.startsupercontents & SUPERCONTENTS_SOLID))
2712 VectorCopy(trace.endpos, p->org);
2713 // react if the particle hit something
2714 if (trace.fraction < 1)
2716 VectorCopy(trace.endpos, p->org);
2718 if (p->staintexnum >= 0)
2720 // blood - splash on solid
2721 if (!(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
2724 p->staincolor[0], p->staincolor[1], p->staincolor[2], (int)(p->stainalpha * p->stainsize * (1.0f / 160.0f)),
2725 p->staincolor[0], p->staincolor[1], p->staincolor[2], (int)(p->stainalpha * p->stainsize * (1.0f / 160.0f)));
2726 if (cl_decals.integer)
2728 // create a decal for the blood splat
2729 a = 0xFFFFFF ^ (p->staincolor[0]*65536+p->staincolor[1]*256+p->staincolor[2]);
2730 CL_SpawnDecalParticleForSurface(hitent, p->org, trace.plane.normal, a, a, p->staintexnum, p->stainsize, p->stainalpha); // staincolor needs to be inverted for decals!
2735 if (p->typeindex == pt_blood)
2737 // blood - splash on solid
2738 if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS)
2740 if(p->staintexnum == -1) // staintex < -1 means no stains at all
2742 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)));
2743 if (cl_decals.integer)
2745 // create a decal for the blood splat
2746 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 * lhrandom(cl_particles_blood_decal_scalemin.value, cl_particles_blood_decal_scalemax.value), cl_particles_blood_decal_alpha.value * 768);
2751 else if (p->bounce < 0)
2753 // bounce -1 means remove on impact
2758 // anything else - bounce off solid
2759 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
2760 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
2761 if (DotProduct(p->vel, p->vel) < 0.03)
2762 VectorClear(p->vel);
2768 if (p->typeindex != pt_static)
2770 switch (p->typeindex)
2772 case pt_entityparticle:
2773 // particle that removes itself after one rendered frame
2780 a = CL_PointSuperContents(p->org);
2781 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
2785 a = CL_PointSuperContents(p->org);
2786 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
2790 a = CL_PointSuperContents(p->org);
2791 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
2795 if (cl.time > p->time2)
2798 p->time2 = cl.time + (rand() & 3) * 0.1;
2799 p->vel[0] = p->vel[0] * 0.9f + lhrandom(-32, 32);
2800 p->vel[1] = p->vel[0] * 0.9f + lhrandom(-32, 32);
2802 a = CL_PointSuperContents(p->org);
2803 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
2811 else if (p->delayedspawn)
2815 // don't render particles too close to the view (they chew fillrate)
2816 // also don't render particles behind the view (useless)
2817 // further checks to cull to the frustum would be too slow here
2818 switch(p->typeindex)
2821 // beams have no culling
2822 R_MeshQueue_AddTransparent(p->sortorigin, R_DrawParticle_TransparentCallback, NULL, i, NULL);
2825 if(cl_particles_visculling.integer)
2826 if (!r_refdef.viewcache.world_novis)
2827 if(r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.PointInLeaf)
2829 mleaf_t *leaf = r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, p->org);
2831 if(!CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, leaf->clusterindex))
2834 // anything else just has to be in front of the viewer and visible at this distance
2835 if (DotProduct(p->org, r_refdef.view.forward) >= minparticledist && VectorDistance2(p->org, r_refdef.view.origin) < drawdist2 * (p->size * p->size))
2836 R_MeshQueue_AddTransparent(p->sortorigin, R_DrawParticle_TransparentCallback, NULL, i, NULL);
2843 if (cl.free_particle > i)
2844 cl.free_particle = i;
2847 // reduce cl.num_particles if possible
2848 while (cl.num_particles > 0 && cl.particles[cl.num_particles - 1].typeindex == 0)
2851 if (cl.num_particles == cl.max_particles && cl.max_particles < MAX_PARTICLES)
2853 particle_t *oldparticles = cl.particles;
2854 cl.max_particles = min(cl.max_particles * 2, MAX_PARTICLES);
2855 cl.particles = (particle_t *) Mem_Alloc(cls.levelmempool, cl.max_particles * sizeof(particle_t));
2856 memcpy(cl.particles, oldparticles, cl.num_particles * sizeof(particle_t));
2857 Mem_Free(oldparticles);