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 float rotate[4]; // min/max base angle, min/max rotation over time
122 particleeffectinfo_t;
124 char particleeffectname[MAX_PARTICLEEFFECTNAME][64];
127 particleeffectinfo_t particleeffectinfo[MAX_PARTICLEEFFECTINFO];
129 static int particlepalette[256];
131 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b, // 0-7
132 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb, // 8-15
133 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b, // 16-23
134 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23, // 24-31
135 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767, // 32-39
136 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb, // 40-47
137 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07, // 48-55
138 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f, // 56-63
139 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000, // 64-71
140 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000, // 72-79
141 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307, // 80-87
142 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723, // 88-95
143 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b, // 96-103
144 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b, // 104-111
145 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733, // 112-119
146 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397, // 120-127
147 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b, // 128-135
148 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707, // 136-143
149 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b, // 144-151
150 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707, // 152-159
151 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353, // 160-167
152 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07, // 168-175
153 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f, // 176-183
154 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07, // 184-191
155 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307, // 192-199
156 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700, // 200-207
157 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f, // 208-215
158 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f, // 216-223
159 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b, // 224-231
160 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b, // 232-239
161 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000, // 240-247
162 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53 // 248-255
165 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
166 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
167 int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
169 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
171 // particletexture_t is a rectangle in the particlefonttexture
172 typedef struct particletexture_s
175 float s1, t1, s2, t2;
179 static rtexturepool_t *particletexturepool;
180 static rtexture_t *particlefonttexture;
181 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
182 skinframe_t *decalskinframe;
184 // texture numbers in particle font
185 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
186 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
187 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
188 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
189 static const int tex_rainsplash = 32;
190 static const int tex_particle = 63;
191 static const int tex_bubble = 62;
192 static const int tex_raindrop = 61;
193 static const int tex_beam = 60;
195 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1", "enables particle effects"};
196 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1", "multiplies number of particles"};
197 cvar_t cl_particles_alpha = {CVAR_SAVE, "cl_particles_alpha", "1", "multiplies opacity of particles"};
198 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1", "multiplies particle size"};
199 cvar_t cl_particles_quake = {CVAR_SAVE, "cl_particles_quake", "0", "makes particle effects look mostly like the ones in Quake"};
200 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1", "enables blood effects"};
201 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "1", "opacity of blood, does not affect decals"};
202 cvar_t cl_particles_blood_decal_alpha = {CVAR_SAVE, "cl_particles_blood_decal_alpha", "1", "opacity of blood decal"};
203 cvar_t cl_particles_blood_decal_scalemin = {CVAR_SAVE, "cl_particles_blood_decal_scalemin", "1.5", "minimal random scale of decal"};
204 cvar_t cl_particles_blood_decal_scalemax = {CVAR_SAVE, "cl_particles_blood_decal_scalemax", "2", "maximal random scale of decal"};
205 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1", "make certain quake particle() calls create blood effects instead"};
206 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1", "enables bulletimpact effects"};
207 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1", "enables sparks from explosions"};
208 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0", "enables polygonal shell from explosions"};
209 cvar_t cl_particles_rain = {CVAR_SAVE, "cl_particles_rain", "1", "enables rain effects"};
210 cvar_t cl_particles_snow = {CVAR_SAVE, "cl_particles_snow", "1", "enables snow effects"};
211 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1", "enables smoke (used by multiple effects)"};
212 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5", "smoke brightness"};
213 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55", "brightness fade per second"};
214 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1", "enables sparks (used by multiple effects)"};
215 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1", "enables bubbles (used by multiple effects)"};
216 cvar_t cl_particles_visculling = {CVAR_SAVE, "cl_particles_visculling", "0", "perform a costly check if each particle is visible before drawing"};
217 cvar_t cl_particles_collisions = {CVAR_SAVE, "cl_particles_collisions", "1", "allow costly collision detection on particles (sparks that bounce, particles not going through walls, blood hitting surfaces, etc)"};
218 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "1", "enables decals (bullet holes, blood, etc)"};
219 cvar_t cl_decals_visculling = {CVAR_SAVE, "cl_decals_visculling", "1", "perform a very cheap check if each decal is visible before drawing"};
220 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "20", "how long before decals start to fade away"};
221 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "1", "how long decals take to fade away"};
222 cvar_t cl_decals_newsystem = {CVAR_SAVE, "cl_decals_newsystem", "0", "enables new advanced decal system"};
223 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)"};
224 cvar_t cl_decals_models = {CVAR_SAVE, "cl_decals_models", "0", "enables decals on animated models (if newsystem is also 1)"};
225 cvar_t cl_decals_bias = {CVAR_SAVE, "cl_decals_bias", "0.125", "distance to bias decals from surface to prevent depth fighting"};
226 cvar_t cl_decals_max = {CVAR_SAVE, "cl_decals_max", "4096", "maximum number of decals allowed to exist in the world at once"};
229 void CL_Particles_ParseEffectInfo(const char *textstart, const char *textend, const char *filename)
235 particleeffectinfo_t *info = NULL;
236 const char *text = textstart;
238 effectinfoindex = -1;
239 for (linenumber = 1;;linenumber++)
242 for (arrayindex = 0;arrayindex < 16;arrayindex++)
243 argv[arrayindex][0] = 0;
246 if (!COM_ParseToken_Simple(&text, true, false))
248 if (!strcmp(com_token, "\n"))
252 strlcpy(argv[argc], com_token, sizeof(argv[argc]));
258 #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;}
259 #define readints(array, n) checkparms(n+1);for (arrayindex = 0;arrayindex < argc - 1;arrayindex++) array[arrayindex] = strtol(argv[1+arrayindex], NULL, 0)
260 #define readfloats(array, n) checkparms(n+1);for (arrayindex = 0;arrayindex < argc - 1;arrayindex++) array[arrayindex] = atof(argv[1+arrayindex])
261 #define readint(var) checkparms(2);var = strtol(argv[1], NULL, 0)
262 #define readfloat(var) checkparms(2);var = atof(argv[1])
263 #define readbool(var) checkparms(2);var = strtol(argv[1], NULL, 0) != 0
264 if (!strcmp(argv[0], "effect"))
269 if (effectinfoindex >= MAX_PARTICLEEFFECTINFO)
271 Con_Printf("%s:%i: too many effects!\n", filename, linenumber);
274 for (effectnameindex = 1;effectnameindex < MAX_PARTICLEEFFECTNAME;effectnameindex++)
276 if (particleeffectname[effectnameindex][0])
278 if (!strcmp(particleeffectname[effectnameindex], argv[1]))
283 strlcpy(particleeffectname[effectnameindex], argv[1], sizeof(particleeffectname[effectnameindex]));
287 // if we run out of names, abort
288 if (effectnameindex == MAX_PARTICLEEFFECTNAME)
290 Con_Printf("%s:%i: too many effects!\n", filename, linenumber);
293 info = particleeffectinfo + effectinfoindex;
294 info->effectnameindex = effectnameindex;
295 info->particletype = pt_alphastatic;
296 info->blendmode = particletype[info->particletype].blendmode;
297 info->orientation = particletype[info->particletype].orientation;
298 info->tex[0] = tex_particle;
299 info->tex[1] = tex_particle;
300 info->color[0] = 0xFFFFFF;
301 info->color[1] = 0xFFFFFF;
305 info->alpha[1] = 256;
306 info->alpha[2] = 256;
307 info->time[0] = 9999;
308 info->time[1] = 9999;
309 VectorSet(info->lightcolor, 1, 1, 1);
310 info->lightshadow = true;
311 info->lighttime = 9999;
312 info->stretchfactor = 1;
313 info->staincolor[0] = (unsigned int)-1;
314 info->staincolor[1] = (unsigned int)-1;
315 info->staintex[0] = -1;
316 info->staintex[1] = -1;
317 info->stainalpha[0] = 1;
318 info->stainalpha[1] = 1;
319 info->stainsize[0] = 2;
320 info->stainsize[1] = 2;
322 info->rotate[1] = 360;
326 else if (info == NULL)
328 Con_Printf("%s:%i: command %s encountered before effect\n", filename, linenumber, argv[0]);
331 else if (!strcmp(argv[0], "countabsolute")) {readfloat(info->countabsolute);}
332 else if (!strcmp(argv[0], "count")) {readfloat(info->countmultiplier);}
333 else if (!strcmp(argv[0], "type"))
336 if (!strcmp(argv[1], "alphastatic")) info->particletype = pt_alphastatic;
337 else if (!strcmp(argv[1], "static")) info->particletype = pt_static;
338 else if (!strcmp(argv[1], "spark")) info->particletype = pt_spark;
339 else if (!strcmp(argv[1], "beam")) info->particletype = pt_beam;
340 else if (!strcmp(argv[1], "rain")) info->particletype = pt_rain;
341 else if (!strcmp(argv[1], "raindecal")) info->particletype = pt_raindecal;
342 else if (!strcmp(argv[1], "snow")) info->particletype = pt_snow;
343 else if (!strcmp(argv[1], "bubble")) info->particletype = pt_bubble;
344 else if (!strcmp(argv[1], "blood")) {info->particletype = pt_blood;info->gravity = 1;}
345 else if (!strcmp(argv[1], "smoke")) info->particletype = pt_smoke;
346 else if (!strcmp(argv[1], "decal")) info->particletype = pt_decal;
347 else if (!strcmp(argv[1], "entityparticle")) info->particletype = pt_entityparticle;
348 else Con_Printf("%s:%i: unrecognized particle type %s\n", filename, linenumber, argv[1]);
349 info->blendmode = particletype[info->particletype].blendmode;
350 info->orientation = particletype[info->particletype].orientation;
352 else if (!strcmp(argv[0], "blend"))
355 if (!strcmp(argv[1], "alpha")) info->blendmode = PBLEND_ALPHA;
356 else if (!strcmp(argv[1], "add")) info->blendmode = PBLEND_ADD;
357 else if (!strcmp(argv[1], "invmod")) info->blendmode = PBLEND_INVMOD;
358 else Con_Printf("%s:%i: unrecognized blendmode %s\n", filename, linenumber, argv[1]);
360 else if (!strcmp(argv[0], "orientation"))
363 if (!strcmp(argv[1], "billboard")) info->orientation = PARTICLE_BILLBOARD;
364 else if (!strcmp(argv[1], "spark")) info->orientation = PARTICLE_SPARK;
365 else if (!strcmp(argv[1], "oriented")) info->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
366 else if (!strcmp(argv[1], "beam")) info->orientation = PARTICLE_HBEAM;
367 else Con_Printf("%s:%i: unrecognized orientation %s\n", filename, linenumber, argv[1]);
369 else if (!strcmp(argv[0], "color")) {readints(info->color, 2);}
370 else if (!strcmp(argv[0], "tex")) {readints(info->tex, 2);}
371 else if (!strcmp(argv[0], "size")) {readfloats(info->size, 2);}
372 else if (!strcmp(argv[0], "sizeincrease")) {readfloat(info->size[2]);}
373 else if (!strcmp(argv[0], "alpha")) {readfloats(info->alpha, 3);}
374 else if (!strcmp(argv[0], "time")) {readfloats(info->time, 2);}
375 else if (!strcmp(argv[0], "gravity")) {readfloat(info->gravity);}
376 else if (!strcmp(argv[0], "bounce")) {readfloat(info->bounce);}
377 else if (!strcmp(argv[0], "airfriction")) {readfloat(info->airfriction);}
378 else if (!strcmp(argv[0], "liquidfriction")) {readfloat(info->liquidfriction);}
379 else if (!strcmp(argv[0], "originoffset")) {readfloats(info->originoffset, 3);}
380 else if (!strcmp(argv[0], "velocityoffset")) {readfloats(info->velocityoffset, 3);}
381 else if (!strcmp(argv[0], "originjitter")) {readfloats(info->originjitter, 3);}
382 else if (!strcmp(argv[0], "velocityjitter")) {readfloats(info->velocityjitter, 3);}
383 else if (!strcmp(argv[0], "velocitymultiplier")) {readfloat(info->velocitymultiplier);}
384 else if (!strcmp(argv[0], "lightradius")) {readfloat(info->lightradiusstart);}
385 else if (!strcmp(argv[0], "lightradiusfade")) {readfloat(info->lightradiusfade);}
386 else if (!strcmp(argv[0], "lighttime")) {readfloat(info->lighttime);}
387 else if (!strcmp(argv[0], "lightcolor")) {readfloats(info->lightcolor, 3);}
388 else if (!strcmp(argv[0], "lightshadow")) {readbool(info->lightshadow);}
389 else if (!strcmp(argv[0], "lightcubemapnum")) {readint(info->lightcubemapnum);}
390 else if (!strcmp(argv[0], "underwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_UNDERWATER;}
391 else if (!strcmp(argv[0], "notunderwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_NOTUNDERWATER;}
392 else if (!strcmp(argv[0], "trailspacing")) {readfloat(info->trailspacing);if (info->trailspacing > 0) info->countmultiplier = 1.0f / info->trailspacing;}
393 else if (!strcmp(argv[0], "stretchfactor")) {readfloat(info->stretchfactor);}
394 else if (!strcmp(argv[0], "staincolor")) {readints(info->staincolor, 2);}
395 else if (!strcmp(argv[0], "stainalpha")) {readfloats(info->stainalpha, 2);}
396 else if (!strcmp(argv[0], "stainsize")) {readfloats(info->stainsize, 2);}
397 else if (!strcmp(argv[0], "staintex")) {readints(info->staintex, 2);}
398 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; }
399 else if (!strcmp(argv[0], "rotate")) {readfloats(info->rotate, 4);}
401 Con_Printf("%s:%i: skipping unknown command %s\n", filename, linenumber, argv[0]);
410 int CL_ParticleEffectIndexForName(const char *name)
413 for (i = 1;i < MAX_PARTICLEEFFECTNAME && particleeffectname[i][0];i++)
414 if (!strcmp(particleeffectname[i], name))
419 const char *CL_ParticleEffectNameForIndex(int i)
421 if (i < 1 || i >= MAX_PARTICLEEFFECTNAME)
423 return particleeffectname[i];
426 // MUST match effectnameindex_t in client.h
427 static const char *standardeffectnames[EFFECT_TOTAL] =
451 "TE_TEI_BIGEXPLOSION",
467 void CL_Particles_LoadEffectInfo(void)
471 unsigned char *filedata;
472 fs_offset_t filesize;
473 char filename[MAX_QPATH];
474 memset(particleeffectinfo, 0, sizeof(particleeffectinfo));
475 memset(particleeffectname, 0, sizeof(particleeffectname));
476 for (i = 0;i < EFFECT_TOTAL;i++)
477 strlcpy(particleeffectname[i], standardeffectnames[i], sizeof(particleeffectname[i]));
478 for (filepass = 0;;filepass++)
481 dpsnprintf(filename, sizeof(filename), "effectinfo.txt");
482 else if (filepass == 1)
484 if (!cl.worldbasename[0])
486 dpsnprintf(filename, sizeof(filename), "%s_effectinfo.txt", cl.worldnamenoextension);
490 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
493 CL_Particles_ParseEffectInfo((const char *)filedata, (const char *)filedata + filesize, filename);
503 void CL_ReadPointFile_f (void);
504 void CL_Particles_Init (void)
506 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)");
507 Cmd_AddCommand ("cl_particles_reloadeffects", CL_Particles_LoadEffectInfo, "reloads effectinfo.txt and maps/levelname_effectinfo.txt (where levelname is the current map)");
509 Cvar_RegisterVariable (&cl_particles);
510 Cvar_RegisterVariable (&cl_particles_quality);
511 Cvar_RegisterVariable (&cl_particles_alpha);
512 Cvar_RegisterVariable (&cl_particles_size);
513 Cvar_RegisterVariable (&cl_particles_quake);
514 Cvar_RegisterVariable (&cl_particles_blood);
515 Cvar_RegisterVariable (&cl_particles_blood_alpha);
516 Cvar_RegisterVariable (&cl_particles_blood_decal_alpha);
517 Cvar_RegisterVariable (&cl_particles_blood_decal_scalemin);
518 Cvar_RegisterVariable (&cl_particles_blood_decal_scalemax);
519 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
520 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
521 Cvar_RegisterVariable (&cl_particles_explosions_shell);
522 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
523 Cvar_RegisterVariable (&cl_particles_rain);
524 Cvar_RegisterVariable (&cl_particles_snow);
525 Cvar_RegisterVariable (&cl_particles_smoke);
526 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
527 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
528 Cvar_RegisterVariable (&cl_particles_sparks);
529 Cvar_RegisterVariable (&cl_particles_bubbles);
530 Cvar_RegisterVariable (&cl_particles_visculling);
531 Cvar_RegisterVariable (&cl_particles_collisions);
532 Cvar_RegisterVariable (&cl_decals);
533 Cvar_RegisterVariable (&cl_decals_visculling);
534 Cvar_RegisterVariable (&cl_decals_time);
535 Cvar_RegisterVariable (&cl_decals_fadetime);
536 Cvar_RegisterVariable (&cl_decals_newsystem);
537 Cvar_RegisterVariable (&cl_decals_newsystem_intensitymultiplier);
538 Cvar_RegisterVariable (&cl_decals_models);
539 Cvar_RegisterVariable (&cl_decals_bias);
540 Cvar_RegisterVariable (&cl_decals_max);
543 void CL_Particles_Shutdown (void)
547 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha);
548 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2);
550 // list of all 26 parameters:
551 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
552 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
553 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
554 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_*BEAM)
555 // palpha - opacity of particle as 0-255 (can be more than 255)
556 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
557 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
558 // pgravity - how much effect gravity has on the particle (0-1)
559 // 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
560 // px,py,pz - starting origin of particle
561 // pvx,pvy,pvz - starting velocity of particle
562 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
563 // blendmode - one of the PBLEND_ values
564 // orientation - one of the PARTICLE_ values
565 // staincolor1, staincolor2: minimum and maximum ranges of stain color, randomly interpolated to decide stain color (-1 to use none)
566 // staintex: any of the tex_ values such as tex_smoke[rand()&7] or tex_particle (-1 to use none)
567 // stainalpha: opacity of the stain as factor for alpha
568 // stainsize: size of the stain as factor for palpha
569 // angle: base rotation of the particle geometry around its center normal
570 // spin: rotation speed of the particle geometry around its center normal
571 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, float angle, float spin)
576 if (!cl_particles.integer)
578 for (;cl.free_particle < cl.max_particles && cl.particles[cl.free_particle].typeindex;cl.free_particle++);
579 if (cl.free_particle >= cl.max_particles)
582 lifetime = palpha / min(1, palphafade);
583 part = &cl.particles[cl.free_particle++];
584 if (cl.num_particles < cl.free_particle)
585 cl.num_particles = cl.free_particle;
586 memset(part, 0, sizeof(*part));
587 VectorCopy(sortorigin, part->sortorigin);
588 part->typeindex = ptypeindex;
589 part->blendmode = blendmode;
590 if(orientation == PARTICLE_HBEAM || orientation == PARTICLE_VBEAM)
592 particletexture_t *tex = &particletexture[ptex];
593 if(tex->t1 == 0 && tex->t2 == 1) // full height of texture?
594 part->orientation = PARTICLE_VBEAM;
596 part->orientation = PARTICLE_HBEAM;
599 part->orientation = orientation;
600 l2 = (int)lhrandom(0.5, 256.5);
602 part->color[0] = ((((pcolor1 >> 16) & 0xFF) * l1 + ((pcolor2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
603 part->color[1] = ((((pcolor1 >> 8) & 0xFF) * l1 + ((pcolor2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
604 part->color[2] = ((((pcolor1 >> 0) & 0xFF) * l1 + ((pcolor2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
605 part->staintexnum = staintex;
606 if(staincolor1 >= 0 && staincolor2 >= 0)
608 l2 = (int)lhrandom(0.5, 256.5);
610 if(blendmode == PBLEND_INVMOD)
612 r = ((((staincolor1 >> 16) & 0xFF) * l1 + ((staincolor2 >> 16) & 0xFF) * l2) * (255 - part->color[0])) / 0x8000; // staincolor 0x808080 keeps color invariant
613 g = ((((staincolor1 >> 8) & 0xFF) * l1 + ((staincolor2 >> 8) & 0xFF) * l2) * (255 - part->color[1])) / 0x8000;
614 b = ((((staincolor1 >> 0) & 0xFF) * l1 + ((staincolor2 >> 0) & 0xFF) * l2) * (255 - part->color[2])) / 0x8000;
618 r = ((((staincolor1 >> 16) & 0xFF) * l1 + ((staincolor2 >> 16) & 0xFF) * l2) * part->color[0]) / 0x8000; // staincolor 0x808080 keeps color invariant
619 g = ((((staincolor1 >> 8) & 0xFF) * l1 + ((staincolor2 >> 8) & 0xFF) * l2) * part->color[1]) / 0x8000;
620 b = ((((staincolor1 >> 0) & 0xFF) * l1 + ((staincolor2 >> 0) & 0xFF) * l2) * part->color[2]) / 0x8000;
622 if(r > 0xFF) r = 0xFF;
623 if(g > 0xFF) g = 0xFF;
624 if(b > 0xFF) b = 0xFF;
628 r = part->color[0]; // -1 is shorthand for stain = particle color
632 part->staincolor[0] = r;
633 part->staincolor[1] = g;
634 part->staincolor[2] = b;
635 part->stainalpha = palpha * stainalpha;
636 part->stainsize = psize * stainsize;
639 part->sizeincrease = psizeincrease;
640 part->alpha = palpha;
641 part->alphafade = palphafade;
642 part->gravity = pgravity;
643 part->bounce = pbounce;
644 part->stretch = stretch;
646 part->org[0] = px + originjitter * v[0];
647 part->org[1] = py + originjitter * v[1];
648 part->org[2] = pz + originjitter * v[2];
649 part->vel[0] = pvx + velocityjitter * v[0];
650 part->vel[1] = pvy + velocityjitter * v[1];
651 part->vel[2] = pvz + velocityjitter * v[2];
653 part->airfriction = pairfriction;
654 part->liquidfriction = pliquidfriction;
655 part->die = cl.time + lifetime;
656 part->delayedspawn = cl.time;
657 // part->delayedcollisions = 0;
658 part->qualityreduction = pqualityreduction;
661 // if it is rain or snow, trace ahead and shut off collisions until an actual collision event needs to occur to improve performance
662 if (part->typeindex == pt_rain)
666 float lifetime = part->die - cl.time;
669 // turn raindrop into simple spark and create delayedspawn splash effect
670 part->typeindex = pt_spark;
672 VectorMA(part->org, lifetime, part->vel, endvec);
673 trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK, true, false, NULL, false);
674 part->die = cl.time + lifetime * trace.fraction;
675 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, 0, 0);
678 part2->delayedspawn = part->die;
679 part2->die += part->die - cl.time;
680 for (i = rand() & 7;i < 10;i++)
682 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, 0, 0);
685 part2->delayedspawn = part->die;
686 part2->die += part->die - cl.time;
692 else if (part->bounce != 0 && part->gravity == 0 && part->typeindex != pt_snow)
694 float lifetime = part->alpha / (part->alphafade ? part->alphafade : 1);
697 VectorMA(part->org, lifetime, part->vel, endvec);
698 trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY, true, false, NULL, false);
699 part->delayedcollisions = cl.time + lifetime * trace.fraction - 0.1;
706 static void CL_ImmediateBloodStain(particle_t *part)
711 // blood creates a splash at spawn, not just at impact, this makes monsters bloody where they are shot
712 if (part->staintexnum >= 0 && cl_decals_newsystem.integer && cl_decals.integer)
714 VectorCopy(part->vel, v);
716 staintex = part->staintexnum;
717 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);
720 // blood creates a splash at spawn, not just at impact, this makes monsters bloody where they are shot
721 if (part->typeindex == pt_blood && cl_decals_newsystem.integer && cl_decals.integer)
723 VectorCopy(part->vel, v);
725 staintex = tex_blooddecal[rand()&7];
726 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);
730 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
734 entity_render_t *ent = &cl.entities[hitent].render;
735 unsigned char color[3];
736 if (!cl_decals.integer)
738 if (!ent->allowdecals)
741 l2 = (int)lhrandom(0.5, 256.5);
743 color[0] = ((((color1 >> 16) & 0xFF) * l1 + ((color2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
744 color[1] = ((((color1 >> 8) & 0xFF) * l1 + ((color2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
745 color[2] = ((((color1 >> 0) & 0xFF) * l1 + ((color2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
747 if (cl_decals_newsystem.integer)
749 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);
753 for (;cl.free_decal < cl.max_decals && cl.decals[cl.free_decal].typeindex;cl.free_decal++);
754 if (cl.free_decal >= cl.max_decals)
756 decal = &cl.decals[cl.free_decal++];
757 if (cl.num_decals < cl.free_decal)
758 cl.num_decals = cl.free_decal;
759 memset(decal, 0, sizeof(*decal));
760 decal->decalsequence = cl.decalsequence++;
761 decal->typeindex = pt_decal;
762 decal->texnum = texnum;
763 VectorMA(org, cl_decals_bias.value, normal, decal->org);
764 VectorCopy(normal, decal->normal);
766 decal->alpha = alpha;
767 decal->time2 = cl.time;
768 decal->color[0] = color[0];
769 decal->color[1] = color[1];
770 decal->color[2] = color[2];
771 decal->owner = hitent;
772 decal->clusterindex = -1000; // no vis culling unless we're sure
775 // these relative things are only used to regenerate p->org and p->vel if decal->owner is not world (0)
776 decal->ownermodel = cl.entities[decal->owner].render.model;
777 Matrix4x4_Transform(&cl.entities[decal->owner].render.inversematrix, org, decal->relativeorigin);
778 Matrix4x4_Transform3x3(&cl.entities[decal->owner].render.inversematrix, normal, decal->relativenormal);
782 if(r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.PointInLeaf)
784 mleaf_t *leaf = r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, decal->org);
786 decal->clusterindex = leaf->clusterindex;
791 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
794 float bestfrac, bestorg[3], bestnormal[3];
796 int besthitent = 0, hitent;
799 for (i = 0;i < 32;i++)
802 VectorMA(org, maxdist, org2, org2);
803 trace = CL_TraceLine(org, org2, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, true, false, &hitent, false);
804 // take the closest trace result that doesn't end up hitting a NOMARKS
805 // surface (sky for example)
806 if (bestfrac > trace.fraction && !(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
808 bestfrac = trace.fraction;
810 VectorCopy(trace.endpos, bestorg);
811 VectorCopy(trace.plane.normal, bestnormal);
815 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
818 static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount);
819 static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount);
820 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)
823 matrix4x4_t tempmatrix;
825 VectorLerp(originmins, 0.5, originmaxs, center);
826 Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
827 if (effectnameindex == EFFECT_SVC_PARTICLE)
829 if (cl_particles.integer)
831 // bloodhack checks if this effect's color matches regular or lightning blood and if so spawns a blood effect instead
833 CL_ParticleExplosion(center);
834 else if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer && (palettecolor == 73 || palettecolor == 225))
835 CL_ParticleEffect(EFFECT_TE_BLOOD, count / 2.0f, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
838 count *= cl_particles_quality.value;
839 for (;count > 0;count--)
841 int k = particlepalette[(palettecolor & ~7) + (rand()&7)];
842 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, 0, 0);
847 else if (effectnameindex == EFFECT_TE_WIZSPIKE)
848 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 30*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 20);
849 else if (effectnameindex == EFFECT_TE_KNIGHTSPIKE)
850 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 226);
851 else if (effectnameindex == EFFECT_TE_SPIKE)
853 if (cl_particles_bulletimpacts.integer)
855 if (cl_particles_quake.integer)
857 if (cl_particles_smoke.integer)
858 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
862 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
863 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
864 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, 0, 0);
868 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
869 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
871 else if (effectnameindex == EFFECT_TE_SPIKEQUAD)
873 if (cl_particles_bulletimpacts.integer)
875 if (cl_particles_quake.integer)
877 if (cl_particles_smoke.integer)
878 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
882 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
883 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
884 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, 0, 0);
888 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
889 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
890 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);
892 else if (effectnameindex == EFFECT_TE_SUPERSPIKE)
894 if (cl_particles_bulletimpacts.integer)
896 if (cl_particles_quake.integer)
898 if (cl_particles_smoke.integer)
899 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
903 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
904 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
905 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, 0, 0);
909 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
910 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
912 else if (effectnameindex == EFFECT_TE_SUPERSPIKEQUAD)
914 if (cl_particles_bulletimpacts.integer)
916 if (cl_particles_quake.integer)
918 if (cl_particles_smoke.integer)
919 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
923 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
924 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
925 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, 0, 0);
929 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
930 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
931 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);
933 else if (effectnameindex == EFFECT_TE_BLOOD)
935 if (!cl_particles_blood.integer)
937 if (cl_particles_quake.integer)
938 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 2*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 73);
941 static double bloodaccumulator = 0;
942 qboolean immediatebloodstain = true;
943 //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);
944 bloodaccumulator += count * 0.333 * cl_particles_quality.value;
945 for (;bloodaccumulator > 0;bloodaccumulator--)
947 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, 0, 0);
948 if (immediatebloodstain && part)
950 immediatebloodstain = false;
951 CL_ImmediateBloodStain(part);
956 else if (effectnameindex == EFFECT_TE_SPARK)
957 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, count);
958 else if (effectnameindex == EFFECT_TE_PLASMABURN)
960 // plasma scorch mark
961 R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
962 CL_SpawnDecalParticleForPoint(center, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
963 CL_AllocLightFlash(NULL, &tempmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
965 else if (effectnameindex == EFFECT_TE_GUNSHOT)
967 if (cl_particles_bulletimpacts.integer)
969 if (cl_particles_quake.integer)
970 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
973 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
974 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
975 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, 0, 0);
979 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
980 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
982 else if (effectnameindex == EFFECT_TE_GUNSHOTQUAD)
984 if (cl_particles_bulletimpacts.integer)
986 if (cl_particles_quake.integer)
987 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
990 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
991 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
992 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, 0, 0);
996 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
997 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
998 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);
1000 else if (effectnameindex == EFFECT_TE_EXPLOSION)
1002 CL_ParticleExplosion(center);
1003 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);
1005 else if (effectnameindex == EFFECT_TE_EXPLOSIONQUAD)
1007 CL_ParticleExplosion(center);
1008 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);
1010 else if (effectnameindex == EFFECT_TE_TAREXPLOSION)
1012 if (cl_particles_quake.integer)
1015 for (i = 0;i < 1024 * cl_particles_quality.value;i++)
1018 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, 0, 0);
1020 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, 0, 0);
1024 CL_ParticleExplosion(center);
1025 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);
1027 else if (effectnameindex == EFFECT_TE_SMALLFLASH)
1028 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);
1029 else if (effectnameindex == EFFECT_TE_FLAMEJET)
1031 count *= cl_particles_quality.value;
1033 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, 0, 0);
1035 else if (effectnameindex == EFFECT_TE_LAVASPLASH)
1037 float i, j, inc, vel;
1040 inc = 8 / cl_particles_quality.value;
1041 for (i = -128;i < 128;i += inc)
1043 for (j = -128;j < 128;j += inc)
1045 dir[0] = j + lhrandom(0, inc);
1046 dir[1] = i + lhrandom(0, inc);
1048 org[0] = center[0] + dir[0];
1049 org[1] = center[1] + dir[1];
1050 org[2] = center[2] + lhrandom(0, 64);
1051 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1052 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, 0, 0);
1056 else if (effectnameindex == EFFECT_TE_TELEPORT)
1058 float i, j, k, inc, vel;
1061 if (cl_particles_quake.integer)
1062 inc = 4 / cl_particles_quality.value;
1064 inc = 8 / cl_particles_quality.value;
1065 for (i = -16;i < 16;i += inc)
1067 for (j = -16;j < 16;j += inc)
1069 for (k = -24;k < 32;k += inc)
1071 VectorSet(dir, i*8, j*8, k*8);
1072 VectorNormalize(dir);
1073 vel = lhrandom(50, 113);
1074 if (cl_particles_quake.integer)
1075 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, 0, 0);
1077 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, 0, 0);
1081 if (!cl_particles_quake.integer)
1082 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, 0, 0);
1083 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);
1085 else if (effectnameindex == EFFECT_TE_TEI_G3)
1086 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, 0, 0);
1087 else if (effectnameindex == EFFECT_TE_TEI_SMOKE)
1089 if (cl_particles_smoke.integer)
1091 count *= 0.25f * cl_particles_quality.value;
1093 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, 0, 0);
1096 else if (effectnameindex == EFFECT_TE_TEI_BIGEXPLOSION)
1098 CL_ParticleExplosion(center);
1099 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);
1101 else if (effectnameindex == EFFECT_TE_TEI_PLASMAHIT)
1104 R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
1105 CL_SpawnDecalParticleForPoint(center, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1106 if (cl_particles_smoke.integer)
1107 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1108 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, 0, 0);
1109 if (cl_particles_sparks.integer)
1110 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1111 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, 0, 0);
1112 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);
1114 else if (effectnameindex == EFFECT_EF_FLAME)
1116 count *= 300 * cl_particles_quality.value;
1118 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, 0, 0);
1119 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);
1121 else if (effectnameindex == EFFECT_EF_STARDUST)
1123 count *= 200 * cl_particles_quality.value;
1125 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, 0, 0);
1126 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);
1128 else if (!strncmp(particleeffectname[effectnameindex], "TR_", 3))
1132 int smoke, blood, bubbles, r, color;
1134 if (spawndlight && r_refdef.scene.numlights < MAX_DLIGHTS)
1137 Vector4Set(light, 0, 0, 0, 0);
1139 if (effectnameindex == EFFECT_TR_ROCKET)
1140 Vector4Set(light, 3.0f, 1.5f, 0.5f, 200);
1141 else if (effectnameindex == EFFECT_TR_VORESPIKE)
1143 if (gamemode == GAME_PRYDON && !cl_particles_quake.integer)
1144 Vector4Set(light, 0.3f, 0.6f, 1.2f, 100);
1146 Vector4Set(light, 1.2f, 0.5f, 1.0f, 200);
1148 else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
1149 Vector4Set(light, 0.75f, 1.5f, 3.0f, 200);
1153 matrix4x4_t tempmatrix;
1154 Matrix4x4_CreateFromQuakeEntity(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, light[3]);
1155 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);
1156 r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
1160 if (!spawnparticles)
1163 if (originmaxs[0] == originmins[0] && originmaxs[1] == originmins[1] && originmaxs[2] == originmins[2])
1166 VectorSubtract(originmaxs, originmins, dir);
1167 len = VectorNormalizeLength(dir);
1170 dec = -ent->persistent.trail_time;
1171 ent->persistent.trail_time += len;
1172 if (ent->persistent.trail_time < 0.01f)
1175 // if we skip out, leave it reset
1176 ent->persistent.trail_time = 0.0f;
1181 // advance into this frame to reach the first puff location
1182 VectorMA(originmins, dec, dir, pos);
1185 smoke = cl_particles.integer && cl_particles_smoke.integer;
1186 blood = cl_particles.integer && cl_particles_blood.integer;
1187 bubbles = cl_particles.integer && cl_particles_bubbles.integer && !cl_particles_quake.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1188 qd = 1.0f / cl_particles_quality.value;
1195 if (effectnameindex == EFFECT_TR_BLOOD)
1197 if (cl_particles_quake.integer)
1199 color = particlepalette[67 + (rand()&3)];
1200 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, 0, 0);
1205 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, 0, 0);
1208 else if (effectnameindex == EFFECT_TR_SLIGHTBLOOD)
1210 if (cl_particles_quake.integer)
1213 color = particlepalette[67 + (rand()&3)];
1214 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, 0, 0);
1219 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, 0, 0);
1225 if (effectnameindex == EFFECT_TR_ROCKET)
1227 if (cl_particles_quake.integer)
1230 color = particlepalette[ramp3[r]];
1231 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, 0, 0);
1235 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, 0, 0);
1236 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, 0, 0);
1239 else if (effectnameindex == EFFECT_TR_GRENADE)
1241 if (cl_particles_quake.integer)
1244 color = particlepalette[ramp3[r]];
1245 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, 0, 0);
1249 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, 0, 0);
1252 else if (effectnameindex == EFFECT_TR_WIZSPIKE)
1254 if (cl_particles_quake.integer)
1257 color = particlepalette[52 + (rand()&7)];
1258 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, 0, 0);
1259 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, 0, 0);
1261 else if (gamemode == GAME_GOODVSBAD2)
1264 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, 0, 0);
1268 color = particlepalette[20 + (rand()&7)];
1269 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, 0, 0);
1272 else if (effectnameindex == EFFECT_TR_KNIGHTSPIKE)
1274 if (cl_particles_quake.integer)
1277 color = particlepalette[230 + (rand()&7)];
1278 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, 0, 0);
1279 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, 0, 0);
1283 color = particlepalette[226 + (rand()&7)];
1284 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, 0, 0);
1287 else if (effectnameindex == EFFECT_TR_VORESPIKE)
1289 if (cl_particles_quake.integer)
1291 color = particlepalette[152 + (rand()&3)];
1292 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, 0, 0);
1294 else if (gamemode == GAME_GOODVSBAD2)
1297 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, 0, 0);
1299 else if (gamemode == GAME_PRYDON)
1302 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, 0, 0);
1305 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, 0, 0);
1307 else if (effectnameindex == EFFECT_TR_NEHAHRASMOKE)
1310 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, 0, 0);
1312 else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
1315 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, 0, 0);
1317 else if (effectnameindex == EFFECT_TR_GLOWTRAIL)
1318 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, 0, 0);
1322 if (effectnameindex == EFFECT_TR_ROCKET)
1323 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, 0, 0);
1324 else if (effectnameindex == EFFECT_TR_GRENADE)
1325 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, 0, 0);
1327 // advance to next time and position
1330 VectorMA (pos, dec, dir, pos);
1333 ent->persistent.trail_time = len;
1336 Con_DPrintf("CL_ParticleEffect_Fallback: no fallback found for effect %s\n", particleeffectname[effectnameindex]);
1339 // this is also called on point effects with spawndlight = true and
1340 // spawnparticles = true
1341 // it is called CL_ParticleTrail because most code does not want to supply
1342 // these parameters, only trail handling does
1343 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)
1345 qboolean found = false;
1346 if (effectnameindex < 1 || effectnameindex >= MAX_PARTICLEEFFECTNAME || !particleeffectname[effectnameindex][0])
1348 Con_DPrintf("Unknown effect number %i received from server\n", effectnameindex);
1349 return; // no such effect
1351 if (!cl_particles_quake.integer && particleeffectinfo[0].effectnameindex)
1353 int effectinfoindex;
1356 particleeffectinfo_t *info;
1363 qboolean underwater;
1364 qboolean immediatebloodstain;
1366 // note this runs multiple effects with the same name, each one spawns only one kind of particle, so some effects need more than one
1367 VectorLerp(originmins, 0.5, originmaxs, center);
1368 supercontents = CL_PointSuperContents(center);
1369 underwater = (supercontents & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)) != 0;
1370 VectorSubtract(originmaxs, originmins, traildir);
1371 traillen = VectorLength(traildir);
1372 VectorNormalize(traildir);
1373 for (effectinfoindex = 0, info = particleeffectinfo;effectinfoindex < MAX_PARTICLEEFFECTINFO && info->effectnameindex;effectinfoindex++, info++)
1375 if (info->effectnameindex == effectnameindex)
1378 if ((info->flags & PARTICLEEFFECT_UNDERWATER) && !underwater)
1380 if ((info->flags & PARTICLEEFFECT_NOTUNDERWATER) && underwater)
1383 // spawn a dlight if requested
1384 if (info->lightradiusstart > 0 && spawndlight)
1386 matrix4x4_t tempmatrix;
1387 if (info->trailspacing > 0)
1388 Matrix4x4_CreateTranslate(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2]);
1390 Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
1391 if (info->lighttime > 0 && info->lightradiusfade > 0)
1393 // light flash (explosion, etc)
1394 // called when effect starts
1395 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);
1397 else if (r_refdef.scene.numlights < MAX_DLIGHTS)
1400 // called by CL_LinkNetworkEntity
1401 Matrix4x4_Scale(&tempmatrix, info->lightradiusstart, 1);
1402 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);
1403 r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
1407 if (!spawnparticles)
1412 if (info->tex[1] > info->tex[0])
1414 tex = (int)lhrandom(info->tex[0], info->tex[1]);
1415 tex = min(tex, info->tex[1] - 1);
1417 if(info->staintex[0] < 0)
1418 staintex = info->staintex[0];
1421 staintex = (int)lhrandom(info->staintex[0], info->staintex[1]);
1422 staintex = min(staintex, info->staintex[1] - 1);
1424 if (info->particletype == pt_decal)
1425 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]);
1426 else if (info->orientation == PARTICLE_HBEAM)
1427 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]), 0, 0);
1430 if (!cl_particles.integer)
1432 switch (info->particletype)
1434 case pt_smoke: if (!cl_particles_smoke.integer) continue;break;
1435 case pt_spark: if (!cl_particles_sparks.integer) continue;break;
1436 case pt_bubble: if (!cl_particles_bubbles.integer) continue;break;
1437 case pt_blood: if (!cl_particles_blood.integer) continue;break;
1438 case pt_rain: if (!cl_particles_rain.integer) continue;break;
1439 case pt_snow: if (!cl_particles_snow.integer) continue;break;
1442 VectorCopy(originmins, trailpos);
1443 if (info->trailspacing > 0)
1445 info->particleaccumulator += traillen / info->trailspacing * cl_particles_quality.value;
1446 trailstep = info->trailspacing / cl_particles_quality.value;
1447 immediatebloodstain = false;
1451 info->particleaccumulator += info->countabsolute + pcount * info->countmultiplier * cl_particles_quality.value;
1453 immediatebloodstain = info->particletype == pt_blood || staintex;
1455 info->particleaccumulator = bound(0, info->particleaccumulator, 16384);
1456 for (;info->particleaccumulator >= 1;info->particleaccumulator--)
1458 if (info->tex[1] > info->tex[0])
1460 tex = (int)lhrandom(info->tex[0], info->tex[1]);
1461 tex = min(tex, info->tex[1] - 1);
1465 trailpos[0] = lhrandom(originmins[0], originmaxs[0]);
1466 trailpos[1] = lhrandom(originmins[1], originmaxs[1]);
1467 trailpos[2] = lhrandom(originmins[2], originmaxs[2]);
1470 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]), lhrandom(info->rotate[0], info->rotate[1]), lhrandom(info->rotate[2], info->rotate[3]));
1471 if (immediatebloodstain && part)
1473 immediatebloodstain = false;
1474 CL_ImmediateBloodStain(part);
1477 VectorMA(trailpos, trailstep, traildir, trailpos);
1484 CL_ParticleEffect_Fallback(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, spawndlight, spawnparticles);
1487 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)
1489 CL_ParticleTrail(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, true, true);
1497 void CL_EntityParticles (const entity_t *ent)
1500 float pitch, yaw, dist = 64, beamlength = 16, org[3], v[3];
1501 static vec3_t avelocities[NUMVERTEXNORMALS];
1502 if (!cl_particles.integer) return;
1503 if (cl.time <= cl.oldtime) return; // don't spawn new entity particles while paused
1505 Matrix4x4_OriginFromMatrix(&ent->render.matrix, org);
1507 if (!avelocities[0][0])
1508 for (i = 0;i < NUMVERTEXNORMALS * 3;i++)
1509 avelocities[0][i] = lhrandom(0, 2.55);
1511 for (i = 0;i < NUMVERTEXNORMALS;i++)
1513 yaw = cl.time * avelocities[i][0];
1514 pitch = cl.time * avelocities[i][1];
1515 v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
1516 v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
1517 v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
1518 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, 0, 0);
1523 void CL_ReadPointFile_f (void)
1525 vec3_t org, leakorg;
1527 char *pointfile = NULL, *pointfilepos, *t, tchar;
1528 char name[MAX_QPATH];
1533 dpsnprintf(name, sizeof(name), "%s.pts", cl.worldnamenoextension);
1534 pointfile = (char *)FS_LoadFile(name, tempmempool, true, NULL);
1537 Con_Printf("Could not open %s\n", name);
1541 Con_Printf("Reading %s...\n", name);
1542 VectorClear(leakorg);
1545 pointfilepos = pointfile;
1546 while (*pointfilepos)
1548 while (*pointfilepos == '\n' || *pointfilepos == '\r')
1553 while (*t && *t != '\n' && *t != '\r')
1557 #if _MSC_VER >= 1400
1558 #define sscanf sscanf_s
1560 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
1566 VectorCopy(org, leakorg);
1569 if (cl.num_particles < cl.max_particles - 3)
1572 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, 0, 0);
1575 Mem_Free(pointfile);
1576 VectorCopy(leakorg, org);
1577 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
1579 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, 0, 0);
1580 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, 0, 0);
1581 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, 0, 0);
1586 CL_ParseParticleEffect
1588 Parse an effect out of the server message
1591 void CL_ParseParticleEffect (void)
1594 int i, count, msgcount, color;
1596 MSG_ReadVector(org, cls.protocol);
1597 for (i=0 ; i<3 ; i++)
1598 dir[i] = MSG_ReadChar () * (1.0 / 16.0);
1599 msgcount = MSG_ReadByte ();
1600 color = MSG_ReadByte ();
1602 if (msgcount == 255)
1607 CL_ParticleEffect(EFFECT_SVC_PARTICLE, count, org, org, dir, dir, NULL, color);
1612 CL_ParticleExplosion
1616 void CL_ParticleExplosion (const vec3_t org)
1622 R_Stain(org, 96, 40, 40, 40, 64, 88, 88, 88, 64);
1623 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1625 if (cl_particles_quake.integer)
1627 for (i = 0;i < 1024;i++)
1633 color = particlepalette[ramp1[r]];
1634 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, 0, 0);
1638 color = particlepalette[ramp2[r]];
1639 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, 0, 0);
1645 i = CL_PointSuperContents(org);
1646 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
1648 if (cl_particles.integer && cl_particles_bubbles.integer)
1649 for (i = 0;i < 128 * cl_particles_quality.value;i++)
1650 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, 0, 0);
1654 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
1656 for (i = 0;i < 512 * cl_particles_quality.value;i++)
1660 for (k = 0;k < 16;k++)
1663 VectorMA(org, 128, v2, v);
1664 trace = CL_TraceLine(org, v, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
1665 if (trace.fraction >= 0.1)
1668 VectorSubtract(trace.endpos, org, v2);
1669 VectorScale(v2, 2.0f, v2);
1670 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, 0, 0);
1676 if (cl_particles_explosions_shell.integer)
1677 R_NewExplosion(org);
1682 CL_ParticleExplosion2
1686 void CL_ParticleExplosion2 (const vec3_t org, int colorStart, int colorLength)
1689 if (!cl_particles.integer) return;
1691 for (i = 0;i < 512 * cl_particles_quality.value;i++)
1693 k = particlepalette[colorStart + (i % colorLength)];
1694 if (cl_particles_quake.integer)
1695 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, 0, 0);
1697 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, 0, 0);
1701 static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount)
1704 VectorMAM(0.5f, originmins, 0.5f, originmaxs, center);
1705 if (cl_particles_sparks.integer)
1707 sparkcount *= cl_particles_quality.value;
1708 while(sparkcount-- > 0)
1709 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, 0, 0);
1713 static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount)
1716 VectorMAM(0.5f, originmins, 0.5f, originmaxs, center);
1717 if (cl_particles_smoke.integer)
1719 smokecount *= cl_particles_quality.value;
1720 while(smokecount-- > 0)
1721 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, 0, 0);
1725 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)
1729 if (!cl_particles.integer) return;
1730 VectorMAM(0.5f, mins, 0.5f, maxs, center);
1732 count = (int)(count * cl_particles_quality.value);
1735 k = particlepalette[colorbase + (rand()&3)];
1736 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, 0, 0);
1740 void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, int count, int colorbase, int type)
1743 float minz, maxz, lifetime = 30;
1745 if (!cl_particles.integer) return;
1746 if (dir[2] < 0) // falling
1748 minz = maxs[2] + dir[2] * 0.1;
1751 lifetime = (maxz - cl.worldmodel->normalmins[2]) / max(1, -dir[2]);
1756 maxz = maxs[2] + dir[2] * 0.1;
1758 lifetime = (cl.worldmodel->normalmaxs[2] - minz) / max(1, dir[2]);
1761 count = (int)(count * cl_particles_quality.value);
1766 if (!cl_particles_rain.integer) break;
1767 count *= 4; // ick, this should be in the mod or maps?
1771 k = particlepalette[colorbase + (rand()&3)];
1772 VectorSet(org, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz));
1773 if (gamemode == GAME_GOODVSBAD2)
1774 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, 0, 0);
1776 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, 0, 0);
1780 if (!cl_particles_snow.integer) break;
1783 k = particlepalette[colorbase + (rand()&3)];
1784 VectorSet(org, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz));
1785 if (gamemode == GAME_GOODVSBAD2)
1786 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, 0, 0);
1788 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, 0, 0);
1792 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
1796 static cvar_t r_drawparticles = {0, "r_drawparticles", "1", "enables drawing of particles"};
1797 static cvar_t r_drawparticles_drawdistance = {CVAR_SAVE, "r_drawparticles_drawdistance", "2000", "particles further than drawdistance*size will not be drawn"};
1798 static cvar_t r_drawdecals = {0, "r_drawdecals", "1", "enables drawing of decals"};
1799 static cvar_t r_drawdecals_drawdistance = {CVAR_SAVE, "r_drawdecals_drawdistance", "500", "decals further than drawdistance*size will not be drawn"};
1801 #define PARTICLETEXTURESIZE 64
1802 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1804 static unsigned char shadebubble(float dx, float dy, vec3_t light)
1808 dz = 1 - (dx*dx+dy*dy);
1809 if (dz > 0) // it does hit the sphere
1813 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1814 VectorNormalize(normal);
1815 dot = DotProduct(normal, light);
1816 if (dot > 0.5) // interior reflection
1817 f += ((dot * 2) - 1);
1818 else if (dot < -0.5) // exterior reflection
1819 f += ((dot * -2) - 1);
1821 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1822 VectorNormalize(normal);
1823 dot = DotProduct(normal, light);
1824 if (dot > 0.5) // interior reflection
1825 f += ((dot * 2) - 1);
1826 else if (dot < -0.5) // exterior reflection
1827 f += ((dot * -2) - 1);
1829 f += 16; // just to give it a haze so you can see the outline
1830 f = bound(0, f, 255);
1831 return (unsigned char) f;
1837 int particlefontwidth, particlefontheight, particlefontcellwidth, particlefontcellheight, particlefontrows, particlefontcols;
1838 void CL_Particle_PixelCoordsForTexnum(int texnum, int *basex, int *basey, int *width, int *height)
1840 *basex = (texnum % particlefontcols) * particlefontcellwidth;
1841 *basey = ((texnum / particlefontcols) % particlefontrows) * particlefontcellheight;
1842 *width = particlefontcellwidth;
1843 *height = particlefontcellheight;
1846 static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
1848 int basex, basey, w, h, y;
1849 CL_Particle_PixelCoordsForTexnum(texnum, &basex, &basey, &w, &h);
1850 if(w != PARTICLETEXTURESIZE || h != PARTICLETEXTURESIZE)
1851 Sys_Error("invalid particle texture size for autogenerating");
1852 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1853 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1856 void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
1859 float cx, cy, dx, dy, f, iradius;
1861 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1862 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1863 iradius = 1.0f / radius;
1864 alpha *= (1.0f / 255.0f);
1865 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1867 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1871 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1876 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1877 d[0] += (int)(f * (blue - d[0]));
1878 d[1] += (int)(f * (green - d[1]));
1879 d[2] += (int)(f * (red - d[2]));
1885 void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1888 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1890 data[0] = bound(minb, data[0], maxb);
1891 data[1] = bound(ming, data[1], maxg);
1892 data[2] = bound(minr, data[2], maxr);
1896 void particletextureinvert(unsigned char *data)
1899 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1901 data[0] = 255 - data[0];
1902 data[1] = 255 - data[1];
1903 data[2] = 255 - data[2];
1907 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1908 static void R_InitBloodTextures (unsigned char *particletexturedata)
1911 size_t datasize = PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4;
1912 unsigned char *data = Mem_Alloc(tempmempool, datasize);
1915 for (i = 0;i < 8;i++)
1917 memset(data, 255, datasize);
1918 for (k = 0;k < 24;k++)
1919 particletextureblotch(data, PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1920 //particletextureclamp(data, 32, 32, 32, 255, 255, 255);
1921 particletextureinvert(data);
1922 setuptex(tex_bloodparticle[i], data, particletexturedata);
1926 for (i = 0;i < 8;i++)
1928 memset(data, 255, datasize);
1930 for (j = 1;j < 10;j++)
1931 for (k = min(j, m - 1);k < m;k++)
1932 particletextureblotch(data, (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 320 - j * 8);
1933 //particletextureclamp(data, 32, 32, 32, 255, 255, 255);
1934 particletextureinvert(data);
1935 setuptex(tex_blooddecal[i], data, particletexturedata);
1941 //uncomment this to make engine save out particle font to a tga file when run
1942 //#define DUMPPARTICLEFONT
1944 static void R_InitParticleTexture (void)
1946 int x, y, d, i, k, m;
1947 int basex, basey, w, h;
1948 float dx, dy, f, s1, t1, s2, t2;
1951 fs_offset_t filesize;
1952 char texturename[MAX_QPATH];
1954 // a note: decals need to modulate (multiply) the background color to
1955 // properly darken it (stain), and they need to be able to alpha fade,
1956 // this is a very difficult challenge because it means fading to white
1957 // (no change to background) rather than black (darkening everything
1958 // behind the whole decal polygon), and to accomplish this the texture is
1959 // inverted (dark red blood on white background becomes brilliant cyan
1960 // and white on black background) so we can alpha fade it to black, then
1961 // we invert it again during the blendfunc to make it work...
1963 #ifndef DUMPPARTICLEFONT
1964 decalskinframe = R_SkinFrame_LoadExternal("particles/particlefont.tga", TEXF_ALPHA | TEXF_FORCELINEAR, false);
1967 particlefonttexture = decalskinframe->base;
1968 // TODO maybe allow custom grid size?
1969 particlefontwidth = image_width;
1970 particlefontheight = image_height;
1971 particlefontcellwidth = image_width / 8;
1972 particlefontcellheight = image_height / 8;
1973 particlefontcols = 8;
1974 particlefontrows = 8;
1979 unsigned char *particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1980 size_t datasize = PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4;
1981 unsigned char *data = (unsigned char *)Mem_Alloc(tempmempool, datasize);
1982 unsigned char *noise1 = (unsigned char *)Mem_Alloc(tempmempool, PARTICLETEXTURESIZE*2*PARTICLETEXTURESIZE*2);
1983 unsigned char *noise2 = (unsigned char *)Mem_Alloc(tempmempool, PARTICLETEXTURESIZE*2*PARTICLETEXTURESIZE*2);
1985 particlefontwidth = particlefontheight = PARTICLEFONTSIZE;
1986 particlefontcellwidth = particlefontcellheight = PARTICLETEXTURESIZE;
1987 particlefontcols = 8;
1988 particlefontrows = 8;
1990 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1993 for (i = 0;i < 8;i++)
1995 memset(data, 255, datasize);
1998 fractalnoise(noise1, PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1999 fractalnoise(noise2, PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
2001 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2003 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2004 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2006 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2007 d = (noise2[y*PARTICLETEXTURESIZE*2+x] - 128) * 3 + 192;
2009 d = (int)(d * (1-(dx*dx+dy*dy)));
2010 d = (d * noise1[y*PARTICLETEXTURESIZE*2+x]) >> 7;
2011 d = bound(0, d, 255);
2012 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (unsigned char) d;
2019 setuptex(tex_smoke[i], data, particletexturedata);
2023 memset(data, 255, datasize);
2024 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2026 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2027 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2029 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2030 f = 255.0f * (1.0 - 4.0f * fabs(10.0f - sqrt(dx*dx+dy*dy)));
2031 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (int) (bound(0.0f, f, 255.0f));
2034 setuptex(tex_rainsplash, data, particletexturedata);
2037 memset(data, 255, datasize);
2038 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2040 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2041 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2043 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2044 d = (int)(256 * (1 - (dx*dx+dy*dy)));
2045 d = bound(0, d, 255);
2046 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (unsigned char) d;
2049 setuptex(tex_particle, data, particletexturedata);
2052 memset(data, 255, datasize);
2053 light[0] = 1;light[1] = 1;light[2] = 1;
2054 VectorNormalize(light);
2055 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2057 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2058 // stretch upper half of bubble by +50% and shrink lower half by -50%
2059 // (this gives an elongated teardrop shape)
2061 dy = (dy - 0.5f) * 2.0f;
2063 dy = (dy - 0.5f) / 1.5f;
2064 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2066 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2067 // shrink bubble width to half
2069 data[(y*PARTICLETEXTURESIZE+x)*4+3] = shadebubble(dx, dy, light);
2072 setuptex(tex_raindrop, data, particletexturedata);
2075 memset(data, 255, datasize);
2076 light[0] = 1;light[1] = 1;light[2] = 1;
2077 VectorNormalize(light);
2078 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2080 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2081 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2083 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2084 data[(y*PARTICLETEXTURESIZE+x)*4+3] = shadebubble(dx, dy, light);
2087 setuptex(tex_bubble, data, particletexturedata);
2089 // Blood particles and blood decals
2090 R_InitBloodTextures (particletexturedata);
2093 for (i = 0;i < 8;i++)
2095 memset(data, 255, datasize);
2096 for (k = 0;k < 12;k++)
2097 particletextureblotch(data, PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
2098 for (k = 0;k < 3;k++)
2099 particletextureblotch(data, PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
2100 //particletextureclamp(data, 64, 64, 64, 255, 255, 255);
2101 particletextureinvert(data);
2102 setuptex(tex_bulletdecal[i], data, particletexturedata);
2105 #ifdef DUMPPARTICLEFONT
2106 Image_WriteTGABGRA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
2109 decalskinframe = R_SkinFrame_LoadInternalBGRA("particlefont", TEXF_ALPHA | TEXF_FORCELINEAR, particletexturedata, PARTICLEFONTSIZE, PARTICLEFONTSIZE);
2110 particlefonttexture = decalskinframe->base;
2112 Mem_Free(particletexturedata);
2117 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
2119 CL_Particle_PixelCoordsForTexnum(i, &basex, &basey, &w, &h);
2120 particletexture[i].texture = particlefonttexture;
2121 particletexture[i].s1 = (basex + 1) / (float)particlefontwidth;
2122 particletexture[i].t1 = (basey + 1) / (float)particlefontheight;
2123 particletexture[i].s2 = (basex + w - 1) / (float)particlefontwidth;
2124 particletexture[i].t2 = (basey + h - 1) / (float)particlefontheight;
2127 #ifndef DUMPPARTICLEFONT
2128 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", false, TEXF_ALPHA | TEXF_FORCELINEAR, true, r_texture_convertsRGB_particles.integer);
2129 if (!particletexture[tex_beam].texture)
2132 unsigned char noise3[64][64], data2[64][16][4];
2134 fractalnoise(&noise3[0][0], 64, 4);
2136 for (y = 0;y < 64;y++)
2138 dy = (y - 0.5f*64) / (64*0.5f-1);
2139 for (x = 0;x < 16;x++)
2141 dx = (x - 0.5f*16) / (16*0.5f-2);
2142 d = (int)((1 - sqrt(fabs(dx))) * noise3[y][x]);
2143 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
2144 data2[y][x][3] = 255;
2148 #ifdef DUMPPARTICLEFONT
2149 Image_WriteTGABGRA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
2151 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_FORCELINEAR, NULL);
2153 particletexture[tex_beam].s1 = 0;
2154 particletexture[tex_beam].t1 = 0;
2155 particletexture[tex_beam].s2 = 1;
2156 particletexture[tex_beam].t2 = 1;
2158 // now load an texcoord/texture override file
2159 buf = (char *) FS_LoadFile("particles/particlefont.txt", tempmempool, false, &filesize);
2166 if(!COM_ParseToken_Simple(&bufptr, true, false))
2168 if(!strcmp(com_token, "\n"))
2169 continue; // empty line
2170 i = atoi(com_token);
2178 if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
2180 s1 = atof(com_token);
2181 if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
2183 t1 = atof(com_token);
2184 if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
2186 s2 = atof(com_token);
2187 if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
2189 t2 = atof(com_token);
2190 strlcpy(texturename, "particles/particlefont.tga", sizeof(texturename));
2191 if (COM_ParseToken_Simple(&bufptr, true, false) && strcmp(com_token, "\n"))
2192 strlcpy(texturename, com_token, sizeof(texturename));
2199 strlcpy(texturename, com_token, sizeof(texturename));
2202 if (!texturename[0])
2204 Con_Printf("particles/particlefont.txt: syntax should be texnum x1 y1 x2 y2 texturename or texnum x1 y1 x2 y2 or texnum texturename\n");
2207 if (i < 0 || i >= MAX_PARTICLETEXTURES)
2209 Con_Printf("particles/particlefont.txt: texnum %i outside valid range (0 to %i)\n", i, MAX_PARTICLETEXTURES);
2212 particletexture[i].texture = R_SkinFrame_LoadExternal(texturename, TEXF_ALPHA | TEXF_FORCELINEAR, false)->base;
2213 particletexture[i].s1 = s1;
2214 particletexture[i].t1 = t1;
2215 particletexture[i].s2 = s2;
2216 particletexture[i].t2 = t2;
2222 static void r_part_start(void)
2225 // generate particlepalette for convenience from the main one
2226 for (i = 0;i < 256;i++)
2227 particlepalette[i] = palette_rgb[i][0] * 65536 + palette_rgb[i][1] * 256 + palette_rgb[i][2];
2228 particletexturepool = R_AllocTexturePool();
2229 R_InitParticleTexture ();
2230 CL_Particles_LoadEffectInfo();
2233 static void r_part_shutdown(void)
2235 R_FreeTexturePool(&particletexturepool);
2238 static void r_part_newmap(void)
2241 R_SkinFrame_MarkUsed(decalskinframe);
2242 CL_Particles_LoadEffectInfo();
2245 #define BATCHSIZE 256
2246 unsigned short particle_elements[BATCHSIZE*6];
2247 float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16];
2249 void R_Particles_Init (void)
2252 for (i = 0;i < BATCHSIZE;i++)
2254 particle_elements[i*6+0] = i*4+0;
2255 particle_elements[i*6+1] = i*4+1;
2256 particle_elements[i*6+2] = i*4+2;
2257 particle_elements[i*6+3] = i*4+0;
2258 particle_elements[i*6+4] = i*4+2;
2259 particle_elements[i*6+5] = i*4+3;
2262 Cvar_RegisterVariable(&r_drawparticles);
2263 Cvar_RegisterVariable(&r_drawparticles_drawdistance);
2264 Cvar_RegisterVariable(&r_drawdecals);
2265 Cvar_RegisterVariable(&r_drawdecals_drawdistance);
2266 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
2269 void R_DrawDecal_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2271 int surfacelistindex;
2273 float *v3f, *t2f, *c4f;
2274 particletexture_t *tex;
2275 float right[3], up[3], size, ca;
2276 float alphascale = (1.0f / 65536.0f) * cl_particles_alpha.value;
2278 RSurf_ActiveWorldEntity();
2280 r_refdef.stats.drawndecals += numsurfaces;
2281 R_Mesh_ResetTextureState();
2282 GL_DepthMask(false);
2283 GL_DepthRange(0, 1);
2284 GL_PolygonOffset(0, 0);
2286 GL_CullFace(GL_NONE);
2288 // generate all the vertices at once
2289 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
2291 d = cl.decals + surfacelist[surfacelistindex];
2294 c4f = particle_color4f + 16*surfacelistindex;
2295 ca = d->alpha * alphascale;
2296 // ensure alpha multiplier saturates properly
2297 if (ca > 1.0f / 256.0f)
2299 if (r_refdef.fogenabled)
2300 ca *= RSurf_FogVertex(d->org);
2301 Vector4Set(c4f, d->color[0] * ca, d->color[1] * ca, d->color[2] * ca, 1);
2302 Vector4Copy(c4f, c4f + 4);
2303 Vector4Copy(c4f, c4f + 8);
2304 Vector4Copy(c4f, c4f + 12);
2306 // calculate vertex positions
2307 size = d->size * cl_particles_size.value;
2308 VectorVectors(d->normal, right, up);
2309 VectorScale(right, size, right);
2310 VectorScale(up, size, up);
2311 v3f = particle_vertex3f + 12*surfacelistindex;
2312 v3f[ 0] = d->org[0] - right[0] - up[0];
2313 v3f[ 1] = d->org[1] - right[1] - up[1];
2314 v3f[ 2] = d->org[2] - right[2] - up[2];
2315 v3f[ 3] = d->org[0] - right[0] + up[0];
2316 v3f[ 4] = d->org[1] - right[1] + up[1];
2317 v3f[ 5] = d->org[2] - right[2] + up[2];
2318 v3f[ 6] = d->org[0] + right[0] + up[0];
2319 v3f[ 7] = d->org[1] + right[1] + up[1];
2320 v3f[ 8] = d->org[2] + right[2] + up[2];
2321 v3f[ 9] = d->org[0] + right[0] - up[0];
2322 v3f[10] = d->org[1] + right[1] - up[1];
2323 v3f[11] = d->org[2] + right[2] - up[2];
2325 // calculate texcoords
2326 tex = &particletexture[d->texnum];
2327 t2f = particle_texcoord2f + 8*surfacelistindex;
2328 t2f[0] = tex->s1;t2f[1] = tex->t2;
2329 t2f[2] = tex->s1;t2f[3] = tex->t1;
2330 t2f[4] = tex->s2;t2f[5] = tex->t1;
2331 t2f[6] = tex->s2;t2f[7] = tex->t2;
2334 // now render the decals all at once
2335 // (this assumes they all use one particle font texture!)
2336 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2337 R_SetupShader_Generic(particletexture[63].texture, NULL, GL_MODULATE, 1);
2338 R_Mesh_PrepareVertices_Generic_Arrays(numsurfaces * 4, particle_vertex3f, particle_color4f, particle_texcoord2f);
2339 R_Mesh_Draw(0, numsurfaces * 4, 0, numsurfaces * 2, NULL, NULL, 0, particle_elements, NULL, 0);
2342 void R_DrawDecals (void)
2345 int drawdecals = r_drawdecals.integer;
2350 int killsequence = cl.decalsequence - max(0, cl_decals_max.integer);
2352 frametime = bound(0, cl.time - cl.decals_updatetime, 1);
2353 cl.decals_updatetime = bound(cl.time - 1, cl.decals_updatetime + frametime, cl.time + 1);
2355 // LordHavoc: early out conditions
2359 decalfade = frametime * 256 / cl_decals_fadetime.value;
2360 drawdist2 = r_drawdecals_drawdistance.value * r_refdef.view.quality;
2361 drawdist2 = drawdist2*drawdist2;
2363 for (i = 0, decal = cl.decals;i < cl.num_decals;i++, decal++)
2365 if (!decal->typeindex)
2368 if (killsequence - decal->decalsequence > 0)
2371 if (cl.time > decal->time2 + cl_decals_time.value)
2373 decal->alpha -= decalfade;
2374 if (decal->alpha <= 0)
2380 if (cl.entities[decal->owner].render.model == decal->ownermodel)
2382 Matrix4x4_Transform(&cl.entities[decal->owner].render.matrix, decal->relativeorigin, decal->org);
2383 Matrix4x4_Transform3x3(&cl.entities[decal->owner].render.matrix, decal->relativenormal, decal->normal);
2389 if(cl_decals_visculling.integer && decal->clusterindex > -1000 && !CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, decal->clusterindex))
2395 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))
2396 R_MeshQueue_AddTransparent(decal->org, R_DrawDecal_TransparentCallback, NULL, i, NULL);
2399 decal->typeindex = 0;
2400 if (cl.free_decal > i)
2404 // reduce cl.num_decals if possible
2405 while (cl.num_decals > 0 && cl.decals[cl.num_decals - 1].typeindex == 0)
2408 if (cl.num_decals == cl.max_decals && cl.max_decals < MAX_DECALS)
2410 decal_t *olddecals = cl.decals;
2411 cl.max_decals = min(cl.max_decals * 2, MAX_DECALS);
2412 cl.decals = (decal_t *) Mem_Alloc(cls.levelmempool, cl.max_decals * sizeof(decal_t));
2413 memcpy(cl.decals, olddecals, cl.num_decals * sizeof(decal_t));
2414 Mem_Free(olddecals);
2417 r_refdef.stats.totaldecals = cl.num_decals;
2420 void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2422 int surfacelistindex;
2423 int batchstart, batchcount;
2424 const particle_t *p;
2426 rtexture_t *texture;
2427 float *v3f, *t2f, *c4f;
2428 particletexture_t *tex;
2429 float up2[3], v[3], right[3], up[3], fog, ifog, size, len, lenfactor, alpha;
2430 float ambient[3], diffuse[3], diffusenormal[3];
2431 float spintime, spinrad, spincos, spinsin, spinm1, spinm2, spinm3, spinm4, baseright[3], baseup[3];
2432 vec4_t colormultiplier;
2434 RSurf_ActiveWorldEntity();
2436 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));
2438 r_refdef.stats.particles += numsurfaces;
2439 R_Mesh_ResetTextureState();
2440 GL_DepthMask(false);
2441 GL_DepthRange(0, 1);
2442 GL_PolygonOffset(0, 0);
2444 GL_AlphaTest(false);
2445 GL_CullFace(GL_NONE);
2447 spintime = r_refdef.scene.time;
2449 // first generate all the vertices at once
2450 for (surfacelistindex = 0, v3f = particle_vertex3f, t2f = particle_texcoord2f, c4f = particle_color4f;surfacelistindex < numsurfaces;surfacelistindex++, v3f += 3*4, t2f += 2*4, c4f += 4*4)
2452 p = cl.particles + surfacelist[surfacelistindex];
2454 blendmode = (pblend_t)p->blendmode;
2458 case PBLEND_INVALID:
2460 alpha = p->alpha * colormultiplier[3];
2461 // ensure alpha multiplier saturates properly
2464 // additive and modulate can just fade out in fog (this is correct)
2465 if (r_refdef.fogenabled)
2466 alpha *= RSurf_FogVertex(p->org);
2467 // collapse alpha into color for these blends (so that the particlefont does not need alpha on most textures)
2468 alpha *= 1.0f / 256.0f;
2469 c4f[0] = p->color[0] * alpha;
2470 c4f[1] = p->color[1] * alpha;
2471 c4f[2] = p->color[2] * alpha;
2475 alpha = p->alpha * colormultiplier[3];
2476 // ensure alpha multiplier saturates properly
2479 // additive and modulate can just fade out in fog (this is correct)
2480 if (r_refdef.fogenabled)
2481 alpha *= RSurf_FogVertex(p->org);
2482 // collapse alpha into color for these blends (so that the particlefont does not need alpha on most textures)
2483 c4f[0] = p->color[0] * colormultiplier[0] * alpha;
2484 c4f[1] = p->color[1] * colormultiplier[1] * alpha;
2485 c4f[2] = p->color[2] * colormultiplier[2] * alpha;
2489 c4f[0] = p->color[0] * colormultiplier[0];
2490 c4f[1] = p->color[1] * colormultiplier[1];
2491 c4f[2] = p->color[2] * colormultiplier[2];
2492 c4f[3] = p->alpha * colormultiplier[3];
2493 // note: lighting is not cheap!
2494 if (particletype[p->typeindex].lighting)
2496 R_CompleteLightPoint(ambient, diffuse, diffusenormal, p->org, true);
2497 c4f[0] *= (ambient[0] + 0.5 * diffuse[0]);
2498 c4f[1] *= (ambient[1] + 0.5 * diffuse[1]);
2499 c4f[2] *= (ambient[2] + 0.5 * diffuse[2]);
2501 // mix in the fog color
2502 if (r_refdef.fogenabled)
2504 fog = RSurf_FogVertex(p->org);
2506 c4f[0] = c4f[0] * fog + r_refdef.fogcolor[0] * ifog;
2507 c4f[1] = c4f[1] * fog + r_refdef.fogcolor[1] * ifog;
2508 c4f[2] = c4f[2] * fog + r_refdef.fogcolor[2] * ifog;
2512 // copy the color into the other three vertices
2513 Vector4Copy(c4f, c4f + 4);
2514 Vector4Copy(c4f, c4f + 8);
2515 Vector4Copy(c4f, c4f + 12);
2517 size = p->size * cl_particles_size.value;
2518 tex = &particletexture[p->texnum];
2519 switch(p->orientation)
2521 // case PARTICLE_INVALID:
2522 case PARTICLE_BILLBOARD:
2523 if (p->angle + p->spin)
2525 spinrad = (p->angle + p->spin * (spintime - p->delayedspawn)) * (float)(M_PI / 180.0f);
2526 spinsin = sin(spinrad) * size;
2527 spincos = cos(spinrad) * size;
2528 spinm1 = -p->stretch * spincos;
2531 spinm4 = -p->stretch * spincos;
2532 VectorMAM(spinm1, r_refdef.view.left, spinm2, r_refdef.view.up, right);
2533 VectorMAM(spinm3, r_refdef.view.left, spinm4, r_refdef.view.up, up);
2537 VectorScale(r_refdef.view.left, -size * p->stretch, right);
2538 VectorScale(r_refdef.view.up, size, up);
2541 v3f[ 0] = p->org[0] - right[0] - up[0];
2542 v3f[ 1] = p->org[1] - right[1] - up[1];
2543 v3f[ 2] = p->org[2] - right[2] - up[2];
2544 v3f[ 3] = p->org[0] - right[0] + up[0];
2545 v3f[ 4] = p->org[1] - right[1] + up[1];
2546 v3f[ 5] = p->org[2] - right[2] + up[2];
2547 v3f[ 6] = p->org[0] + right[0] + up[0];
2548 v3f[ 7] = p->org[1] + right[1] + up[1];
2549 v3f[ 8] = p->org[2] + right[2] + up[2];
2550 v3f[ 9] = p->org[0] + right[0] - up[0];
2551 v3f[10] = p->org[1] + right[1] - up[1];
2552 v3f[11] = p->org[2] + right[2] - up[2];
2553 t2f[0] = tex->s1;t2f[1] = tex->t2;
2554 t2f[2] = tex->s1;t2f[3] = tex->t1;
2555 t2f[4] = tex->s2;t2f[5] = tex->t1;
2556 t2f[6] = tex->s2;t2f[7] = tex->t2;
2558 case PARTICLE_ORIENTED_DOUBLESIDED:
2559 VectorVectors(p->vel, baseright, baseup);
2560 if (p->angle + p->spin)
2562 spinrad = (p->angle + p->spin * (spintime - p->delayedspawn)) * (float)(M_PI / 180.0f);
2563 spinsin = sin(spinrad) * size;
2564 spincos = cos(spinrad) * size;
2565 spinm1 = p->stretch * spincos;
2568 spinm4 = p->stretch * spincos;
2569 VectorMAM(spinm1, baseright, spinm2, baseup, right);
2570 VectorMAM(spinm3, baseright, spinm4, baseup, up);
2574 VectorScale(baseright, size * p->stretch, right);
2575 VectorScale(baseup, size, up);
2577 v3f[ 0] = p->org[0] - right[0] - up[0];
2578 v3f[ 1] = p->org[1] - right[1] - up[1];
2579 v3f[ 2] = p->org[2] - right[2] - up[2];
2580 v3f[ 3] = p->org[0] - right[0] + up[0];
2581 v3f[ 4] = p->org[1] - right[1] + up[1];
2582 v3f[ 5] = p->org[2] - right[2] + up[2];
2583 v3f[ 6] = p->org[0] + right[0] + up[0];
2584 v3f[ 7] = p->org[1] + right[1] + up[1];
2585 v3f[ 8] = p->org[2] + right[2] + up[2];
2586 v3f[ 9] = p->org[0] + right[0] - up[0];
2587 v3f[10] = p->org[1] + right[1] - up[1];
2588 v3f[11] = p->org[2] + right[2] - up[2];
2589 t2f[0] = tex->s1;t2f[1] = tex->t2;
2590 t2f[2] = tex->s1;t2f[3] = tex->t1;
2591 t2f[4] = tex->s2;t2f[5] = tex->t1;
2592 t2f[6] = tex->s2;t2f[7] = tex->t2;
2594 case PARTICLE_SPARK:
2595 len = VectorLength(p->vel);
2596 VectorNormalize2(p->vel, up);
2597 lenfactor = p->stretch * 0.04 * len;
2598 if(lenfactor < size * 0.5)
2599 lenfactor = size * 0.5;
2600 VectorMA(p->org, -lenfactor, up, v);
2601 VectorMA(p->org, lenfactor, up, up2);
2602 R_CalcBeam_Vertex3f(v3f, v, up2, size);
2603 t2f[0] = tex->s1;t2f[1] = tex->t2;
2604 t2f[2] = tex->s1;t2f[3] = tex->t1;
2605 t2f[4] = tex->s2;t2f[5] = tex->t1;
2606 t2f[6] = tex->s2;t2f[7] = tex->t2;
2608 case PARTICLE_VBEAM:
2609 R_CalcBeam_Vertex3f(v3f, p->org, p->vel, size);
2610 VectorSubtract(p->vel, p->org, up);
2611 VectorNormalize(up);
2612 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) * p->stretch;
2613 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f) * p->stretch;
2614 t2f[0] = tex->s2;t2f[1] = v[0];
2615 t2f[2] = tex->s1;t2f[3] = v[0];
2616 t2f[4] = tex->s1;t2f[5] = v[1];
2617 t2f[6] = tex->s2;t2f[7] = v[1];
2619 case PARTICLE_HBEAM:
2620 R_CalcBeam_Vertex3f(v3f, p->org, p->vel, size);
2621 VectorSubtract(p->vel, p->org, up);
2622 VectorNormalize(up);
2623 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) * p->stretch;
2624 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f) * p->stretch;
2625 t2f[0] = v[0];t2f[1] = tex->t1;
2626 t2f[2] = v[0];t2f[3] = tex->t2;
2627 t2f[4] = v[1];t2f[5] = tex->t2;
2628 t2f[6] = v[1];t2f[7] = tex->t1;
2633 // now render batches of particles based on blendmode and texture
2634 blendmode = PBLEND_INVALID;
2638 R_Mesh_PrepareVertices_Generic_Arrays(numsurfaces * 4, particle_vertex3f, particle_color4f, particle_texcoord2f);
2639 for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
2641 p = cl.particles + surfacelist[surfacelistindex];
2643 if (blendmode != p->blendmode)
2645 blendmode = (pblend_t)p->blendmode;
2649 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2651 case PBLEND_INVALID:
2653 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2656 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2660 if (texture != particletexture[p->texnum].texture)
2662 texture = particletexture[p->texnum].texture;
2663 R_SetupShader_Generic(texture, NULL, GL_MODULATE, 1);
2666 // iterate until we find a change in settings
2667 batchstart = surfacelistindex++;
2668 for (;surfacelistindex < numsurfaces;surfacelistindex++)
2670 p = cl.particles + surfacelist[surfacelistindex];
2671 if (blendmode != p->blendmode || texture != particletexture[p->texnum].texture)
2675 batchcount = surfacelistindex - batchstart;
2676 R_Mesh_Draw(batchstart * 4, batchcount * 4, batchstart * 2, batchcount * 2, NULL, NULL, 0, particle_elements, NULL, 0);
2680 void R_DrawParticles (void)
2683 int drawparticles = r_drawparticles.integer;
2684 float minparticledist;
2686 float gravity, frametime, f, dist, oldorg[3];
2692 frametime = bound(0, cl.time - cl.particles_updatetime, 1);
2693 cl.particles_updatetime = bound(cl.time - 1, cl.particles_updatetime + frametime, cl.time + 1);
2695 // LordHavoc: early out conditions
2696 if (!cl.num_particles)
2699 minparticledist = DotProduct(r_refdef.view.origin, r_refdef.view.forward) + 4.0f;
2700 gravity = frametime * cl.movevars_gravity;
2701 update = frametime > 0;
2702 drawdist2 = r_drawparticles_drawdistance.value * r_refdef.view.quality;
2703 drawdist2 = drawdist2*drawdist2;
2705 for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++)
2709 if (cl.free_particle > i)
2710 cl.free_particle = i;
2716 if (p->delayedspawn > cl.time)
2719 p->size += p->sizeincrease * frametime;
2720 p->alpha -= p->alphafade * frametime;
2722 if (p->alpha <= 0 || p->die <= cl.time)
2725 if (p->orientation != PARTICLE_VBEAM && p->orientation != PARTICLE_HBEAM && frametime > 0)
2727 if (p->liquidfriction && cl_particles_collisions.integer && (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK))
2729 if (p->typeindex == pt_blood)
2730 p->size += frametime * 8;
2732 p->vel[2] -= p->gravity * gravity;
2733 f = 1.0f - min(p->liquidfriction * frametime, 1);
2734 VectorScale(p->vel, f, p->vel);
2738 p->vel[2] -= p->gravity * gravity;
2741 f = 1.0f - min(p->airfriction * frametime, 1);
2742 VectorScale(p->vel, f, p->vel);
2746 VectorCopy(p->org, oldorg);
2747 VectorMA(p->org, frametime, p->vel, p->org);
2748 // if (p->bounce && cl.time >= p->delayedcollisions)
2749 if (p->bounce && cl_particles_collisions.integer)
2751 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);
2752 // if the trace started in or hit something of SUPERCONTENTS_NODROP
2753 // or if the trace hit something flagged as NOIMPACT
2754 // then remove the particle
2755 if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT || ((trace.startsupercontents | trace.hitsupercontents) & SUPERCONTENTS_NODROP) || (trace.startsupercontents & SUPERCONTENTS_SOLID))
2757 VectorCopy(trace.endpos, p->org);
2758 // react if the particle hit something
2759 if (trace.fraction < 1)
2761 VectorCopy(trace.endpos, p->org);
2763 if (p->staintexnum >= 0)
2765 // blood - splash on solid
2766 if (!(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
2769 p->staincolor[0], p->staincolor[1], p->staincolor[2], (int)(p->stainalpha * p->stainsize * (1.0f / 160.0f)),
2770 p->staincolor[0], p->staincolor[1], p->staincolor[2], (int)(p->stainalpha * p->stainsize * (1.0f / 160.0f)));
2771 if (cl_decals.integer)
2773 // create a decal for the blood splat
2774 a = 0xFFFFFF ^ (p->staincolor[0]*65536+p->staincolor[1]*256+p->staincolor[2]);
2775 CL_SpawnDecalParticleForSurface(hitent, p->org, trace.plane.normal, a, a, p->staintexnum, p->stainsize, p->stainalpha); // staincolor needs to be inverted for decals!
2780 if (p->typeindex == pt_blood)
2782 // blood - splash on solid
2783 if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS)
2785 if(p->staintexnum == -1) // staintex < -1 means no stains at all
2787 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)));
2788 if (cl_decals.integer)
2790 // create a decal for the blood splat
2791 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);
2796 else if (p->bounce < 0)
2798 // bounce -1 means remove on impact
2803 // anything else - bounce off solid
2804 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
2805 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
2806 if (DotProduct(p->vel, p->vel) < 0.03)
2807 VectorClear(p->vel);
2813 if (p->typeindex != pt_static)
2815 switch (p->typeindex)
2817 case pt_entityparticle:
2818 // particle that removes itself after one rendered frame
2825 a = CL_PointSuperContents(p->org);
2826 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
2830 a = CL_PointSuperContents(p->org);
2831 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
2835 a = CL_PointSuperContents(p->org);
2836 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
2840 if (cl.time > p->time2)
2843 p->time2 = cl.time + (rand() & 3) * 0.1;
2844 p->vel[0] = p->vel[0] * 0.9f + lhrandom(-32, 32);
2845 p->vel[1] = p->vel[0] * 0.9f + lhrandom(-32, 32);
2847 a = CL_PointSuperContents(p->org);
2848 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
2856 else if (p->delayedspawn > cl.time)
2860 // don't render particles too close to the view (they chew fillrate)
2861 // also don't render particles behind the view (useless)
2862 // further checks to cull to the frustum would be too slow here
2863 switch(p->typeindex)
2866 // beams have no culling
2867 R_MeshQueue_AddTransparent(p->sortorigin, R_DrawParticle_TransparentCallback, NULL, i, NULL);
2870 if(cl_particles_visculling.integer)
2871 if (!r_refdef.viewcache.world_novis)
2872 if(r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.PointInLeaf)
2874 mleaf_t *leaf = r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, p->org);
2876 if(!CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, leaf->clusterindex))
2879 // anything else just has to be in front of the viewer and visible at this distance
2880 if (DotProduct(p->org, r_refdef.view.forward) >= minparticledist && VectorDistance2(p->org, r_refdef.view.origin) < drawdist2 * (p->size * p->size))
2881 R_MeshQueue_AddTransparent(p->sortorigin, R_DrawParticle_TransparentCallback, NULL, i, NULL);
2888 if (cl.free_particle > i)
2889 cl.free_particle = i;
2892 // reduce cl.num_particles if possible
2893 while (cl.num_particles > 0 && cl.particles[cl.num_particles - 1].typeindex == 0)
2896 if (cl.num_particles == cl.max_particles && cl.max_particles < MAX_PARTICLES)
2898 particle_t *oldparticles = cl.particles;
2899 cl.max_particles = min(cl.max_particles * 2, MAX_PARTICLES);
2900 cl.particles = (particle_t *) Mem_Alloc(cls.levelmempool, cl.max_particles * sizeof(particle_t));
2901 memcpy(cl.particles, oldparticles, cl.num_particles * sizeof(particle_t));
2902 Mem_Free(oldparticles);