DarkPlaces
Game engine based on the Quake 1 engine by id Software, developed by LadyHavoc
 
cl_particles.c
Go to the documentation of this file.
1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19*/
20
21#include "quakedef.h"
22
23#include "cl_collision.h"
24#include "image.h"
25#include "r_shadow.h"
26
27// must match ptype_t values
29{
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
42 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
43};
44
45#define PARTICLEEFFECT_UNDERWATER 1
46#define PARTICLEEFFECT_NOTUNDERWATER 2
47#define PARTICLEEFFECT_FORCENEAREST 4
48#define PARTICLEEFFECT_DEFINED 2147483648U
49
50typedef struct particleeffectinfo_s
51{
52 int effectnameindex; // which effect this belongs to
53 // PARTICLEEFFECT_* bits
54 int flags;
55 // blood effects may spawn very few particles, so proper fraction-overflow
56 // handling is very important, this variable keeps track of the fraction
58 // the math is: countabsolute + requestedcount * countmultiplier * quality
59 // absolute number of particles to spawn, often used for decals
60 // (unaffected by quality and requestedcount)
62 // multiplier for the number of particles CL_ParticleEffect was told to
63 // spawn, most effects do not really have a count and hence use 1, so
64 // this is often the actual count to spawn, not merely a multiplier
66 // if > 0 this causes the particle to spawn in an evenly spaced line from
67 // originmins to originmaxs (causing them to describe a trail, not a box)
69 // type of particle to spawn (defines some aspects of behavior)
71 // blending mode used on this particle type
73 // orientation of this particle type (BILLBOARD, SPARK, BEAM, etc)
75 // range of colors to choose from in hex RRGGBB (like HTML color tags),
76 // randomly interpolated at spawn
77 unsigned int color[2];
78 // a random texture is chosen in this range (note the second value is one
79 // past the last choosable, so for example 8,16 chooses any from 8 up and
80 // including 15)
81 // if start and end of the range are the same, no randomization is done
82 int tex[2];
83 // range of size values randomly chosen when spawning, plus size increase over time
84 float size[3];
85 // range of alpha values randomly chosen when spawning, plus alpha fade
86 float alpha[3];
87 // how long the particle should live (note it is also removed if alpha drops to 0)
88 float time[2];
89 // how much gravity affects this particle (negative makes it fly up!)
90 float gravity;
91 // how much bounce the particle has when it hits a surface
92 // if negative the particle is removed on impact
93 float bounce;
94 // if in air this friction is applied
95 // if negative the particle accelerates
97 // if in liquid (water/slime/lava) this friction is applied
98 // if negative the particle accelerates
100 // these offsets are added to the values given to particleeffect(), and
101 // then an ellipsoid-shaped jitter is added as defined by these
102 // (they are the 3 radii)
104 // stretch velocity factor (used for sparks)
105 float originoffset[3];
106 float relativeoriginoffset[3];
107 float velocityoffset[3];
108 float relativevelocityoffset[3];
109 float originjitter[3];
110 float velocityjitter[3];
112 // an effect can also spawn a dlight
116 float lightcolor[3];
119 float lightcorona[2];
120 unsigned int staincolor[2]; // note: 0x808080 = neutral (particle's own color), these are modding factors for the particle's original color!
121 int staintex[2];
122 float stainalpha[2];
123 float stainsize[2];
124 // other parameters
125 float rotate[4]; // min/max base angle, min/max rotation over time
126}
128
130
133
134static int particlepalette[256];
135/*
136 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b, // 0-7
137 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb, // 8-15
138 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b, // 16-23
139 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23, // 24-31
140 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767, // 32-39
141 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb, // 40-47
142 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07, // 48-55
143 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f, // 56-63
144 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000, // 64-71
145 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000, // 72-79
146 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307, // 80-87
147 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723, // 88-95
148 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b, // 96-103
149 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b, // 104-111
150 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733, // 112-119
151 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397, // 120-127
152 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b, // 128-135
153 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707, // 136-143
154 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b, // 144-151
155 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707, // 152-159
156 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353, // 160-167
157 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07, // 168-175
158 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f, // 176-183
159 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07, // 184-191
160 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307, // 192-199
161 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700, // 200-207
162 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f, // 208-215
163 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f, // 216-223
164 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b, // 224-231
165 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b, // 232-239
166 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000, // 240-247
167 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53 // 248-255
168*/
169
170int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
171int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
172int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
173
174//static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
175
176// particletexture_t is a rectangle in the particlefonttexture
177typedef struct particletexture_s
178{
180 float s1, t1, s2, t2;
181}
183
188
189// texture numbers in particle font
190static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
191static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
192static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
193static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
194static const int tex_rainsplash = 32;
195static const int tex_square = 33;
196static const int tex_beam = 60;
197static const int tex_bubble = 62;
198static const int tex_raindrop = 61;
199static const int tex_particle = 63;
200
202{
203 0, //int effectnameindex; // which effect this belongs to
204 // PARTICLEEFFECT_* bits
205 0, //int flags;
206 // blood effects may spawn very few particles, so proper fraction-overflow
207 // handling is very important, this variable keeps track of the fraction
208 0.0, //double particleaccumulator;
209 // the math is: countabsolute + requestedcount * countmultiplier * quality
210 // absolute number of particles to spawn, often used for decals
211 // (unaffected by quality and requestedcount)
212 0.0f, //float countabsolute;
213 // multiplier for the number of particles CL_ParticleEffect was told to
214 // spawn, most effects do not really have a count and hence use 1, so
215 // this is often the actual count to spawn, not merely a multiplier
216 0.0f, //float countmultiplier;
217 // if > 0 this causes the particle to spawn in an evenly spaced line from
218 // originmins to originmaxs (causing them to describe a trail, not a box)
219 0.0f, //float trailspacing;
220 // type of particle to spawn (defines some aspects of behavior)
221 pt_alphastatic, //ptype_t particletype;
222 // blending mode used on this particle type
223 PBLEND_ALPHA, //pblend_t blendmode;
224 // orientation of this particle type (BILLBOARD, SPARK, BEAM, etc)
225 PARTICLE_BILLBOARD, //porientation_t orientation;
226 // range of colors to choose from in hex RRGGBB (like HTML color tags),
227 // randomly interpolated at spawn
228 {0xFFFFFF, 0xFFFFFF}, //unsigned int color[2];
229 // a random texture is chosen in this range (note the second value is one
230 // past the last choosable, so for example 8,16 chooses any from 8 up and
231 // including 15)
232 // if start and end of the range are the same, no randomization is done
233 {63, 63 /* tex_particle */}, //int tex[2];
234 // range of size values randomly chosen when spawning, plus size increase over time
235 {1, 1, 0.0f}, //float size[3];
236 // range of alpha values randomly chosen when spawning, plus alpha fade
237 {0.0f, 256.0f, 256.0f}, //float alpha[3];
238 // how long the particle should live (note it is also removed if alpha drops to 0)
239 {16777216.0f, 16777216.0f}, //float time[2];
240 // how much gravity affects this particle (negative makes it fly up!)
241 0.0f, //float gravity;
242 // how much bounce the particle has when it hits a surface
243 // if negative the particle is removed on impact
244 0.0f, //float bounce;
245 // if in air this friction is applied
246 // if negative the particle accelerates
247 0.0f, //float airfriction;
248 // if in liquid (water/slime/lava) this friction is applied
249 // if negative the particle accelerates
250 0.0f, //float liquidfriction;
251 // these offsets are added to the values given to particleeffect(), and
252 // then an ellipsoid-shaped jitter is added as defined by these
253 // (they are the 3 radii)
254 1.0f, //float stretchfactor;
255 // stretch velocity factor (used for sparks)
256 {0.0f, 0.0f, 0.0f}, //float originoffset[3];
257 {0.0f, 0.0f, 0.0f}, //float relativeoriginoffset[3];
258 {0.0f, 0.0f, 0.0f}, //float velocityoffset[3];
259 {0.0f, 0.0f, 0.0f}, //float relativevelocityoffset[3];
260 {0.0f, 0.0f, 0.0f}, //float originjitter[3];
261 {0.0f, 0.0f, 0.0f}, //float velocityjitter[3];
262 0.0f, //float velocitymultiplier;
263 // an effect can also spawn a dlight
264 0.0f, //float lightradiusstart;
265 0.0f, //float lightradiusfade;
266 16777216.0f, //float lighttime;
267 {1.0f, 1.0f, 1.0f}, //float lightcolor[3];
268 true, //qbool lightshadow;
269 0, //int lightcubemapnum;
270 {1.0f, 0.25f}, //float lightcorona[2];
271 {(unsigned int)-1, (unsigned int)-1}, //unsigned int staincolor[2]; // note: 0x808080 = neutral (particle's own color), these are modding factors for the particle's original color!
272 {-1, -1}, //int staintex[2];
273 {1.0f, 1.0f}, //float stainalpha[2];
274 {2.0f, 2.0f}, //float stainsize[2];
275 // other parameters
276 {0.0f, 360.0f, 0.0f, 0.0f}, //float rotate[4]; // min/max base angle, min/max rotation over time
277};
278
279cvar_t cl_particles = {CF_CLIENT | CF_ARCHIVE, "cl_particles", "1", "enables particle effects"};
280cvar_t cl_particles_quality = {CF_CLIENT | CF_ARCHIVE, "cl_particles_quality", "1", "multiplies number of particles"};
281cvar_t cl_particles_alpha = {CF_CLIENT | CF_ARCHIVE, "cl_particles_alpha", "1", "multiplies opacity of particles"};
282cvar_t cl_particles_size = {CF_CLIENT | CF_ARCHIVE, "cl_particles_size", "1", "multiplies particle size"};
283cvar_t cl_particles_quake = {CF_CLIENT | CF_ARCHIVE, "cl_particles_quake", "0", "0: Fancy particles; 1: Disc particles like GLQuake; 2: Square particles like software-rendered Quake"};
284cvar_t cl_particles_blood = {CF_CLIENT | CF_ARCHIVE, "cl_particles_blood", "1", "enables blood effects"};
285cvar_t cl_particles_blood_alpha = {CF_CLIENT | CF_ARCHIVE, "cl_particles_blood_alpha", "1", "opacity of blood, does not affect decals"};
286cvar_t cl_particles_blood_decal_alpha = {CF_CLIENT | CF_ARCHIVE, "cl_particles_blood_decal_alpha", "1", "opacity of blood decal"};
287cvar_t cl_particles_blood_decal_scalemin = {CF_CLIENT | CF_ARCHIVE, "cl_particles_blood_decal_scalemin", "1.5", "minimal random scale of decal"};
288cvar_t cl_particles_blood_decal_scalemax = {CF_CLIENT | CF_ARCHIVE, "cl_particles_blood_decal_scalemax", "2", "maximal random scale of decal"};
289cvar_t cl_particles_blood_bloodhack = {CF_CLIENT | CF_ARCHIVE, "cl_particles_blood_bloodhack", "1", "make certain quake particle() calls create blood effects instead"};
290cvar_t cl_particles_bulletimpacts = {CF_CLIENT | CF_ARCHIVE, "cl_particles_bulletimpacts", "1", "enables bulletimpact effects"};
291cvar_t cl_particles_explosions_sparks = {CF_CLIENT | CF_ARCHIVE, "cl_particles_explosions_sparks", "1", "enables sparks from explosions"};
292cvar_t cl_particles_explosions_shell = {CF_CLIENT | CF_ARCHIVE, "cl_particles_explosions_shell", "0", "enables polygonal shell from explosions"};
293cvar_t cl_particles_rain = {CF_CLIENT | CF_ARCHIVE, "cl_particles_rain", "1", "enables rain effects"};
294cvar_t cl_particles_snow = {CF_CLIENT | CF_ARCHIVE, "cl_particles_snow", "1", "enables snow effects"};
295cvar_t cl_particles_smoke = {CF_CLIENT | CF_ARCHIVE, "cl_particles_smoke", "1", "enables smoke (used by multiple effects)"};
296cvar_t cl_particles_smoke_alpha = {CF_CLIENT | CF_ARCHIVE, "cl_particles_smoke_alpha", "0.5", "smoke brightness"};
297cvar_t cl_particles_smoke_alphafade = {CF_CLIENT | CF_ARCHIVE, "cl_particles_smoke_alphafade", "0.55", "brightness fade per second"};
298cvar_t cl_particles_sparks = {CF_CLIENT | CF_ARCHIVE, "cl_particles_sparks", "1", "enables sparks (used by multiple effects)"};
299cvar_t cl_particles_bubbles = {CF_CLIENT | CF_ARCHIVE, "cl_particles_bubbles", "1", "enables bubbles (used by multiple effects)"};
300cvar_t cl_particles_visculling = {CF_CLIENT | CF_ARCHIVE, "cl_particles_visculling", "0", "perform a costly check if each particle is visible before drawing"};
301cvar_t cl_particles_collisions = {CF_CLIENT | CF_ARCHIVE, "cl_particles_collisions", "1", "allow costly collision detection on particles (sparks that bounce, particles not going through walls, blood hitting surfaces, etc)"};
302cvar_t cl_particles_forcetraileffects = {CF_CLIENT, "cl_particles_forcetraileffects", "0", "force trails to be displayed even if a non-trail draw primitive was used (debug/compat feature)"};
303cvar_t cl_decals = {CF_CLIENT | CF_ARCHIVE, "cl_decals", "1", "enables decals (bullet holes, blood, etc)"};
304cvar_t cl_decals_time = {CF_CLIENT | CF_ARCHIVE, "cl_decals_time", "20", "how long before decals start to fade away"};
305cvar_t cl_decals_fadetime = {CF_CLIENT | CF_ARCHIVE, "cl_decals_fadetime", "1", "how long decals take to fade away"};
306cvar_t cl_decals_newsystem_intensitymultiplier = {CF_CLIENT | CF_ARCHIVE, "cl_decals_newsystem_intensitymultiplier", "2", "boosts intensity of decals (because the distance fade can make them hard to see otherwise)"};
307cvar_t cl_decals_newsystem_immediatebloodstain = {CF_CLIENT | CF_ARCHIVE, "cl_decals_newsystem_immediatebloodstain", "2", "0: no on-spawn blood stains; 1: on-spawn blood stains for pt_blood; 2: always use on-spawn blood stains"};
308cvar_t cl_decals_newsystem_bloodsmears = {CF_CLIENT | CF_ARCHIVE, "cl_decals_newsystem_bloodsmears", "1", "enable use of particle velocity as decal projection direction rather than surface normal"};
309cvar_t cl_decals_models = {CF_CLIENT | CF_ARCHIVE, "cl_decals_models", "0", "enables decals on animated models"};
310cvar_t cl_decals_bias = {CF_CLIENT | CF_ARCHIVE, "cl_decals_bias", "0.125", "distance to bias decals from surface to prevent depth fighting"};
311cvar_t cl_decals_max = {CF_CLIENT | CF_ARCHIVE, "cl_decals_max", "4096", "maximum number of decals allowed to exist in the world at once"};
312
313
314static void CL_Particles_ParseEffectInfo(const char *textstart, const char *textend, const char *filename)
315{
316 int arrayindex;
317 int argc;
318 int i;
319 int linenumber;
321 const char *text = textstart;
322 char argv[16][1024];
323 for (linenumber = 1;;linenumber++)
324 {
325 argc = 0;
326 for (arrayindex = 0;arrayindex < 16;arrayindex++)
327 argv[arrayindex][0] = 0;
328 for (;;)
329 {
330 if (!COM_ParseToken_Simple(&text, true, false, true))
331 return;
332 if (!strcmp(com_token, "\n"))
333 break;
334 if (argc < 16)
335 {
336 dp_strlcpy(argv[argc], com_token, sizeof(argv[argc]));
337 argc++;
338 }
339 }
340 if (argc < 1)
341 continue;
342#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;}
343#define readints(array, n) checkparms(n+1);for (arrayindex = 0;arrayindex < argc - 1;arrayindex++) array[arrayindex] = strtol(argv[1+arrayindex], NULL, 0)
344#define readfloats(array, n) checkparms(n+1);for (arrayindex = 0;arrayindex < argc - 1;arrayindex++) array[arrayindex] = atof(argv[1+arrayindex])
345#define readint(var) checkparms(2);var = strtol(argv[1], NULL, 0)
346#define readfloat(var) checkparms(2);var = atof(argv[1])
347#define readbool(var) checkparms(2);var = strtol(argv[1], NULL, 0) != 0
348 if (!strcmp(argv[0], "effect"))
349 {
350 int effectnameindex;
351 checkparms(2);
353 {
354 Con_Printf("%s:%i: too many effects!\n", filename, linenumber);
355 break;
356 }
357 for (effectnameindex = 1;effectnameindex < MAX_PARTICLEEFFECTNAME;effectnameindex++)
358 {
359 if (particleeffectname[effectnameindex][0])
360 {
361 if (!strcmp(particleeffectname[effectnameindex], argv[1]))
362 break;
363 }
364 else
365 {
366 dp_strlcpy(particleeffectname[effectnameindex], argv[1], sizeof(particleeffectname[effectnameindex]));
367 break;
368 }
369 }
370 // if we run out of names, abort
371 if (effectnameindex == MAX_PARTICLEEFFECTNAME)
372 {
373 Con_Printf("%s:%i: too many effects!\n", filename, linenumber);
374 break;
375 }
376 for(i = 0; i < numparticleeffectinfo; ++i)
377 {
378 info = particleeffectinfo + i;
379 if(!(info->flags & PARTICLEEFFECT_DEFINED))
380 if(info->effectnameindex == effectnameindex)
381 break;
382 }
384 continue;
386 // copy entire info from baseline, then fix up the nameindex
388 info->effectnameindex = effectnameindex;
389 continue;
390 }
391 else if (info == NULL)
392 {
393 Con_Printf("%s:%i: command %s encountered before effect\n", filename, linenumber, argv[0]);
394 break;
395 }
396
398 if (!strcmp(argv[0], "countabsolute")) {readfloat(info->countabsolute);}
399 else if (!strcmp(argv[0], "count")) {readfloat(info->countmultiplier);}
400 else if (!strcmp(argv[0], "type"))
401 {
402 checkparms(2);
403 if (!strcmp(argv[1], "alphastatic")) info->particletype = pt_alphastatic;
404 else if (!strcmp(argv[1], "static")) info->particletype = pt_static;
405 else if (!strcmp(argv[1], "spark")) info->particletype = pt_spark;
406 else if (!strcmp(argv[1], "beam")) info->particletype = pt_beam;
407 else if (!strcmp(argv[1], "rain")) info->particletype = pt_rain;
408 else if (!strcmp(argv[1], "raindecal")) info->particletype = pt_raindecal;
409 else if (!strcmp(argv[1], "snow")) info->particletype = pt_snow;
410 else if (!strcmp(argv[1], "bubble")) info->particletype = pt_bubble;
411 else if (!strcmp(argv[1], "blood")) {info->particletype = pt_blood;info->gravity = 1;}
412 else if (!strcmp(argv[1], "smoke")) info->particletype = pt_smoke;
413 else if (!strcmp(argv[1], "decal")) info->particletype = pt_decal;
414 else if (!strcmp(argv[1], "entityparticle")) info->particletype = pt_entityparticle;
415 else Con_Printf("%s:%i: unrecognized particle type %s\n", filename, linenumber, argv[1]);
418 }
419 else if (!strcmp(argv[0], "blend"))
420 {
421 checkparms(2);
422 if (!strcmp(argv[1], "alpha")) info->blendmode = PBLEND_ALPHA;
423 else if (!strcmp(argv[1], "add")) info->blendmode = PBLEND_ADD;
424 else if (!strcmp(argv[1], "invmod")) info->blendmode = PBLEND_INVMOD;
425 else Con_Printf("%s:%i: unrecognized blendmode %s\n", filename, linenumber, argv[1]);
426 }
427 else if (!strcmp(argv[0], "orientation"))
428 {
429 checkparms(2);
430 if (!strcmp(argv[1], "billboard")) info->orientation = PARTICLE_BILLBOARD;
431 else if (!strcmp(argv[1], "spark")) info->orientation = PARTICLE_SPARK;
432 else if (!strcmp(argv[1], "oriented")) info->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
433 else if (!strcmp(argv[1], "beam")) info->orientation = PARTICLE_HBEAM;
434 else Con_Printf("%s:%i: unrecognized orientation %s\n", filename, linenumber, argv[1]);
435 }
436 else if (!strcmp(argv[0], "color")) {readints(info->color, 2);}
437 else if (!strcmp(argv[0], "tex")) {readints(info->tex, 2);}
438 else if (!strcmp(argv[0], "size")) {readfloats(info->size, 2);}
439 else if (!strcmp(argv[0], "sizeincrease")) {readfloat(info->size[2]);}
440 else if (!strcmp(argv[0], "alpha")) {readfloats(info->alpha, 3);}
441 else if (!strcmp(argv[0], "time")) {readfloats(info->time, 2);}
442 else if (!strcmp(argv[0], "gravity")) {readfloat(info->gravity);}
443 else if (!strcmp(argv[0], "bounce")) {readfloat(info->bounce);}
444 else if (!strcmp(argv[0], "airfriction")) {readfloat(info->airfriction);}
445 else if (!strcmp(argv[0], "liquidfriction")) {readfloat(info->liquidfriction);}
446 else if (!strcmp(argv[0], "originoffset")) {readfloats(info->originoffset, 3);}
447 else if (!strcmp(argv[0], "relativeoriginoffset")) {readfloats(info->relativeoriginoffset, 3);}
448 else if (!strcmp(argv[0], "velocityoffset")) {readfloats(info->velocityoffset, 3);}
449 else if (!strcmp(argv[0], "relativevelocityoffset")) {readfloats(info->relativevelocityoffset, 3);}
450 else if (!strcmp(argv[0], "originjitter")) {readfloats(info->originjitter, 3);}
451 else if (!strcmp(argv[0], "velocityjitter")) {readfloats(info->velocityjitter, 3);}
452 else if (!strcmp(argv[0], "velocitymultiplier")) {readfloat(info->velocitymultiplier);}
453 else if (!strcmp(argv[0], "lightradius")) {readfloat(info->lightradiusstart);}
454 else if (!strcmp(argv[0], "lightradiusfade")) {readfloat(info->lightradiusfade);}
455 else if (!strcmp(argv[0], "lighttime")) {readfloat(info->lighttime);}
456 else if (!strcmp(argv[0], "lightcolor")) {readfloats(info->lightcolor, 3);}
457 else if (!strcmp(argv[0], "lightshadow")) {readbool(info->lightshadow);}
458 else if (!strcmp(argv[0], "lightcubemapnum")) {readint(info->lightcubemapnum);}
459 else if (!strcmp(argv[0], "lightcorona")) {readints(info->lightcorona, 2);}
460 else if (!strcmp(argv[0], "underwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_UNDERWATER;}
461 else if (!strcmp(argv[0], "notunderwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_NOTUNDERWATER;}
462 else if (!strcmp(argv[0], "trailspacing")) {readfloat(info->trailspacing);if (info->trailspacing > 0) info->countmultiplier = 1.0f / info->trailspacing;}
463 else if (!strcmp(argv[0], "stretchfactor")) {readfloat(info->stretchfactor);}
464 else if (!strcmp(argv[0], "staincolor")) {readints(info->staincolor, 2);}
465 else if (!strcmp(argv[0], "stainalpha")) {readfloats(info->stainalpha, 2);}
466 else if (!strcmp(argv[0], "stainsize")) {readfloats(info->stainsize, 2);}
467 else if (!strcmp(argv[0], "staintex")) {readints(info->staintex, 2);}
468 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; }
469 else if (!strcmp(argv[0], "rotate")) {readfloats(info->rotate, 4);}
470 else if (!strcmp(argv[0], "forcenearest")) {checkparms(1);info->flags |= PARTICLEEFFECT_FORCENEAREST;}
471 else
472 Con_Printf("%s:%i: skipping unknown command %s\n", filename, linenumber, argv[0]);
473#undef checkparms
474#undef readints
475#undef readfloats
476#undef readint
477#undef readfloat
478 }
479}
480
482{
483 int i;
484 for (i = 1;i < MAX_PARTICLEEFFECTNAME && particleeffectname[i][0];i++)
485 if (!strcmp(particleeffectname[i], name))
486 return i;
487 return 0;
488}
489
491{
493 return NULL;
494 return particleeffectname[i];
495}
496
497// MUST match effectnameindex_t in client.h
499{
500 "",
501 "TE_GUNSHOT",
502 "TE_GUNSHOTQUAD",
503 "TE_SPIKE",
504 "TE_SPIKEQUAD",
505 "TE_SUPERSPIKE",
506 "TE_SUPERSPIKEQUAD",
507 "TE_WIZSPIKE",
508 "TE_KNIGHTSPIKE",
509 "TE_EXPLOSION",
510 "TE_EXPLOSIONQUAD",
511 "TE_TAREXPLOSION",
512 "TE_TELEPORT",
513 "TE_LAVASPLASH",
514 "TE_SMALLFLASH",
515 "TE_FLAMEJET",
516 "EF_FLAME",
517 "TE_BLOOD",
518 "TE_SPARK",
519 "TE_PLASMABURN",
520 "TE_TEI_G3",
521 "TE_TEI_SMOKE",
522 "TE_TEI_BIGEXPLOSION",
523 "TE_TEI_PLASMAHIT",
524 "EF_STARDUST",
525 "TR_ROCKET",
526 "TR_GRENADE",
527 "TR_BLOOD",
528 "TR_WIZSPIKE",
529 "TR_SLIGHTBLOOD",
530 "TR_KNIGHTSPIKE",
531 "TR_VORESPIKE",
532 "TR_NEHAHRASMOKE",
533 "TR_NEXUIZPLASMA",
534 "TR_GLOWTRAIL",
535 "SVC_PARTICLE"
536};
537
538static void CL_Particles_LoadEffectInfo(const char *customfile)
539{
540 int i;
541 int filepass;
542 unsigned char *filedata;
543 fs_offset_t filesize;
544 char filename[MAX_QPATH];
546 memset(particleeffectinfo, 0, sizeof(particleeffectinfo));
547 memset(particleeffectname, 0, sizeof(particleeffectname));
548 for (i = 0;i < EFFECT_TOTAL;i++)
550 for (filepass = 0;;filepass++)
551 {
552 if (filepass == 0)
553 {
554 if (customfile)
555 dp_strlcpy(filename, customfile, sizeof(filename));
556 else
557 dp_strlcpy(filename, "effectinfo.txt", sizeof(filename));
558 }
559 else if (filepass == 1)
560 {
561 if (!cl.worldbasename[0] || customfile)
562 continue;
563 dpsnprintf(filename, sizeof(filename), "%s_effectinfo.txt", cl.worldnamenoextension);
564 }
565 else
566 break;
567 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
568 if (!filedata)
569 continue;
570 CL_Particles_ParseEffectInfo((const char *)filedata, (const char *)filedata + filesize, filename);
571 Mem_Free(filedata);
572 }
573}
574
579
580/*
581===============
582CL_InitParticles
583===============
584*/
587{
588 Cmd_AddCommand(CF_CLIENT, "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)");
589 Cmd_AddCommand(CF_CLIENT, "cl_particles_reloadeffects", CL_Particles_LoadEffectInfo_f, "reloads effectinfo.txt and maps/levelname_effectinfo.txt (where levelname is the current map) if parameter is given, loads from custom file (no levelname_effectinfo are loaded in this case)");
590
624}
625
627{
628}
629
630void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha);
631void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2);
632
633
634
669 const vec3_t sortorigin,
670 unsigned short ptypeindex,
671 int pcolor1, int pcolor2,
672 int ptex,
673 float psize,
674 float psizeincrease,
675 float palpha,
676 float palphafade,
677 float pgravity,
678 float pbounce,
679 float px, float py, float pz,
680 float pvx, float pvy, float pvz,
681 float pairfriction,
682 float pliquidfriction,
683 float originjitter,
684 float velocityjitter,
685 qbool pqualityreduction,
686 float lifetime,
687 float stretch,
688 pblend_t blendmode,
689 porientation_t orientation,
690 int staincolor1,
691 int staincolor2,
692 int staintex,
693 float stainalpha,
694 float stainsize,
695 float angle,
696 float spin,
697 float tint[4])
698{
699 int l1, l2, r, g, b;
700 particle_t *part;
701 vec3_t v;
703 return NULL;
706 return NULL;
707 if (!lifetime)
708 lifetime = palpha / min(1, palphafade);
709 part = &cl.particles[cl.free_particle++];
712 memset(part, 0, sizeof(*part));
713 VectorCopy(sortorigin, part->sortorigin);
714 part->typeindex = ptypeindex;
715 part->blendmode = blendmode;
716 if(orientation == PARTICLE_HBEAM || orientation == PARTICLE_VBEAM)
717 {
719 if(tex->t1 == 0 && tex->t2 == 1) // full height of texture?
721 else
723 }
724 else
725 part->orientation = orientation;
726 l2 = (int)lhrandom(0.5, 256.5);
727 l1 = 256 - l2;
728 part->color[0] = ((((pcolor1 >> 16) & 0xFF) * l1 + ((pcolor2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
729 part->color[1] = ((((pcolor1 >> 8) & 0xFF) * l1 + ((pcolor2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
730 part->color[2] = ((((pcolor1 >> 0) & 0xFF) * l1 + ((pcolor2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
731 if (vid.sRGB3D)
732 {
733 part->color[0] = (unsigned char)floor(Image_LinearFloatFromsRGB(part->color[0]) * 255.0f + 0.5f);
734 part->color[1] = (unsigned char)floor(Image_LinearFloatFromsRGB(part->color[1]) * 255.0f + 0.5f);
735 part->color[2] = (unsigned char)floor(Image_LinearFloatFromsRGB(part->color[2]) * 255.0f + 0.5f);
736 }
737 part->alpha = palpha;
738 part->alphafade = palphafade;
739 part->staintexnum = staintex;
740 if(staincolor1 >= 0 && staincolor2 >= 0)
741 {
742 l2 = (int)lhrandom(0.5, 256.5);
743 l1 = 256 - l2;
744 if(blendmode == PBLEND_INVMOD)
745 {
746 r = ((((staincolor1 >> 16) & 0xFF) * l1 + ((staincolor2 >> 16) & 0xFF) * l2) * (255 - part->color[0])) / 0x8000; // staincolor 0x808080 keeps color invariant
747 g = ((((staincolor1 >> 8) & 0xFF) * l1 + ((staincolor2 >> 8) & 0xFF) * l2) * (255 - part->color[1])) / 0x8000;
748 b = ((((staincolor1 >> 0) & 0xFF) * l1 + ((staincolor2 >> 0) & 0xFF) * l2) * (255 - part->color[2])) / 0x8000;
749 }
750 else
751 {
752 r = ((((staincolor1 >> 16) & 0xFF) * l1 + ((staincolor2 >> 16) & 0xFF) * l2) * part->color[0]) / 0x8000; // staincolor 0x808080 keeps color invariant
753 g = ((((staincolor1 >> 8) & 0xFF) * l1 + ((staincolor2 >> 8) & 0xFF) * l2) * part->color[1]) / 0x8000;
754 b = ((((staincolor1 >> 0) & 0xFF) * l1 + ((staincolor2 >> 0) & 0xFF) * l2) * part->color[2]) / 0x8000;
755 }
756 if(r > 0xFF) r = 0xFF;
757 if(g > 0xFF) g = 0xFF;
758 if(b > 0xFF) b = 0xFF;
759 }
760 else
761 {
762 r = part->color[0]; // -1 is shorthand for stain = particle color
763 g = part->color[1];
764 b = part->color[2];
765 }
766 part->staincolor[0] = r;
767 part->staincolor[1] = g;
768 part->staincolor[2] = b;
769 part->stainalpha = palpha * stainalpha;
770 part->stainsize = psize * stainsize;
771 if(tint)
772 {
773 if(blendmode != PBLEND_INVMOD) // invmod is immune to tinting
774 {
775 part->color[0] *= tint[0];
776 part->color[1] *= tint[1];
777 part->color[2] *= tint[2];
778 }
779 part->alpha *= tint[3];
780 part->alphafade *= tint[3];
781 part->stainalpha *= tint[3];
782 }
783 part->texnum = ptex;
784 part->size = psize;
785 part->sizeincrease = psizeincrease;
786 part->gravity = pgravity;
787 part->bounce = pbounce;
788 part->stretch = stretch;
790 part->org[0] = px + originjitter * v[0];
791 part->org[1] = py + originjitter * v[1];
792 part->org[2] = pz + originjitter * v[2];
793 part->vel[0] = pvx + velocityjitter * v[0];
794 part->vel[1] = pvy + velocityjitter * v[1];
795 part->vel[2] = pvz + velocityjitter * v[2];
796 part->airfriction = pairfriction;
797 part->liquidfriction = pliquidfriction;
798 part->die = cl.time + lifetime;
799 part->delayedspawn = cl.time;
800// part->delayedcollisions = 0;
801 part->qualityreduction = pqualityreduction;
802 part->angle = angle;
803 part->spin = spin;
804 // if it is rain or snow, trace ahead and shut off collisions until an actual collision event needs to occur to improve performance
805 if (part->typeindex == pt_rain)
806 {
807 int i;
808 particle_t *part2;
809 vec3_t endvec;
810 trace_t trace;
811 // turn raindrop into simple spark and create delayedspawn splash effect
812 part->typeindex = pt_spark;
813 part->bounce = 0;
814 VectorMA(part->org, lifetime, part->vel, endvec);
815 trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, 0, 0, collision_extendmovelength.value, true, false, NULL, false, false);
816 part->die = cl.time + lifetime * trace.fraction;
817 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, NULL);
818 if (part2)
819 {
820 part2->delayedspawn = part->die;
821 part2->die += part->die - cl.time;
822 for (i = rand() & 7;i < 10;i++)
823 {
824 part2 = CL_NewParticle(endvec, pt_spark, pcolor1, pcolor2, tex_particle, 0.25f, 0, part->alpha * 2, part->alpha * 4, 1, 0.1, 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, NULL);
825 if (part2)
826 {
827 part2->delayedspawn = part->die;
828 part2->die += part->die - cl.time;
829 }
830 }
831 }
832 }
833 else if (part->typeindex == pt_explode || part->typeindex == pt_explode2)
834 part->time2 = rand()&3; // time2 is used to progress the colour ramp index
835
836#if 0
837 else if (part->bounce != 0 && part->gravity == 0 && part->typeindex != pt_snow)
838 {
839 float lifetime = part->alpha / (part->alphafade ? part->alphafade : 1);
840 vec3_t endvec;
841 trace_t trace;
842 VectorMA(part->org, lifetime, part->vel, endvec);
843 trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY, true, false, NULL, false);
844 part->delayedcollisions = cl.time + lifetime * trace.fraction - 0.1;
845 }
846#endif
847
848 return part;
849}
850
851
852
870 const vec3_t origin,
871 const unsigned short ptypeindex,
872 const int color_1,
873 const int color_2,
874 const float gravity,
875 const float offset_x,
876 const float offset_y,
877 const float offset_z,
878 const float velocity_offset_x,
879 const float velocity_offset_y,
880 const float velocity_offset_z,
881 const float air_friction,
882 const float liquid_friction,
883 const float origin_jitter,
884 const float velocity_jitter,
885 const float lifetime)
886{
887 int texture;
888
889 // Set the particle texture based on the value of cl_particles_quake; defaulting to the GLQuake disc
892 else
894
895 return CL_NewParticle(
896 origin,
897 ptypeindex, // type
898 color_1,
899 color_2,
900 texture,
901 0.8f, // size
902 0, // size increase
903 255, // alpha
904 0, // alpha fade
905 gravity,
906 0, // bounce
907 offset_x,
908 offset_y,
909 offset_z,
910 velocity_offset_x,
911 velocity_offset_y,
912 velocity_offset_z,
913 air_friction,
914 liquid_friction,
915 origin_jitter,
916 velocity_jitter,
917 true, // quality reduction
918 lifetime,
919 1, // stretch
920 PBLEND_ALPHA, // blend mode
921 PARTICLE_BILLBOARD, // orientation
922 -1, // stain color 1
923 -1, // stain color 2
924 -1, // stain texture
925 1, // stain alpha
926 1, // stain size
927 0, // angle
928 0, // spin
929 NULL // tint
930 );
931}
932
933
934
936{
937 vec3_t v;
938 int staintex;
939
940 // blood creates a splash at spawn, not just at impact, this makes monsters bloody where they are shot
941 if (part->staintexnum >= 0 && cl_decals.integer)
942 {
943 VectorCopy(part->vel, v);
945 staintex = part->staintexnum;
946 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);
947 }
948
949 // blood creates a splash at spawn, not just at impact, this makes monsters bloody where they are shot
950 if (part->typeindex == pt_blood && cl_decals.integer)
951 {
952 VectorCopy(part->vel, v);
954 staintex = tex_blooddecal[rand()&7];
955 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);
956 }
957}
958
959void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
960{
961 int l1, l2;
962 entity_render_t *ent = &cl.entities[hitent].render;
963 unsigned char color[3];
964 if (!cl_decals.integer)
965 return;
966 if (!ent->allowdecals)
967 return;
968
969 l2 = (int)lhrandom(0.5, 256.5);
970 l1 = 256 - l2;
971 color[0] = ((((color1 >> 16) & 0xFF) * l1 + ((color2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
972 color[1] = ((((color1 >> 8) & 0xFF) * l1 + ((color2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
973 color[2] = ((((color1 >> 0) & 0xFF) * l1 + ((color2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
974
975 if (vid.sRGB3D)
977 else
978 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);
979}
980
981void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
982{
983 int i;
984 vec_t bestfrac;
985 vec3_t bestorg;
986 vec3_t bestnormal;
987 vec3_t org2;
988 int besthitent = 0, hitent;
989 trace_t trace;
990 bestfrac = 10;
991 for (i = 0;i < 32;i++)
992 {
993 VectorRandom(org2);
994 VectorMA(org, maxdist, org2, org2);
995 trace = CL_TraceLine(org, org2, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, 0, 0, collision_extendmovelength.value, true, false, &hitent, false, true);
996 // take the closest trace result that doesn't end up hitting a NOMARKS
997 // surface (sky for example)
998 if (bestfrac > trace.fraction && !(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
999 {
1000 bestfrac = trace.fraction;
1001 besthitent = hitent;
1002 VectorCopy(trace.endpos, bestorg);
1003 VectorCopy(trace.plane.normal, bestnormal);
1004 }
1005 }
1006 if (bestfrac < 1)
1007 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
1008}
1009
1010// generates a cubemap name with prefix flags based on info flags (for now only `!`)
1011static char *LightCubemapNumToName(char *vabuf, size_t vasize, int lightcubemapnum, int flags)
1012{
1013 if (lightcubemapnum <= 0)
1014 return NULL;
1015 // `!` is prepended if the cubemap must be nearest-filtered
1017 return va(vabuf, vasize, "!cubemaps/%i", lightcubemapnum);
1018 return va(vabuf, vasize, "cubemaps/%i", lightcubemapnum);
1019}
1020
1021static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount);
1022static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount);
1023static void CL_NewParticlesFromEffectinfo(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, qbool spawndlight, qbool spawnparticles, float tintmins[4], float tintmaxs[4], float fade, qbool wanttrail);
1024static 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, qbool spawndlight, qbool spawnparticles, qbool wanttrail)
1025{
1026 vec3_t center;
1027 matrix4x4_t lightmatrix;
1028 particle_t *part;
1029
1030 VectorLerp(originmins, 0.5, originmaxs, center);
1031 Matrix4x4_CreateTranslate(&lightmatrix, center[0], center[1], center[2]);
1032 if (effectnameindex == EFFECT_SVC_PARTICLE)
1033 {
1035 {
1036 // bloodhack checks if this effect's color matches regular or lightning blood and if so spawns a blood effect instead
1037 if (count == 1024)
1038 CL_NewParticlesFromEffectinfo(EFFECT_TE_EXPLOSION, 1, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
1039 else if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer && (palettecolor == 73 || palettecolor == 225))
1040 CL_NewParticlesFromEffectinfo(EFFECT_TE_BLOOD, count / 2.0f, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
1041 else
1042 {
1044 for (;count > 0;count--)
1045 {
1046 int k = particlepalette[(palettecolor & ~7) + (rand()&7)];
1048 center, // origin
1049 pt_alphastatic, // type
1050 k, // color 1
1051 k, // color 2
1052 0.15, // gravity
1053 lhrandom(originmins[0], originmaxs[0]), // offset x
1054 lhrandom(originmins[1], originmaxs[1]), // offset y
1055 lhrandom(originmins[2], originmaxs[2]), // offset z
1056 lhrandom(velocitymins[0], velocitymaxs[0]), // velocity offset x
1057 lhrandom(velocitymins[1], velocitymaxs[1]), // velocity offset y
1058 lhrandom(velocitymins[2], velocitymaxs[2]), // velocity offset z
1059 0, // air friction
1060 0, // liquid friction
1061 8, // origin jitter
1062 3, // velocity jitter
1063 lhrandom(0.1, 0.4) // lifetime
1064 );
1065 }
1066 }
1067 }
1068 }
1069 else if (effectnameindex == EFFECT_TE_WIZSPIKE)
1070 CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 30*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 20, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
1071 else if (effectnameindex == EFFECT_TE_KNIGHTSPIKE)
1072 CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 226, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
1073 else if (effectnameindex == EFFECT_TE_SPIKE)
1074 {
1076 {
1078 {
1080 CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
1081 }
1082 else
1083 {
1084 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
1085 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
1086 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, NULL);
1087 }
1088 }
1089 // bullet hole
1090 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
1091 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1092 }
1093 else if (effectnameindex == EFFECT_TE_SPIKEQUAD)
1094 {
1096 {
1098 {
1100 CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
1101 }
1102 else
1103 {
1104 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
1105 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
1106 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, NULL);
1107 }
1108 }
1109 // bullet hole
1110 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
1111 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1112 CL_AllocLightFlash(NULL, &lightmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, NULL, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1113 }
1114 else if (effectnameindex == EFFECT_TE_SUPERSPIKE)
1115 {
1117 {
1119 {
1121 CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
1122 }
1123 else
1124 {
1125 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
1126 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
1127 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, NULL);
1128 }
1129 }
1130 // bullet hole
1131 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
1132 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1133 }
1134 else if (effectnameindex == EFFECT_TE_SUPERSPIKEQUAD)
1135 {
1137 {
1139 {
1141 CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
1142 }
1143 else
1144 {
1145 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
1146 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
1147 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, NULL);
1148 }
1149 }
1150 // bullet hole
1151 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
1152 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1153 CL_AllocLightFlash(NULL, &lightmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, NULL, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1154 }
1155 else if (effectnameindex == EFFECT_TE_BLOOD)
1156 {
1158 return;
1160 CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 2*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 73, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
1161 else
1162 {
1163 static double bloodaccumulator = 0;
1164 qbool immediatebloodstain = (cl_decals_newsystem_immediatebloodstain.integer >= 1);
1165 //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, NULL);
1166 bloodaccumulator += count * 0.333 * cl_particles_quality.value;
1167 for (;bloodaccumulator > 0;bloodaccumulator--)
1168 {
1169 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, NULL);
1170 if (immediatebloodstain && part)
1171 {
1172 immediatebloodstain = false;
1174 }
1175 }
1176 }
1177 }
1178 else if (effectnameindex == EFFECT_TE_SPARK)
1179 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, count);
1180 else if (effectnameindex == EFFECT_TE_PLASMABURN)
1181 {
1182 // plasma scorch mark
1183 R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
1184 CL_SpawnDecalParticleForPoint(center, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1185 CL_AllocLightFlash(NULL, &lightmatrix, 200, 1, 1, 1, 1000, 0.2, NULL, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1186 }
1187 else if (effectnameindex == EFFECT_TE_GUNSHOT)
1188 {
1190 {
1192 CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
1193 else
1194 {
1195 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
1196 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
1197 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, NULL);
1198 }
1199 }
1200 // bullet hole
1201 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
1202 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1203 }
1204 else if (effectnameindex == EFFECT_TE_GUNSHOTQUAD)
1205 {
1207 {
1209 CL_NewParticlesFromEffectinfo(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0, spawndlight, spawnparticles, NULL, NULL, 1, wanttrail);
1210 else
1211 {
1212 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
1213 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
1214 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, NULL);
1215 }
1216 }
1217 // bullet hole
1218 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
1219 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1220 CL_AllocLightFlash(NULL, &lightmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, NULL, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1221 }
1222 else if (effectnameindex == EFFECT_TE_EXPLOSION)
1223 {
1224 CL_ParticleExplosion(center);
1225 CL_AllocLightFlash(NULL, &lightmatrix, 350, 4.0f, 2.0f, 0.50f, 700, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1226 }
1227 else if (effectnameindex == EFFECT_TE_EXPLOSIONQUAD)
1228 {
1229 CL_ParticleExplosion(center);
1230 CL_AllocLightFlash(NULL, &lightmatrix, 350, 2.5f, 2.0f, 4.0f, 700, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1231 }
1232 else if (effectnameindex == EFFECT_TE_TAREXPLOSION)
1233 {
1235 {
1236 int i;
1237 for (i = 0;i < 1024 * cl_particles_quality.value;i++)
1238 {
1239 if (i & 1)
1240 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, NULL);
1241 else
1242 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, NULL);
1243 }
1244 }
1245 else
1246 CL_ParticleExplosion(center);
1247 CL_AllocLightFlash(NULL, &lightmatrix, 600, 1.6f, 0.8f, 2.0f, 1200, 0.5, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1248 }
1249 else if (effectnameindex == EFFECT_TE_SMALLFLASH)
1250 CL_AllocLightFlash(NULL, &lightmatrix, 200, 2, 2, 2, 1000, 0.2, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1251 else if (effectnameindex == EFFECT_TE_FLAMEJET)
1252 {
1254 while (count-- > 0)
1255 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, NULL);
1256 }
1257 else if (effectnameindex == EFFECT_TE_LAVASPLASH)
1258 {
1259 float i, j, inc, vel;
1260 vec3_t dir, org;
1261
1262 inc = 8 / cl_particles_quality.value;
1263 for (i = -128;i < 128;i += inc)
1264 {
1265 for (j = -128;j < 128;j += inc)
1266 {
1267 dir[0] = j + lhrandom(0, inc);
1268 dir[1] = i + lhrandom(0, inc);
1269 dir[2] = 256;
1270 org[0] = center[0] + dir[0];
1271 org[1] = center[1] + dir[1];
1272 org[2] = center[2] + lhrandom(0, 64);
1273 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1274 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, NULL);
1275 }
1276 }
1277 }
1278 else if (effectnameindex == EFFECT_TE_TELEPORT)
1279 {
1280 float i, j, k, inc, vel;
1281 vec3_t dir;
1282
1284 inc = 4 / cl_particles_quality.value;
1285 else
1286 inc = 8 / cl_particles_quality.value;
1287 for (i = -16;i < 16;i += inc)
1288 {
1289 for (j = -16;j < 16;j += inc)
1290 {
1291 for (k = -24;k < 32;k += inc)
1292 {
1293 VectorSet(dir, i*8, j*8, k*8);
1295 vel = lhrandom(50, 113);
1297 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, NULL);
1298 else
1299 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, NULL);
1300 }
1301 }
1302 }
1304 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, NULL);
1305 CL_AllocLightFlash(NULL, &lightmatrix, 200, 2.0f, 2.0f, 2.0f, 400, 99.0f, NULL, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1306 }
1307 else if (effectnameindex == EFFECT_TE_TEI_G3)
1308 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, NULL);
1309 else if (effectnameindex == EFFECT_TE_TEI_SMOKE)
1310 {
1312 {
1313 count *= 0.25f * cl_particles_quality.value;
1314 while (count-- > 0)
1315 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, NULL);
1316 }
1317 }
1318 else if (effectnameindex == EFFECT_TE_TEI_BIGEXPLOSION)
1319 {
1320 CL_ParticleExplosion(center);
1321 CL_AllocLightFlash(NULL, &lightmatrix, 500, 2.5f, 2.0f, 1.0f, 500, 9999, NULL, -1, true, 1, 0.25, 0.5, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1322 }
1323 else if (effectnameindex == EFFECT_TE_TEI_PLASMAHIT)
1324 {
1325 float f;
1326 R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
1327 CL_SpawnDecalParticleForPoint(center, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1329 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1330 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, NULL);
1332 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1333 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, NULL);
1334 CL_AllocLightFlash(NULL, &lightmatrix, 500, 0.6f, 1.2f, 2.0f, 2000, 9999, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1335 }
1336 else if (effectnameindex == EFFECT_EF_FLAME)
1337 {
1338 if (!spawnparticles)
1339 count = 0;
1341 while (count-- > 0)
1342 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, NULL);
1343 CL_AllocLightFlash(NULL, &lightmatrix, 200, 2.0f, 1.5f, 0.5f, 0, 0, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1344 }
1345 else if (effectnameindex == EFFECT_EF_STARDUST)
1346 {
1347 if (!spawnparticles)
1348 count = 0;
1350 while (count-- > 0)
1351 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, NULL);
1352 CL_AllocLightFlash(NULL, &lightmatrix, 200, 1.0f, 0.7f, 0.3f, 0, 0, NULL, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1353 }
1354 else if (!strncmp(particleeffectname[effectnameindex], "TR_", 3))
1355 {
1356 vec3_t dir, pos;
1357 float len, dec, qd;
1358 int smoke, blood, bubbles, r, color, spawnedcount;
1359
1360 if (spawndlight && r_refdef.scene.numlights < MAX_DLIGHTS)
1361 {
1362 vec4_t light;
1363 Vector4Set(light, 0, 0, 0, 0);
1364
1365 if (effectnameindex == EFFECT_TR_ROCKET)
1366 Vector4Set(light, 3.0f, 1.5f, 0.5f, 200);
1367 else if (effectnameindex == EFFECT_TR_VORESPIKE)
1368 {
1370 Vector4Set(light, 0.3f, 0.6f, 1.2f, 100);
1371 else
1372 Vector4Set(light, 1.2f, 0.5f, 1.0f, 200);
1373 }
1374 else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
1375 Vector4Set(light, 0.75f, 1.5f, 3.0f, 200);
1376
1377 if (light[3])
1378 {
1379 matrix4x4_t traillightmatrix;
1380 Matrix4x4_CreateFromQuakeEntity(&traillightmatrix, originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, light[3]);
1381 R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &traillightmatrix, light, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1383 }
1384 }
1385
1386 if (!spawnparticles)
1387 return;
1388
1389 if (originmaxs[0] == originmins[0] && originmaxs[1] == originmins[1] && originmaxs[2] == originmins[2])
1390 return;
1391
1392 VectorSubtract(originmaxs, originmins, dir);
1394
1395 if (ent)
1396 {
1397 dec = -ent->persistent.trail_time;
1398 ent->persistent.trail_time += len;
1399 if (ent->persistent.trail_time < 0.01f)
1400 return;
1401
1402 // if we skip out, leave it reset
1403 ent->persistent.trail_time = 0.0f;
1404 }
1405 else
1406 dec = 0;
1407
1408 // advance into this frame to reach the first puff location
1409 VectorMA(originmins, dec, dir, pos);
1410 len -= dec;
1411
1415 qd = 1.0f / cl_particles_quality.value;
1416 spawnedcount = 0;
1417
1418 while (len >= 0 && ++spawnedcount <= 16384)
1419 {
1420 dec = 3;
1421 if (blood)
1422 {
1423 if (effectnameindex == EFFECT_TR_BLOOD)
1424 {
1426 {
1427 color = particlepalette[67 + (rand()&3)];
1428 CL_NewQuakeParticle(center, pt_alphastatic, color, color, 0.25, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, 2);
1429 }
1430 else
1431 {
1432 dec = 16;
1433 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, NULL);
1434 }
1435 }
1436 else if (effectnameindex == EFFECT_TR_SLIGHTBLOOD)
1437 {
1439 {
1440 dec = 6;
1441 color = particlepalette[67 + (rand()&3)];
1442 CL_NewQuakeParticle(center, pt_alphastatic, color, color, 0.25, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, 2);
1443 }
1444 else
1445 {
1446 dec = 32;
1447 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, NULL);
1448 }
1449 }
1450 }
1451 if (smoke)
1452 {
1453 if (effectnameindex == EFFECT_TR_ROCKET)
1454 {
1456 {
1457 r = rand()&3;
1459 CL_NewQuakeParticle(center, pt_alphastatic, color, color, -0.10, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, 0.1372549 * (6 - r));
1460 }
1461 else
1462 {
1463 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, NULL);
1464 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, NULL);
1465 }
1466 }
1467 else if (effectnameindex == EFFECT_TR_GRENADE)
1468 {
1470 {
1471 r = 2 + (rand()%4);
1473 CL_NewQuakeParticle(center, pt_alphastatic, color, color, -0.15, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, 0.1372549 * (6 - r));
1474 }
1475 else
1476 {
1477 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, NULL);
1478 }
1479 }
1480 else if (effectnameindex == EFFECT_TR_WIZSPIKE)
1481 {
1483 {
1484 dec = 6;
1485 color = particlepalette[52 + (rand()&7)];
1486 CL_NewQuakeParticle(center, pt_alphastatic, color, color, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, 0.5);
1487 CL_NewQuakeParticle(center, pt_alphastatic, color, color, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, 0.5);
1488 }
1489 else if (gamemode == GAME_GOODVSBAD2)
1490 {
1491 dec = 6;
1492 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, NULL);
1493 }
1494 else
1495 {
1496 color = particlepalette[20 + (rand()&7)];
1497 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, NULL);
1498 }
1499 }
1500 else if (effectnameindex == EFFECT_TR_KNIGHTSPIKE)
1501 {
1503 {
1504 dec = 6;
1505 color = particlepalette[230 + (rand()&7)];
1506 CL_NewQuakeParticle(center, pt_alphastatic, color, color, 0, pos[0], pos[1], pos[2], 30 * dir[1], 30 * -dir[0], 0, 0, 0, 0, 0, 0.5);
1507 CL_NewQuakeParticle(center, pt_alphastatic, color, color, 0, pos[0], pos[1], pos[2], 30 * -dir[1], 30 * dir[0], 0, 0, 0, 0, 0, 0.5);
1508 }
1509 else
1510 {
1511 color = particlepalette[226 + (rand()&7)];
1512 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, NULL);
1513 }
1514 }
1515 else if (effectnameindex == EFFECT_TR_VORESPIKE)
1516 {
1518 {
1519 color = particlepalette[152 + (rand()&3)];
1520 CL_NewQuakeParticle(center, pt_alphastatic, color, color, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 8, 0, 0.3);
1521 }
1522 else if (gamemode == GAME_GOODVSBAD2)
1523 {
1524 dec = 6;
1525 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, NULL);
1526 }
1527 else if (gamemode == GAME_PRYDON)
1528 {
1529 dec = 6;
1530 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, NULL);
1531 }
1532 else
1533 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, NULL);
1534 }
1535 else if (effectnameindex == EFFECT_TR_NEHAHRASMOKE)
1536 {
1537 dec = 7;
1538 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, NULL);
1539 }
1540 else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
1541 {
1542 dec = 4;
1543 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, NULL);
1544 }
1545 else if (effectnameindex == EFFECT_TR_GLOWTRAIL)
1546 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, NULL);
1547 }
1548 if (bubbles)
1549 {
1550 if (effectnameindex == EFFECT_TR_ROCKET)
1551 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, NULL);
1552 else if (effectnameindex == EFFECT_TR_GRENADE)
1553 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, NULL);
1554 }
1555 // advance to next time and position
1556 dec *= qd;
1557 len -= dec;
1558 VectorMA (pos, dec, dir, pos);
1559 }
1560 if (ent)
1561 ent->persistent.trail_time = len;
1562 }
1563 else
1564 Con_DPrintf("CL_ParticleEffect_Fallback: no fallback found for effect %s\n", particleeffectname[effectnameindex]);
1565}
1566
1567// this is also called on point effects with spawndlight = true and
1568// spawnparticles = true
1569static void CL_NewParticlesFromEffectinfo(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, qbool spawndlight, qbool spawnparticles, float tintmins[4], float tintmaxs[4], float fade, qbool wanttrail)
1570{
1571 qbool found = false;
1572 char vabuf[1024];
1573 if (effectnameindex < 1 || effectnameindex >= MAX_PARTICLEEFFECTNAME || !particleeffectname[effectnameindex][0])
1574 {
1575 Con_DPrintf("Unknown effect number %i received from server\n", effectnameindex);
1576 return; // no such effect
1577 }
1579 {
1580 int effectinfoindex;
1581 int supercontents;
1582 int tex, staintex;
1584 vec3_t center;
1585 vec3_t traildir;
1586 vec3_t trailpos;
1587 vec3_t rvec;
1588 vec3_t angles;
1591 vec3_t right;
1592 vec3_t up;
1593 vec_t traillen;
1594 vec_t trailstep;
1596 qbool immediatebloodstain;
1597 particle_t *part;
1598 float avgtint[4], tint[4], tintlerp;
1599 // note this runs multiple effects with the same name, each one spawns only one kind of particle, so some effects need more than one
1600 VectorLerp(originmins, 0.5, originmaxs, center);
1601 supercontents = CL_PointSuperContents(center);
1602 underwater = (supercontents & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)) != 0;
1603 VectorSubtract(originmaxs, originmins, traildir);
1604 traillen = VectorLength(traildir);
1605 VectorNormalize(traildir);
1606 if(tintmins)
1607 {
1608 Vector4Lerp(tintmins, 0.5, tintmaxs, avgtint);
1609 }
1610 else
1611 {
1612 Vector4Set(avgtint, 1, 1, 1, 1);
1613 }
1614 for (effectinfoindex = 0, info = particleeffectinfo;effectinfoindex < MAX_PARTICLEEFFECTINFO && info->effectnameindex;effectinfoindex++, info++)
1615 {
1616 if ((info->effectnameindex == effectnameindex) && (info->flags & PARTICLEEFFECT_DEFINED))
1617 {
1618 qbool definedastrail = info->trailspacing > 0;
1619
1620 qbool drawastrail = wanttrail;
1622 drawastrail = drawastrail || definedastrail;
1623
1624 found = true;
1625 if ((info->flags & PARTICLEEFFECT_UNDERWATER) && !underwater)
1626 continue;
1628 continue;
1629
1630 // spawn a dlight if requested
1631 if (info->lightradiusstart > 0 && spawndlight)
1632 {
1633 matrix4x4_t tempmatrix;
1634 if (drawastrail)
1635 Matrix4x4_CreateTranslate(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2]);
1636 else
1637 Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
1638 if (info->lighttime > 0 && info->lightradiusfade > 0)
1639 {
1640 // light flash (explosion, etc)
1641 // called when effect starts
1642 CL_AllocLightFlash(NULL, &tempmatrix, info->lightradiusstart, info->lightcolor[0]*avgtint[0]*avgtint[3], info->lightcolor[1]*avgtint[1]*avgtint[3], info->lightcolor[2]*avgtint[2]*avgtint[3], info->lightradiusfade, info->lighttime, LightCubemapNumToName(vabuf, sizeof(vabuf), info->lightcubemapnum, info->flags), -1, info->lightshadow, info->lightcorona[0], info->lightcorona[1], 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1643 }
1645 {
1646 // glowing entity
1647 // called by CL_LinkNetworkEntity
1648 Matrix4x4_Scale(&tempmatrix, info->lightradiusstart, 1);
1649 rvec[0] = info->lightcolor[0]*avgtint[0]*avgtint[3];
1650 rvec[1] = info->lightcolor[1]*avgtint[1]*avgtint[3];
1651 rvec[2] = info->lightcolor[2]*avgtint[2]*avgtint[3];
1652 R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &tempmatrix, rvec, -1, LightCubemapNumToName(vabuf, sizeof(vabuf), info->lightcubemapnum, info->flags), info->lightshadow, info->lightcorona[0], info->lightcorona[1], 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1654 }
1655 }
1656
1657 if (!spawnparticles)
1658 continue;
1659
1660 // spawn particles
1661 tex = info->tex[0];
1662 if (info->tex[1] > info->tex[0])
1663 {
1664 tex = (int)lhrandom(info->tex[0], info->tex[1]);
1665 tex = min(tex, info->tex[1] - 1);
1666 }
1667 if(info->staintex[0] < 0)
1668 staintex = info->staintex[0];
1669 else
1670 {
1671 staintex = (int)lhrandom(info->staintex[0], info->staintex[1]);
1672 staintex = min(staintex, info->staintex[1] - 1);
1673 }
1674 if (info->particletype == pt_decal)
1675 {
1676 VectorMAM(0.5f, velocitymins, 0.5f, velocitymaxs, velocity);
1679 VectorMAMAMAM(1.0f, center, info->relativeoriginoffset[0], forward, info->relativeoriginoffset[1], right, info->relativeoriginoffset[2], up, trailpos);
1680
1681 CL_SpawnDecalParticleForPoint(trailpos, info->originjitter[0], lhrandom(info->size[0], info->size[1]), lhrandom(info->alpha[0], info->alpha[1])*avgtint[3], tex, info->color[0], info->color[1]);
1682 }
1683 else if (info->orientation == PARTICLE_HBEAM)
1684 {
1685 if (!drawastrail)
1686 continue;
1687
1688 AnglesFromVectors(angles, traildir, NULL, false);
1691
1692 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] + trailpos[0], originmins[1] + trailpos[1], originmins[2] + trailpos[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, tintmins ? avgtint : NULL);
1693 }
1694 else
1695 {
1696 float cnt;
1697 if (!cl_particles.integer)
1698 continue;
1699 switch (info->particletype)
1700 {
1701 case pt_smoke: if (!cl_particles_smoke.integer) continue;break;
1702 case pt_spark: if (!cl_particles_sparks.integer) continue;break;
1703 case pt_bubble: if (!cl_particles_bubbles.integer) continue;break;
1704 case pt_blood: if (!cl_particles_blood.integer) continue;break;
1705 case pt_rain: if (!cl_particles_rain.integer) continue;break;
1706 case pt_snow: if (!cl_particles_snow.integer) continue;break;
1707 default: break;
1708 }
1709
1710 cnt = info->countabsolute;
1711 cnt += (pcount * info->countmultiplier) * cl_particles_quality.value;
1712 // if drawastrail is not set, we will
1713 // use the regular cnt-based random
1714 // particle spawning at the center; so
1715 // do NOT apply trailspacing then!
1716 if (drawastrail && definedastrail)
1717 cnt += (traillen / info->trailspacing) * cl_particles_quality.value;
1718 cnt *= fade;
1719 if (cnt == 0)
1720 continue; // nothing to draw
1721 info->particleaccumulator += cnt;
1722
1723 if (drawastrail || definedastrail)
1724 immediatebloodstain = false;
1725 else
1726 immediatebloodstain =
1728 ||
1730
1731 if (drawastrail)
1732 {
1733 VectorCopy(originmins, trailpos);
1734 trailstep = traillen / cnt;
1735 }
1736 else
1737 {
1738 VectorCopy(center, trailpos);
1739 trailstep = 0;
1740 }
1741
1742 if (trailstep == 0)
1743 {
1744 VectorMAM(0.5f, velocitymins, 0.5f, velocitymaxs, velocity);
1746 }
1747 else
1748 AnglesFromVectors(angles, traildir, NULL, false);
1749
1751 VectorMAMAMAM(1.0f, trailpos, info->relativeoriginoffset[0], forward, info->relativeoriginoffset[1], right, info->relativeoriginoffset[2], up, trailpos);
1753 info->particleaccumulator = bound(0, info->particleaccumulator, 16384);
1754 for (;info->particleaccumulator >= 1;info->particleaccumulator--)
1755 {
1756 if (info->tex[1] > info->tex[0])
1757 {
1758 tex = (int)lhrandom(info->tex[0], info->tex[1]);
1759 tex = min(tex, info->tex[1] - 1);
1760 }
1761 if (!(drawastrail || definedastrail))
1762 {
1763 trailpos[0] = lhrandom(originmins[0], originmaxs[0]);
1764 trailpos[1] = lhrandom(originmins[1], originmaxs[1]);
1765 trailpos[2] = lhrandom(originmins[2], originmaxs[2]);
1766 }
1767 if(tintmins)
1768 {
1769 tintlerp = lhrandom(0, 1);
1770 Vector4Lerp(tintmins, tintlerp, tintmaxs, tint);
1771 }
1772 VectorRandom(rvec);
1773 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] + velocity[0], lhrandom(velocitymins[1], velocitymaxs[1]) * info->velocitymultiplier + info->velocityoffset[1] + info->velocityjitter[1] * rvec[1] + velocity[1], lhrandom(velocitymins[2], velocitymaxs[2]) * info->velocitymultiplier + info->velocityoffset[2] + info->velocityjitter[2] * rvec[2] + velocity[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]), tintmins ? tint : NULL);
1774 if (immediatebloodstain && part)
1775 {
1776 immediatebloodstain = false;
1778 }
1779 if (trailstep)
1780 VectorMA(trailpos, trailstep, traildir, trailpos);
1781 }
1782 }
1783 }
1784 }
1785 }
1786 if (!found)
1787 CL_ParticleEffect_Fallback(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, spawndlight, spawnparticles, wanttrail);
1788}
1789
1790void 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, qbool spawndlight, qbool spawnparticles, float tintmins[4], float tintmaxs[4], float fade)
1791{
1792 CL_NewParticlesFromEffectinfo(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, spawndlight, spawnparticles, tintmins, tintmaxs, fade, true);
1793}
1794
1795void CL_ParticleBox(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, qbool spawndlight, qbool spawnparticles, float tintmins[4], float tintmaxs[4], float fade)
1796{
1797 CL_NewParticlesFromEffectinfo(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, spawndlight, spawnparticles, tintmins, tintmaxs, fade, false);
1798}
1799
1800// note: this one ONLY does boxes!
1801void 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)
1802{
1803 CL_ParticleBox(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, true, true, NULL, NULL, 1);
1804}
1805
1806/*
1807===============
1808CL_EntityParticles
1809===============
1810*/
1812{
1813 int i, j;
1814 vec_t pitch, yaw, dist = 64, beamlength = 16;
1815 vec3_t org, v;
1816 static vec3_t avelocities[NUMVERTEXNORMALS];
1817 if (!cl_particles.integer) return;
1818 if (cl.time <= cl.oldtime) return; // don't spawn new entity particles while paused
1819
1821
1822 if (!avelocities[0][0])
1823 for (i = 0;i < NUMVERTEXNORMALS;i++)
1824 for (j = 0;j < 3;j++)
1825 avelocities[i][j] = lhrandom(0, 2.55);
1826
1827 for (i = 0;i < NUMVERTEXNORMALS;i++)
1828 {
1829 yaw = cl.time * avelocities[i][0];
1830 pitch = cl.time * avelocities[i][1];
1831 v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
1832 v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
1833 v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
1834 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, NULL);
1835 }
1836}
1837
1838
1840{
1841 double org[3], leakorg[3];
1842 vec3_t vecorg;
1843 int r, c, s;
1844 char *pointfile = NULL, *pointfilepos, *t, tchar;
1845 char name[MAX_QPATH];
1846
1847 if (!cl.worldmodel)
1848 return;
1849
1850 dpsnprintf(name, sizeof(name), "%s.pts", cl.worldnamenoextension);
1851 pointfile = (char *)FS_LoadFile(name, tempmempool, true, NULL);
1852 if (!pointfile)
1853 {
1854 Con_Printf("Could not open %s\n", name);
1855 return;
1856 }
1857
1858 Con_Printf("Reading %s...\n", name);
1859 VectorClear(leakorg);
1860 c = 0;
1861 s = 0;
1862 pointfilepos = pointfile;
1863 while (*pointfilepos)
1864 {
1865 while (*pointfilepos == '\n' || *pointfilepos == '\r')
1866 pointfilepos++;
1867 if (!*pointfilepos)
1868 break;
1869 t = pointfilepos;
1870 while (*t && *t != '\n' && *t != '\r')
1871 t++;
1872 tchar = *t;
1873 *t = 0;
1874#if _MSC_VER >= 1400
1875#define sscanf sscanf_s
1876#endif
1877 r = sscanf (pointfilepos,"%lf %lf %lf", &org[0], &org[1], &org[2]);
1878 VectorCopy(org, vecorg);
1879 *t = tchar;
1880 pointfilepos = t;
1881 if (r != 3)
1882 break;
1883 if (c == 0)
1884 VectorCopy(org, leakorg);
1885 c++;
1886
1887 if (cl.num_particles < cl.max_particles - 3)
1888 {
1889 s++;
1890 CL_NewParticle(vecorg, 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, NULL);
1891 }
1892 }
1893 Mem_Free(pointfile);
1894 VectorCopy(leakorg, vecorg);
1895 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, leakorg[0], leakorg[1], leakorg[2]);
1896
1897 if (c == 0)
1898 {
1899 return;
1900 }
1901
1902 CL_NewParticle(vecorg, 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, NULL);
1903 CL_NewParticle(vecorg, 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, NULL);
1904 CL_NewParticle(vecorg, 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, NULL);
1905}
1906
1907/*
1908===============
1909CL_ParseParticleEffect
1910
1911Parse an effect out of the server message
1912===============
1913*/
1915{
1916 vec3_t org, dir;
1917 int i, count, msgcount, color;
1918
1920 for (i=0 ; i<3 ; i++)
1921 dir[i] = MSG_ReadChar(&cl_message) * (1.0 / 16.0);
1922 msgcount = MSG_ReadByte(&cl_message);
1924
1925 if (msgcount == 255)
1926 count = 1024;
1927 else
1928 count = msgcount;
1929
1931}
1932
1933/*
1934===============
1935CL_ParticleExplosion
1936
1937===============
1938*/
1940{
1941 int i;
1942 trace_t trace;
1943
1944 R_Stain(org, 96, 40, 40, 40, 64, 88, 88, 88, 64);
1945 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1946
1948 {
1949 for (i = 0; i < 1024; i++)
1950 {
1951 int color;
1952 int r = rand()&3;
1953
1954 if (i & 1)
1955 {
1957
1959 org,
1960 pt_explode,
1961 color, color,
1962 0.05, // gravity
1963 org[0], org[1], org[2], // offset
1964 0, 0, 0, // velocity
1965 2, // air friction
1966 0, // liquid friction
1967 16, // origin jitter
1968 256, // velocity jitter
1969 5 // lifetime
1970 );
1971 }
1972 else
1973 {
1975
1977 org,
1979 color, color,
1980 0.05, // gravity
1981 org[0], org[1], org[2], // offset
1982 0, 0, 0, // velocity
1983 0, // air friction
1984 0, // liquid friction
1985 16, // origin jitter
1986 256, // velocity jitter
1987 5 // lifetime
1988 );
1989 }
1990 }
1991 }
1992 else
1993 {
1994 i = CL_PointSuperContents(org);
1996 {
1998 for (i = 0;i < 128 * cl_particles_quality.value;i++)
1999 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, NULL);
2000 }
2001 else
2002 {
2004 {
2005 for (i = 0;i < 512 * cl_particles_quality.value;i++)
2006 {
2007 int k = 0;
2008 vec3_t v, v2;
2009 do
2010 {
2012 VectorMA(org, 128, v2, v);
2013 trace = CL_TraceLine(org, v, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, 0, 0, collision_extendmovelength.value, true, false, NULL, false, false);
2014 }
2015 while (k++ < 16 && trace.fraction < 0.1f);
2016 VectorSubtract(trace.endpos, org, v2);
2017 VectorScale(v2, 2.0f, v2);
2018 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, NULL);
2019 }
2020 }
2021 }
2022 }
2023
2025 R_NewExplosion(org);
2026}
2027
2028/*
2029===============
2030CL_ParticleExplosion2
2031
2032===============
2033*/
2034void CL_ParticleExplosion2 (const vec3_t org, int colorStart, int colorLength)
2035{
2036 int i, k;
2037 if (!cl_particles.integer) return;
2038
2039 for (i = 0;i < 512 * cl_particles_quality.value;i++)
2040 {
2041 k = particlepalette[colorStart + (i % colorLength)];
2043 CL_NewQuakeParticle(org, pt_alphastatic, k, k, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256, 0.3);
2044 else
2045 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, NULL);
2046 }
2047}
2048
2049static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount)
2050{
2051 vec3_t center;
2052 VectorMAM(0.5f, originmins, 0.5f, originmaxs, center);
2054 {
2055 sparkcount *= cl_particles_quality.value;
2056 while(sparkcount-- > 0)
2057 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, NULL);
2058 }
2059}
2060
2061static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount)
2062{
2063 vec3_t center;
2064 VectorMAM(0.5f, originmins, 0.5f, originmaxs, center);
2066 {
2067 smokecount *= cl_particles_quality.value;
2068 while(smokecount-- > 0)
2069 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, NULL);
2070 }
2071}
2072
2073void CL_ParticleCube (const vec3_t mins, const vec3_t maxs, const vec3_t dir, int count, int colorbase, vec_t gravity, vec_t randomvel)
2074{
2075 vec3_t center;
2076 int k;
2077 if (!cl_particles.integer) return;
2078 VectorMAM(0.5f, mins, 0.5f, maxs, center);
2079
2081 while (count--)
2082 {
2083 k = particlepalette[colorbase + (rand()&3)];
2084 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, NULL);
2085 }
2086}
2087
2088void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, int count, int colorbase, int type)
2089{
2090 int k;
2091 float minz, maxz, lifetime = 30;
2092 float particle_size;
2093 vec3_t org;
2094
2095 if (!cl_particles.integer) return;
2096 if (dir[2] < 0) // falling
2097 {
2098 minz = maxs[2] + dir[2] * 0.1;
2099 maxz = maxs[2];
2100 if (cl.worldmodel)
2101 lifetime = (maxz - cl.worldmodel->normalmins[2]) / max(1, -dir[2]);
2102 }
2103 else // rising??
2104 {
2105 minz = mins[2];
2106 maxz = maxs[2] + dir[2] * 0.1;
2107 if (cl.worldmodel)
2108 lifetime = (cl.worldmodel->normalmaxs[2] - minz) / max(1, dir[2]);
2109 }
2110
2112
2113 switch(type)
2114 {
2115 case 0:
2116 if (!cl_particles_rain.integer) break;
2117
2118 count *= 4; // ick, this should be in the mod or maps?
2119 particle_size = (gamemode == GAME_GOODVSBAD2) ? 20 : 0.5;
2120
2121 while(count--)
2122 {
2123 k = particlepalette[colorbase + (rand()&3)];
2124 VectorSet(org, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz));
2125 CL_NewParticle(org, pt_rain, k, k, tex_particle, particle_size, 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, NULL);
2126 }
2127 break;
2128 case 1:
2129 if (!cl_particles_snow.integer) break;
2130
2131 particle_size = (gamemode == GAME_GOODVSBAD2) ? 20 : 1.0;
2132
2133 while(count--)
2134 {
2135 k = particlepalette[colorbase + (rand()&3)];
2136 VectorSet(org, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz));
2137 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, NULL);
2138 }
2139 break;
2140 default:
2141 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
2142 }
2143}
2144
2145cvar_t r_drawparticles = {CF_CLIENT, "r_drawparticles", "1", "enables drawing of particles"};
2146static cvar_t r_drawparticles_drawdistance = {CF_CLIENT | CF_ARCHIVE, "r_drawparticles_drawdistance", "2000", "particles further than drawdistance*size will not be drawn"};
2147static cvar_t r_drawparticles_nearclip_min = {CF_CLIENT | CF_ARCHIVE, "r_drawparticles_nearclip_min", "4", "particles closer than drawnearclip_min will not be drawn"};
2148static cvar_t r_drawparticles_nearclip_max = {CF_CLIENT | CF_ARCHIVE, "r_drawparticles_nearclip_max", "4", "particles closer than drawnearclip_min will be faded"};
2149cvar_t r_drawdecals = {CF_CLIENT, "r_drawdecals", "1", "enables drawing of decals"};
2150static cvar_t r_drawdecals_drawdistance = {CF_CLIENT | CF_ARCHIVE, "r_drawdecals_drawdistance", "500", "decals further than drawdistance*size will not be drawn"};
2151
2152#define PARTICLETEXTURESIZE 64
2153#define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
2154
2155static unsigned char shadebubble(float dx, float dy, vec3_t light)
2156{
2157 float dz, f, dot;
2158 vec3_t normal;
2159 dz = 1 - (dx*dx+dy*dy);
2160 if (dz > 0) // it does hit the sphere
2161 {
2162 f = 0;
2163 // back side
2164 normal[0] = dx;normal[1] = dy;normal[2] = dz;
2166 dot = DotProduct(normal, light);
2167 if (dot > 0.5) // interior reflection
2168 f += ((dot * 2) - 1);
2169 else if (dot < -0.5) // exterior reflection
2170 f += ((dot * -2) - 1);
2171 // front side
2172 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
2174 dot = DotProduct(normal, light);
2175 if (dot > 0.5) // interior reflection
2176 f += ((dot * 2) - 1);
2177 else if (dot < -0.5) // exterior reflection
2178 f += ((dot * -2) - 1);
2179 f *= 128;
2180 f += 16; // just to give it a haze so you can see the outline
2181 f = bound(0, f, 255);
2182 return (unsigned char) f;
2183 }
2184 else
2185 return 0;
2186}
2187
2189static void CL_Particle_PixelCoordsForTexnum(int texnum, int *basex, int *basey, int *width, int *height)
2190{
2191 *basex = (texnum % particlefontcols) * particlefontcellwidth;
2195}
2196
2197static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
2198{
2199 int basex, basey, w, h, y;
2200 CL_Particle_PixelCoordsForTexnum(texnum, &basex, &basey, &w, &h);
2202 Sys_Error("invalid particle texture size for autogenerating");
2203 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2204 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
2205}
2206
2207static void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
2208{
2209 int x, y;
2210 float cx, cy, dx, dy, f, iradius;
2211 unsigned char *d;
2212 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
2213 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
2214 iradius = 1.0f / radius;
2215 alpha *= (1.0f / 255.0f);
2216 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2217 {
2218 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2219 {
2220 dx = (x - cx);
2221 dy = (y - cy);
2222 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
2223 if (f > 0)
2224 {
2225 if (f > 1)
2226 f = 1;
2227 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
2228 d[0] += (int)(f * (blue - d[0]));
2229 d[1] += (int)(f * (green - d[1]));
2230 d[2] += (int)(f * (red - d[2]));
2231 }
2232 }
2233 }
2234}
2235
2236#if 0
2237static void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
2238{
2239 int i;
2240 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
2241 {
2242 data[0] = bound(minb, data[0], maxb);
2243 data[1] = bound(ming, data[1], maxg);
2244 data[2] = bound(minr, data[2], maxr);
2245 }
2246}
2247#endif
2248
2249static void particletextureinvert(unsigned char *data)
2250{
2251 int i;
2252 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
2253 {
2254 data[0] = 255 - data[0];
2255 data[1] = 255 - data[1];
2256 data[2] = 255 - data[2];
2257 }
2258}
2259
2260// Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
2261static void R_InitBloodTextures (unsigned char *particletexturedata)
2262{
2263 int i, j, k, m;
2264 size_t datasize = PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4;
2265 unsigned char *data = (unsigned char *)Mem_Alloc(tempmempool, datasize);
2266
2267 // blood particles
2268 for (i = 0;i < 8;i++)
2269 {
2270 memset(data, 255, datasize);
2271 for (k = 0;k < 24;k++)
2273 //particletextureclamp(data, 32, 32, 32, 255, 255, 255);
2275 setuptex(tex_bloodparticle[i], data, particletexturedata);
2276 }
2277
2278 // blood decals
2279 for (i = 0;i < 8;i++)
2280 {
2281 memset(data, 255, datasize);
2282 m = 8;
2283 for (j = 1;j < 10;j++)
2284 for (k = min(j, m - 1);k < m;k++)
2285 particletextureblotch(data, (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 320 - j * 8);
2286 //particletextureclamp(data, 32, 32, 32, 255, 255, 255);
2288 setuptex(tex_blooddecal[i], data, particletexturedata);
2289 }
2290
2291 Mem_Free(data);
2292}
2293
2294//uncomment this to make engine save out particle font to a tga file when run
2295//#define DUMPPARTICLEFONT
2296
2297static void R_InitParticleTexture (void)
2298{
2299 int x, y, d, i, k, m;
2300 int basex, basey, w, h;
2301 float dx, dy, f, s1, t1, s2, t2;
2302 vec3_t light;
2303 char *buf;
2304 fs_offset_t filesize;
2305 char texturename[MAX_QPATH];
2306 skinframe_t *sf;
2307
2308 // a note: decals need to modulate (multiply) the background color to
2309 // properly darken it (stain), and they need to be able to alpha fade,
2310 // this is a very difficult challenge because it means fading to white
2311 // (no change to background) rather than black (darkening everything
2312 // behind the whole decal polygon), and to accomplish this the texture is
2313 // inverted (dark red blood on white background becomes brilliant cyan
2314 // and white on black background) so we can alpha fade it to black, then
2315 // we invert it again during the blendfunc to make it work...
2316
2317#ifndef DUMPPARTICLEFONT
2318 decalskinframe = R_SkinFrame_LoadExternal("particles/particlefont.tga", TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, false, false);
2319 if (decalskinframe)
2320 {
2322 // TODO maybe allow custom grid size?
2327 particlefontcols = 8;
2328 particlefontrows = 8;
2329 }
2330 else
2331#endif
2332 {
2333 unsigned char *particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
2334 size_t datasize = PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4;
2335 unsigned char *data = (unsigned char *)Mem_Alloc(tempmempool, datasize);
2336 unsigned char *noise1 = (unsigned char *)Mem_Alloc(tempmempool, PARTICLETEXTURESIZE*2*PARTICLETEXTURESIZE*2);
2337 unsigned char *noise2 = (unsigned char *)Mem_Alloc(tempmempool, PARTICLETEXTURESIZE*2*PARTICLETEXTURESIZE*2);
2338
2341 particlefontcols = 8;
2342 particlefontrows = 8;
2343
2344 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
2345
2346 // smoke
2347 for (i = 0;i < 8;i++)
2348 {
2349 memset(data, 255, datasize);
2350 do
2351 {
2354 m = 0;
2355 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2356 {
2357 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2358 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2359 {
2360 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2361 d = (noise2[y*PARTICLETEXTURESIZE*2+x] - 128) * 3 + 192;
2362 if (d > 0)
2363 d = (int)(d * (1-(dx*dx+dy*dy)));
2364 d = (d * noise1[y*PARTICLETEXTURESIZE*2+x]) >> 7;
2365 d = bound(0, d, 255);
2366 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (unsigned char) d;
2367 if (m < d)
2368 m = d;
2369 }
2370 }
2371 }
2372 while (m < 224);
2373 setuptex(tex_smoke[i], data, particletexturedata);
2374 }
2375
2376 // rain splash
2377 memset(data, 255, datasize);
2378 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2379 {
2380 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2381 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2382 {
2383 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2384 f = 255.0f * (1.0 - 4.0f * fabs(10.0f - sqrt(dx*dx+dy*dy)));
2385 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (int) (bound(0.0f, f, 255.0f));
2386 }
2387 }
2388 setuptex(tex_rainsplash, data, particletexturedata);
2389
2390 // normal particle
2391 memset(data, 255, datasize);
2392 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2393 {
2394 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2395 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2396 {
2397 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2398 d = (int)(256 * (1 - (dx*dx+dy*dy)));
2399 d = bound(0, d, 255);
2400 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (unsigned char) d;
2401 }
2402 }
2403 setuptex(tex_particle, data, particletexturedata);
2404
2405 // rain
2406 memset(data, 255, datasize);
2407 light[0] = 1;light[1] = 1;light[2] = 1;
2408 VectorNormalize(light);
2409 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2410 {
2411 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2412 // stretch upper half of bubble by +50% and shrink lower half by -50%
2413 // (this gives an elongated teardrop shape)
2414 if (dy > 0.5f)
2415 dy = (dy - 0.5f) * 2.0f;
2416 else
2417 dy = (dy - 0.5f) / 1.5f;
2418 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2419 {
2420 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2421 // shrink bubble width to half
2422 dx *= 2.0f;
2423 data[(y*PARTICLETEXTURESIZE+x)*4+3] = shadebubble(dx, dy, light);
2424 }
2425 }
2426 setuptex(tex_raindrop, data, particletexturedata);
2427
2428 // bubble
2429 memset(data, 255, datasize);
2430 light[0] = 1;light[1] = 1;light[2] = 1;
2431 VectorNormalize(light);
2432 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2433 {
2434 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2435 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2436 {
2437 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2438 data[(y*PARTICLETEXTURESIZE+x)*4+3] = shadebubble(dx, dy, light);
2439 }
2440 }
2441 setuptex(tex_bubble, data, particletexturedata);
2442
2443 // Blood particles and blood decals
2444 R_InitBloodTextures (particletexturedata);
2445
2446 // bullet decals
2447 for (i = 0;i < 8;i++)
2448 {
2449 memset(data, 255, datasize);
2450 for (k = 0;k < 12;k++)
2452 for (k = 0;k < 3;k++)
2454 //particletextureclamp(data, 64, 64, 64, 255, 255, 255);
2456 setuptex(tex_bulletdecal[i], data, particletexturedata);
2457 }
2458
2459#ifdef DUMPPARTICLEFONT
2460 Image_WriteTGABGRA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
2461#endif
2462
2465
2466 Mem_Free(particletexturedata);
2467 Mem_Free(data);
2470 }
2471 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
2472 {
2473 CL_Particle_PixelCoordsForTexnum(i, &basex, &basey, &w, &h);
2475 particletexture[i].s1 = (basex + 1) / (float)particlefontwidth;
2476 particletexture[i].t1 = (basey + 1) / (float)particlefontheight;
2477 particletexture[i].s2 = (basex + w - 1) / (float)particlefontwidth;
2478 particletexture[i].t2 = (basey + h - 1) / (float)particlefontheight;
2479 }
2480
2481#ifndef DUMPPARTICLEFONT
2484#endif
2485 {
2486 unsigned char noise3[64][64], data2[64][16][4];
2487 // nexbeam
2488 fractalnoise(&noise3[0][0], 64, 4);
2489 m = 0;
2490 for (y = 0;y < 64;y++)
2491 {
2492 dy = (y - 0.5f*64) / (64*0.5f-1);
2493 for (x = 0;x < 16;x++)
2494 {
2495 dx = (x - 0.5f*16) / (16*0.5f-2);
2496 d = (int)((1 - sqrt(fabs(dx))) * noise3[y][x]);
2497 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
2498 data2[y][x][3] = 255;
2499 }
2500 }
2501
2502#ifdef DUMPPARTICLEFONT
2503 Image_WriteTGABGRA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
2504#endif
2506 }
2511
2512 // now load an texcoord/texture override file
2513 buf = (char *) FS_LoadFile("particles/particlefont.txt", tempmempool, false, &filesize);
2514 if(buf)
2515 {
2516 const char *bufptr;
2517 bufptr = buf;
2518 for(;;)
2519 {
2520 if(!COM_ParseToken_Simple(&bufptr, true, false, true))
2521 break;
2522 if(!strcmp(com_token, "\n"))
2523 continue; // empty line
2524 i = atoi(com_token);
2525
2526 texturename[0] = 0;
2527 s1 = 0;
2528 t1 = 0;
2529 s2 = 1;
2530 t2 = 1;
2531
2532 if (COM_ParseToken_Simple(&bufptr, true, false, true) && strcmp(com_token, "\n"))
2533 {
2534 dp_strlcpy(texturename, com_token, sizeof(texturename));
2535 s1 = atof(com_token);
2536 if (COM_ParseToken_Simple(&bufptr, true, false, true) && strcmp(com_token, "\n"))
2537 {
2538 texturename[0] = 0;
2539 t1 = atof(com_token);
2540 if (COM_ParseToken_Simple(&bufptr, true, false, true) && strcmp(com_token, "\n"))
2541 {
2542 s2 = atof(com_token);
2543 if (COM_ParseToken_Simple(&bufptr, true, false, true) && strcmp(com_token, "\n"))
2544 {
2545 t2 = atof(com_token);
2546 dp_strlcpy(texturename, "particles/particlefont.tga", sizeof(texturename));
2547 if (COM_ParseToken_Simple(&bufptr, true, false, true) && strcmp(com_token, "\n"))
2548 dp_strlcpy(texturename, com_token, sizeof(texturename));
2549 }
2550 }
2551 }
2552 else
2553 s1 = 0;
2554 }
2555 if (!texturename[0])
2556 {
2557 Con_Printf("particles/particlefont.txt: syntax should be texnum x1 y1 x2 y2 texturename or texnum x1 y1 x2 y2 or texnum texturename\n");
2558 continue;
2559 }
2561 {
2562 Con_Printf("particles/particlefont.txt: texnum %i outside valid range (0 to %i)\n", i, MAX_PARTICLETEXTURES);
2563 continue;
2564 }
2565 sf = R_SkinFrame_LoadExternal(texturename, TEXF_ALPHA | TEXF_FORCELINEAR | TEXF_RGBMULTIPLYBYALPHA, true, true); // note: this loads as sRGB if sRGB is active!
2567 particletexture[i].s1 = s1;
2568 particletexture[i].t1 = t1;
2569 particletexture[i].s2 = s2;
2570 particletexture[i].t2 = t2;
2571 }
2572 Mem_Free(buf);
2573 }
2574}
2575
2576static void r_part_start(void)
2577{
2578 int i;
2579 // generate particlepalette for convenience from the main one
2580 for (i = 0;i < 256;i++)
2581 particlepalette[i] = palette_rgb[i][0] * 65536 + palette_rgb[i][1] * 256 + palette_rgb[i][2];
2585}
2586
2587static void r_part_shutdown(void)
2588{
2590}
2591
2598
2601
2623
2624static void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2625{
2626 vec3_t vecorg, vecvel, baseright, baseup;
2627 int surfacelistindex;
2628 int batchstart, batchcount;
2629 const particle_t *p;
2630 pblend_t blendmode;
2632 float *v3f, *t2f, *c4f;
2633 particletexture_t *tex;
2634 float up2[3], v[3], right[3], up[3], fog, ifog, size, len, lenfactor, alpha;
2635// float ambient[3], diffuse[3], diffusenormal[3];
2636 float palpha, spintime, spinrad, spincos, spinsin, spinm1, spinm2, spinm3, spinm4;
2637 vec4_t colormultiplier;
2638 float minparticledist_start, minparticledist_end;
2639 qbool dofade;
2640
2641 RSurf_ActiveModelEntity(r_refdef.scene.worldentity, false, false, false);
2642
2643 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));
2644
2645 r_refdef.stats[r_stat_particles] += numsurfaces;
2646// R_Mesh_ResetTextureState();
2647 GL_DepthMask(false);
2648 GL_DepthRange(0, 1);
2649 GL_PolygonOffset(0, 0);
2650 GL_DepthTest(true);
2652
2653 spintime = r_refdef.scene.time;
2654
2657 dofade = (minparticledist_start < minparticledist_end);
2658
2659 // first generate all the vertices at once
2660 for (surfacelistindex = 0, v3f = particle_vertex3f, t2f = particle_texcoord2f, c4f = particle_color4f;surfacelistindex < numsurfaces;surfacelistindex++, v3f += 3*4, t2f += 2*4, c4f += 4*4)
2661 {
2662 p = cl.particles + surfacelist[surfacelistindex];
2663
2664 blendmode = (pblend_t)p->blendmode;
2665 palpha = p->alpha;
2666 if(dofade && p->orientation != PARTICLE_VBEAM && p->orientation != PARTICLE_HBEAM)
2667 palpha *= min(1, (DotProduct(p->org, r_refdef.view.forward) - minparticledist_start) / (minparticledist_end - minparticledist_start));
2668 alpha = palpha * colormultiplier[3];
2669 // ensure alpha multiplier saturates properly
2670 if (alpha > 1.0f)
2671 alpha = 1.0f;
2672
2673 switch (blendmode)
2674 {
2675 case PBLEND_INVALID:
2676 case PBLEND_INVMOD:
2677 // additive and modulate can just fade out in fog (this is correct)
2678 if (r_refdef.fogenabled)
2679 alpha *= RSurf_FogVertex(p->org);
2680 // collapse alpha into color for these blends (so that the particlefont does not need alpha on most textures)
2681 alpha *= 1.0f / 256.0f;
2682 c4f[0] = p->color[0] * alpha;
2683 c4f[1] = p->color[1] * alpha;
2684 c4f[2] = p->color[2] * alpha;
2685 c4f[3] = 0;
2686 break;
2687 case PBLEND_ADD:
2688 // additive and modulate can just fade out in fog (this is correct)
2689 if (r_refdef.fogenabled)
2690 alpha *= RSurf_FogVertex(p->org);
2691 // collapse alpha into color for these blends (so that the particlefont does not need alpha on most textures)
2692 c4f[0] = p->color[0] * colormultiplier[0] * alpha;
2693 c4f[1] = p->color[1] * colormultiplier[1] * alpha;
2694 c4f[2] = p->color[2] * colormultiplier[2] * alpha;
2695 c4f[3] = 0;
2696 break;
2697 case PBLEND_ALPHA:
2698 c4f[0] = p->color[0] * colormultiplier[0];
2699 c4f[1] = p->color[1] * colormultiplier[1];
2700 c4f[2] = p->color[2] * colormultiplier[2];
2701 c4f[3] = alpha;
2702 // note: lighting is not cheap!
2704 {
2705 float a[3], c[3], dir[3];
2706 vecorg[0] = p->org[0];
2707 vecorg[1] = p->org[1];
2708 vecorg[2] = p->org[2];
2710 c4f[0] = p->color[0] * colormultiplier[0] * (a[0] + 0.25f * c[0]);
2711 c4f[1] = p->color[1] * colormultiplier[1] * (a[1] + 0.25f * c[1]);
2712 c4f[2] = p->color[2] * colormultiplier[2] * (a[2] + 0.25f * c[2]);
2713 }
2714 // mix in the fog color
2715 if (r_refdef.fogenabled)
2716 {
2717 fog = RSurf_FogVertex(p->org);
2718 ifog = 1 - fog;
2719 c4f[0] = c4f[0] * fog + r_refdef.fogcolor[0] * ifog;
2720 c4f[1] = c4f[1] * fog + r_refdef.fogcolor[1] * ifog;
2721 c4f[2] = c4f[2] * fog + r_refdef.fogcolor[2] * ifog;
2722 }
2723 // for premultiplied alpha we have to apply the alpha to the color (after fog of course)
2724 VectorScale(c4f, alpha, c4f);
2725 break;
2726 }
2727 // copy the color into the other three vertices
2728 Vector4Copy(c4f, c4f + 4);
2729 Vector4Copy(c4f, c4f + 8);
2730 Vector4Copy(c4f, c4f + 12);
2731
2733 tex = &particletexture[p->texnum];
2734 switch(p->orientation)
2735 {
2736// case PARTICLE_INVALID:
2737 case PARTICLE_BILLBOARD:
2738 if (p->angle + p->spin)
2739 {
2740 spinrad = (p->angle + p->spin * (spintime - p->delayedspawn)) * (float)(M_PI / 180.0f);
2741 spinsin = sin(spinrad) * size;
2742 spincos = cos(spinrad) * size;
2743 spinm1 = -p->stretch * spincos;
2744 spinm2 = -spinsin;
2745 spinm3 = spinsin;
2746 spinm4 = -p->stretch * spincos;
2747 VectorMAM(spinm1, r_refdef.view.left, spinm2, r_refdef.view.up, right);
2748 VectorMAM(spinm3, r_refdef.view.left, spinm4, r_refdef.view.up, up);
2749 }
2750 else
2751 {
2754 }
2755
2756 v3f[ 0] = p->org[0] - right[0] - up[0];
2757 v3f[ 1] = p->org[1] - right[1] - up[1];
2758 v3f[ 2] = p->org[2] - right[2] - up[2];
2759 v3f[ 3] = p->org[0] - right[0] + up[0];
2760 v3f[ 4] = p->org[1] - right[1] + up[1];
2761 v3f[ 5] = p->org[2] - right[2] + up[2];
2762 v3f[ 6] = p->org[0] + right[0] + up[0];
2763 v3f[ 7] = p->org[1] + right[1] + up[1];
2764 v3f[ 8] = p->org[2] + right[2] + up[2];
2765 v3f[ 9] = p->org[0] + right[0] - up[0];
2766 v3f[10] = p->org[1] + right[1] - up[1];
2767 v3f[11] = p->org[2] + right[2] - up[2];
2768 t2f[0] = tex->s1;t2f[1] = tex->t2;
2769 t2f[2] = tex->s1;t2f[3] = tex->t1;
2770 t2f[4] = tex->s2;t2f[5] = tex->t1;
2771 t2f[6] = tex->s2;t2f[7] = tex->t2;
2772 break;
2774 vecvel[0] = p->vel[0];
2775 vecvel[1] = p->vel[1];
2776 vecvel[2] = p->vel[2];
2777 VectorVectors(vecvel, baseright, baseup);
2778 if (p->angle + p->spin)
2779 {
2780 spinrad = (p->angle + p->spin * (spintime - p->delayedspawn)) * (float)(M_PI / 180.0f);
2781 spinsin = sin(spinrad) * size;
2782 spincos = cos(spinrad) * size;
2783 spinm1 = p->stretch * spincos;
2784 spinm2 = -spinsin;
2785 spinm3 = spinsin;
2786 spinm4 = p->stretch * spincos;
2787 VectorMAM(spinm1, baseright, spinm2, baseup, right);
2788 VectorMAM(spinm3, baseright, spinm4, baseup, up);
2789 }
2790 else
2791 {
2792 VectorScale(baseright, size * p->stretch, right);
2793 VectorScale(baseup, size, up);
2794 }
2795 v3f[ 0] = p->org[0] - right[0] - up[0];
2796 v3f[ 1] = p->org[1] - right[1] - up[1];
2797 v3f[ 2] = p->org[2] - right[2] - up[2];
2798 v3f[ 3] = p->org[0] - right[0] + up[0];
2799 v3f[ 4] = p->org[1] - right[1] + up[1];
2800 v3f[ 5] = p->org[2] - right[2] + up[2];
2801 v3f[ 6] = p->org[0] + right[0] + up[0];
2802 v3f[ 7] = p->org[1] + right[1] + up[1];
2803 v3f[ 8] = p->org[2] + right[2] + up[2];
2804 v3f[ 9] = p->org[0] + right[0] - up[0];
2805 v3f[10] = p->org[1] + right[1] - up[1];
2806 v3f[11] = p->org[2] + right[2] - up[2];
2807 t2f[0] = tex->s1;t2f[1] = tex->t2;
2808 t2f[2] = tex->s1;t2f[3] = tex->t1;
2809 t2f[4] = tex->s2;t2f[5] = tex->t1;
2810 t2f[6] = tex->s2;t2f[7] = tex->t2;
2811 break;
2812 case PARTICLE_SPARK:
2813 len = VectorLength(p->vel);
2814 VectorNormalize2(p->vel, up);
2815 lenfactor = p->stretch * 0.04 * len;
2816 if(lenfactor < size * 0.5)
2817 lenfactor = size * 0.5;
2818 VectorMA(p->org, -lenfactor, up, v);
2819 VectorMA(p->org, lenfactor, up, up2);
2820 R_CalcBeam_Vertex3f(v3f, v, up2, size);
2821 t2f[0] = tex->s1;t2f[1] = tex->t2;
2822 t2f[2] = tex->s1;t2f[3] = tex->t1;
2823 t2f[4] = tex->s2;t2f[5] = tex->t1;
2824 t2f[6] = tex->s2;t2f[7] = tex->t2;
2825 break;
2826 case PARTICLE_VBEAM:
2827 R_CalcBeam_Vertex3f(v3f, p->org, p->vel, size);
2828 VectorSubtract(p->vel, p->org, up);
2830 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) * p->stretch;
2831 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f) * p->stretch;
2832 t2f[0] = tex->s2;t2f[1] = v[0];
2833 t2f[2] = tex->s1;t2f[3] = v[0];
2834 t2f[4] = tex->s1;t2f[5] = v[1];
2835 t2f[6] = tex->s2;t2f[7] = v[1];
2836 break;
2837 case PARTICLE_HBEAM:
2838 R_CalcBeam_Vertex3f(v3f, p->org, p->vel, size);
2839 VectorSubtract(p->vel, p->org, up);
2841 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) * p->stretch;
2842 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f) * p->stretch;
2843 t2f[0] = v[0];t2f[1] = tex->t1;
2844 t2f[2] = v[0];t2f[3] = tex->t2;
2845 t2f[4] = v[1];t2f[5] = tex->t2;
2846 t2f[6] = v[1];t2f[7] = tex->t1;
2847 break;
2848 }
2850 {
2851 R_DebugLine(v3f, v3f + 3);
2852 R_DebugLine(v3f + 3, v3f + 6);
2853 R_DebugLine(v3f + 6, v3f + 9);
2854 R_DebugLine(v3f + 9, v3f);
2855 }
2856 }
2857
2858 // now render batches of particles based on blendmode and texture
2859 blendmode = PBLEND_INVALID;
2860 texture = NULL;
2861 batchstart = 0;
2862 batchcount = 0;
2864 for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
2865 {
2866 p = cl.particles + surfacelist[surfacelistindex];
2867
2869 {
2871 R_SetupShader_Generic(texture, false, false, false);
2872 }
2873
2874 if (p->blendmode == PBLEND_INVMOD)
2875 {
2876 // inverse modulate blend - group these
2878 // iterate until we find a change in settings
2879 batchstart = surfacelistindex++;
2880 for (;surfacelistindex < numsurfaces;surfacelistindex++)
2881 {
2882 p = cl.particles + surfacelist[surfacelistindex];
2884 break;
2885 }
2886 }
2887 else
2888 {
2889 // additive or alpha blend - group these
2890 // (we can group these because we premultiplied the texture alpha)
2892 // iterate until we find a change in settings
2893 batchstart = surfacelistindex++;
2894 for (;surfacelistindex < numsurfaces;surfacelistindex++)
2895 {
2896 p = cl.particles + surfacelist[surfacelistindex];
2898 break;
2899 }
2900 }
2901
2902 batchcount = surfacelistindex - batchstart;
2903 R_Mesh_Draw(batchstart * 4, batchcount * 4, batchstart * 2, batchcount * 2, NULL, NULL, 0, particle_elements, NULL, 0);
2904 }
2905}
2906
2908{
2909 int i, a;
2910 int drawparticles = r_drawparticles.integer;
2911 float minparticledist_start;
2912 particle_t *p;
2913 float gravity, frametime, f, dist, oldorg[3], decaldir[3];
2914 float drawdist2;
2915 int hitent;
2916 trace_t trace;
2917 qbool update;
2918 float pt_explode_frame_interval, pt_explode2_frame_interval;
2919 int color;
2920
2923
2924 // LadyHavoc: early out conditions
2925 if (!cl.num_particles)
2926 return;
2927
2928 // Handling of the colour ramp for pt_explode and pt_explode2
2929 pt_explode_frame_interval = frametime * 10;
2930 pt_explode2_frame_interval = frametime * 15;
2931
2933 gravity = frametime * cl.movevars_gravity;
2934 update = frametime > 0;
2936 drawdist2 = drawdist2*drawdist2;
2937
2938 for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++)
2939 {
2940 if (!p->typeindex)
2941 {
2942 if (cl.free_particle > i)
2943 cl.free_particle = i;
2944 continue;
2945 }
2946
2947 if (update)
2948 {
2949 if (p->delayedspawn > cl.time)
2950 continue;
2951
2952 p->size += p->sizeincrease * frametime;
2953 p->alpha -= p->alphafade * frametime;
2954
2955 if (p->alpha <= 0 || p->die <= cl.time)
2956 goto killparticle;
2957
2959 {
2961 {
2962 if (p->typeindex == pt_blood)
2963 p->size += frametime * 8;
2964 else
2965 p->vel[2] -= p->gravity * gravity;
2966 f = 1.0f - min(p->liquidfriction * frametime, 1);
2967 VectorScale(p->vel, f, p->vel);
2968 }
2969 else
2970 {
2971 p->vel[2] -= p->gravity * gravity;
2972 if (p->airfriction)
2973 {
2974 f = 1.0f - min(p->airfriction * frametime, 1);
2975 VectorScale(p->vel, f, p->vel);
2976 }
2977 }
2978
2979 VectorCopy(p->org, oldorg);
2980 VectorMA(p->org, frametime, p->vel, p->org);
2981// if (p->bounce && cl.time >= p->delayedcollisions)
2983 {
2984 trace = CL_TraceLine(oldorg, p->org, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | ((p->typeindex == pt_rain || p->typeindex == pt_snow) ? SUPERCONTENTS_LIQUIDSMASK : 0), 0, 0, collision_extendmovelength.value, true, false, &hitent, false, false);
2985 // if the trace started in or hit something of SUPERCONTENTS_NODROP
2986 // or if the trace hit something flagged as NOIMPACT
2987 // then remove the particle
2989 goto killparticle;
2990 VectorCopy(trace.endpos, p->org);
2991 // react if the particle hit something
2992 if (trace.fraction < 1)
2993 {
2994 VectorCopy(trace.endpos, p->org);
2995
2996 if (p->staintexnum >= 0)
2997 {
2998 // blood - splash on solid
3000 {
3001 R_Stain(p->org, 16,
3002 p->staincolor[0], p->staincolor[1], p->staincolor[2], (int)(p->stainalpha * p->stainsize * (1.0f / 160.0f)),
3003 p->staincolor[0], p->staincolor[1], p->staincolor[2], (int)(p->stainalpha * p->stainsize * (1.0f / 160.0f)));
3004 if (cl_decals.integer)
3005 {
3006 // create a decal for the blood splat
3007 a = 0xFFFFFF ^ (p->staincolor[0]*65536+p->staincolor[1]*256+p->staincolor[2]);
3009 {
3010 VectorCopy(p->vel, decaldir);
3011 VectorNormalize(decaldir);
3012 }
3013 else
3014 VectorCopy(trace.plane.normal, decaldir);
3015 CL_SpawnDecalParticleForSurface(hitent, p->org, decaldir, a, a, p->staintexnum, p->stainsize, p->stainalpha); // staincolor needs to be inverted for decals!
3016 }
3017 }
3018 }
3019
3020 if (p->typeindex == pt_blood)
3021 {
3022 // blood - splash on solid
3024 goto killparticle;
3025 if(p->staintexnum == -1) // staintex < -1 means no stains at all
3026 {
3027 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)));
3028 if (cl_decals.integer)
3029 {
3030 // create a decal for the blood splat
3032 {
3033 VectorCopy(p->vel, decaldir);
3034 VectorNormalize(decaldir);
3035 }
3036 else
3037 VectorCopy(trace.plane.normal, decaldir);
3038 CL_SpawnDecalParticleForSurface(hitent, p->org, decaldir, 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);
3039 }
3040 }
3041 goto killparticle;
3042 }
3043 else if (p->bounce < 0)
3044 {
3045 // bounce -1 means remove on impact
3046 goto killparticle;
3047 }
3048 else
3049 {
3050 // anything else - bounce off solid
3051 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
3052 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
3053 }
3054 }
3055 }
3056
3057 if (VectorLength2(p->vel) < 0.03)
3058 {
3059 if(p->orientation == PARTICLE_SPARK) // sparks are virtually invisible if very slow, so rather let them go off
3060 goto killparticle;
3061 VectorClear(p->vel);
3062 }
3063 }
3064
3065 if (p->typeindex != pt_static)
3066 {
3067 switch (p->typeindex)
3068 {
3069 case pt_entityparticle:
3070 // particle that removes itself after one rendered frame
3071 if (p->time2)
3072 goto killparticle;
3073 else
3074 p->time2 = 1;
3075 break;
3076 case pt_blood:
3079 goto killparticle;
3080 break;
3081 case pt_bubble:
3084 goto killparticle;
3085 break;
3086 case pt_rain:
3089 goto killparticle;
3090 break;
3091 case pt_snow:
3092 if (cl.time > p->time2)
3093 {
3094 // snow flutter
3095 p->time2 = cl.time + (rand() & 3) * 0.1;
3096 p->vel[0] = p->vel[0] * 0.9f + lhrandom(-32, 32);
3097 p->vel[1] = p->vel[0] * 0.9f + lhrandom(-32, 32);
3098 }
3101 goto killparticle;
3102 break;
3103 case pt_explode:
3104 // Progress the particle colour up the ramp
3105 p->time2 += pt_explode_frame_interval;
3106 if (p->time2 >= 8)
3107 p->die = -1;
3108 else
3109 {
3111 p->color[0] = color >> 16;
3112 p->color[1] = color >> 8;
3113 p->color[2] = color >> 0;
3114 }
3115 break;
3116 case pt_explode2:
3117 // Progress the particle colour up the ramp
3118 p->time2 += pt_explode2_frame_interval;
3119 if (p->time2 >= 8)
3120 p->die = -1;
3121 else
3122 {
3124 p->color[0] = color >> 16;
3125 p->color[1] = color >> 8;
3126 p->color[2] = color >> 0;
3127 }
3128 break;
3129 default:
3130 break;
3131 }
3132 }
3133 }
3134 else if (p->delayedspawn > cl.time)
3135 continue;
3136 if (!drawparticles)
3137 continue;
3138 // don't render particles too close to the view (they chew fillrate)
3139 // also don't render particles behind the view (useless)
3140 // further checks to cull to the frustum would be too slow here
3141 switch(p->typeindex)
3142 {
3143 case pt_beam:
3144 // beams have no culling
3146 break;
3147 default:
3151 {
3153 if(leaf)
3155 continue;
3156 }
3157 // anything else just has to be in front of the viewer and visible at this distance
3158 if (!r_refdef.view.useperspective || (DotProduct(p->org, r_refdef.view.forward) >= minparticledist_start && VectorDistance2(p->org, r_refdef.view.origin) < drawdist2 * (p->size * p->size)))
3160 break;
3161 }
3162
3163 continue;
3164killparticle:
3165 p->typeindex = 0;
3166 if (cl.free_particle > i)
3167 cl.free_particle = i;
3168 }
3169
3170 // reduce cl.num_particles if possible
3171 while (cl.num_particles > 0 && cl.particles[cl.num_particles - 1].typeindex == 0)
3172 cl.num_particles--;
3173
3175 {
3176 particle_t *oldparticles = cl.particles;
3179 memcpy(cl.particles, oldparticles, cl.num_particles * sizeof(particle_t));
3180 Mem_Free(oldparticles);
3181 }
3182}
#define SUPERCONTENTS_SKY
Definition bspfile.h:200
#define SUPERCONTENTS_BODY
Definition bspfile.h:201
#define SUPERCONTENTS_LAVA
Definition bspfile.h:199
#define SUPERCONTENTS_SLIME
Definition bspfile.h:198
#define SUPERCONTENTS_SOLID
Definition bspfile.h:196
#define SUPERCONTENTS_LIQUIDSMASK
Definition bspfile.h:218
#define SUPERCONTENTS_NODROP
Definition bspfile.h:203
#define SUPERCONTENTS_WATER
Definition bspfile.h:197
trace_t CL_TraceLine(const vec3_t start, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float extend, qbool hitnetworkbrushmodels, qbool hitnetworkplayers, int *hitnetworkentity, qbool hitcsqcentities, qbool hitsurfaces)
#define CL_PointSuperContents(point)
void CL_AllocLightFlash(entity_render_t *ent, matrix4x4_t *matrix, float radius, float red, float green, float blue, float decay, float lifetime, char *cubemapname, int style, int shadowenable, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags)
Definition cl_main.c:852
client_state_t cl
Definition cl_main.c:117
client_static_t cls
Definition cl_main.c:116
static int particlepalette[256]
static void R_InitBloodTextures(unsigned char *particletexturedata)
particleeffectinfo_t particleeffectinfo[MAX_PARTICLEEFFECTINFO]
#define PARTICLEEFFECT_UNDERWATER
cvar_t cl_particles_visculling
float particle_color4f[MESHQUEUE_TRANSPARENT_BATCHSIZE *16]
void CL_ReadPointFile_f(cmd_state_t *cmd)
#define readints(array, n)
const char * CL_ParticleEffectNameForIndex(int i)
cvar_t cl_particles_blood_bloodhack
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, qbool spawndlight, qbool spawnparticles, float tintmins[4], float tintmaxs[4], float fade)
#define checkparms(n)
float particle_vertex3f[MESHQUEUE_TRANSPARENT_BATCHSIZE *12]
static void r_part_start(void)
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)
#define readint(var)
cvar_t cl_particles_blood_decal_alpha
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, qbool 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, float tint[4])
Creates a new particle and returns a pointer to it.
static unsigned char shadebubble(float dx, float dy, vec3_t light)
cvar_t cl_particles_explosions_sparks
static char * LightCubemapNumToName(char *vabuf, size_t vasize, int lightcubemapnum, int flags)
static cvar_t r_drawparticles_nearclip_min
particleeffectinfo_t baselineparticleeffectinfo
static cvar_t r_drawparticles_drawdistance
void R_DrawParticles(void)
cvar_t r_drawdecals
static void CL_ImmediateBloodStain(particle_t *part)
int ramp1[8]
cvar_t cl_decals
skinframe_t * decalskinframe
void CL_Particles_Init(void)
cvar_t cl_particles_blood_alpha
static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount)
cvar_t cl_particles_sparks
int particlefontrows
int particlefontcellheight
static void r_part_newmap(void)
void CL_ParticleRain(const vec3_t mins, const vec3_t maxs, const vec3_t dir, int count, int colorbase, int type)
cvar_t cl_decals_newsystem_bloodsmears
static void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
particletype_t particletype[pt_total]
static rtexture_t * particlefonttexture
#define PARTICLETEXTURESIZE
static void particletextureinvert(unsigned char *data)
int CL_ParticleEffectIndexForName(const char *name)
cvar_t cl_particles
cvar_t cl_particles_snow
static const int tex_bubble
#define readfloats(array, n)
static void CL_Particle_PixelCoordsForTexnum(int texnum, int *basex, int *basey, int *width, int *height)
static cvar_t r_drawdecals_drawdistance
int particlefontheight
void CL_ParticleExplosion2(const vec3_t org, int colorStart, int colorLength)
#define PARTICLEEFFECT_DEFINED
#define PARTICLEEFFECT_NOTUNDERWATER
cvar_t r_drawparticles
cvar_t cl_particles_smoke_alpha
void CL_Particles_Shutdown(void)
cvar_t cl_particles_explosions_shell
cvar_t cl_decals_newsystem_immediatebloodstain
static const int tex_bulletdecal[8]
cvar_t cl_particles_quake
#define readbool(var)
cvar_t cl_particles_forcetraileffects
cvar_t cl_particles_blood
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)
#define PARTICLEFONTSIZE
void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
particle_t * CL_NewQuakeParticle(const vec3_t origin, const unsigned short ptypeindex, const int color_1, const int color_2, const float gravity, const float offset_x, const float offset_y, const float offset_z, const float velocity_offset_x, const float velocity_offset_y, const float velocity_offset_z, const float air_friction, const float liquid_friction, const float origin_jitter, const float velocity_jitter, const float lifetime)
Creates a simple particle, a square like Quake, or a disc like GLQuake.
void CL_ParticleExplosion(const vec3_t org)
cvar_t cl_particles_rain
static void R_InitParticleTexture(void)
cvar_t cl_particles_smoke_alphafade
char particleeffectname[MAX_PARTICLEEFFECTNAME][64]
static const int tex_blooddecal[8]
cvar_t cl_particles_alpha
int ramp3[8]
void CL_ParticleBox(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, qbool spawndlight, qbool spawnparticles, float tintmins[4], float tintmaxs[4], float fade)
int particlefontcols
cvar_t cl_particles_blood_decal_scalemax
cvar_t cl_particles_collisions
static void r_part_shutdown(void)
cvar_t cl_particles_blood_decal_scalemin
int particlefontcellwidth
static void CL_Particles_LoadEffectInfo(const char *customfile)
static const int tex_bloodparticle[8]
static const char * standardeffectnames[EFFECT_TOTAL]
static const int tex_rainsplash
int ramp2[8]
void CL_ParseParticleEffect(void)
static rtexturepool_t * particletexturepool
void CL_EntityParticles(const entity_t *ent)
cvar_t cl_particles_size
float particle_texcoord2f[MESHQUEUE_TRANSPARENT_BATCHSIZE *8]
cvar_t cl_decals_models
static 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, qbool spawndlight, qbool spawnparticles, qbool wanttrail)
#define readfloat(var)
static const int tex_particle
static const int tex_smoke[8]
cvar_t cl_decals_bias
cvar_t cl_decals_fadetime
static const int tex_beam
static cvar_t r_drawparticles_nearclip_max
static void CL_Particles_ParseEffectInfo(const char *textstart, const char *textend, const char *filename)
static const int tex_raindrop
cvar_t cl_particles_bulletimpacts
void R_Particles_Init(void)
#define PARTICLEEFFECT_FORCENEAREST
static const int tex_square
static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount)
int particlefontwidth
cvar_t cl_decals_time
static particletexture_t particletexture[MAX_PARTICLETEXTURES]
static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
int numparticleeffectinfo
cvar_t cl_particles_smoke
static void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
unsigned short particle_elements[MESHQUEUE_TRANSPARENT_BATCHSIZE *6]
cvar_t cl_particles_quality
static void CL_NewParticlesFromEffectinfo(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, qbool spawndlight, qbool spawnparticles, float tintmins[4], float tintmaxs[4], float fade, qbool wanttrail)
void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
static void CL_Particles_LoadEffectInfo_f(cmd_state_t *cmd)
cvar_t cl_decals_newsystem_intensitymultiplier
cvar_t cl_decals_max
cvar_t cl_particles_bubbles
@ EFFECT_TE_WIZSPIKE
@ EFFECT_TR_GLOWTRAIL
@ EFFECT_TE_TEI_SMOKE
@ EFFECT_EF_STARDUST
@ EFFECT_TE_FLAMEJET
@ EFFECT_TR_SLIGHTBLOOD
@ EFFECT_TE_SUPERSPIKEQUAD
@ EFFECT_TE_SPIKEQUAD
@ EFFECT_TE_SPARK
@ EFFECT_TR_WIZSPIKE
@ EFFECT_TE_GUNSHOTQUAD
@ EFFECT_TE_EXPLOSIONQUAD
@ EFFECT_TE_EXPLOSION
@ EFFECT_TR_NEHAHRASMOKE
@ EFFECT_TR_ROCKET
@ EFFECT_SVC_PARTICLE
@ EFFECT_TE_GUNSHOT
@ EFFECT_TR_VORESPIKE
@ EFFECT_TR_GRENADE
@ EFFECT_TE_PLASMABURN
@ EFFECT_TE_TAREXPLOSION
@ EFFECT_TR_BLOOD
@ EFFECT_EF_FLAME
@ EFFECT_TE_LAVASPLASH
@ EFFECT_TE_TELEPORT
@ EFFECT_TE_SMALLFLASH
@ EFFECT_TE_KNIGHTSPIKE
@ EFFECT_TE_BLOOD
@ EFFECT_TE_TEI_PLASMAHIT
@ EFFECT_TR_NEXUIZPLASMA
@ EFFECT_TE_SUPERSPIKE
@ EFFECT_TE_TEI_G3
@ EFFECT_TR_KNIGHTSPIKE
@ EFFECT_TOTAL
@ EFFECT_TE_TEI_BIGEXPLOSION
@ EFFECT_TE_SPIKE
pblend_t
@ PBLEND_ALPHA
@ PBLEND_ADD
@ PBLEND_INVMOD
@ PBLEND_INVALID
ptype_t
@ pt_snow
@ pt_blood
@ pt_total
@ pt_spark
@ pt_explode2
@ pt_bubble
@ pt_static
@ pt_beam
@ pt_alphastatic
@ pt_raindecal
@ pt_entityparticle
@ pt_rain
@ pt_explode
@ pt_smoke
@ pt_decal
porientation_t
@ PARTICLE_SPARK
@ PARTICLE_HBEAM
@ PARTICLE_BILLBOARD
@ PARTICLE_VBEAM
@ PARTICLE_ORIENTED_DOUBLESIDED
@ PARTICLE_INVALID
#define LIGHTFLAG_NORMALMODE
Definition client.h:34
#define LIGHTFLAG_REALTIMEMODE
Definition client.h:35
void Cmd_AddCommand(unsigned flags, const char *cmd_name, xcommand_t function, const char *description)
called by the init functions of other parts of the program to register commands and functions to call...
Definition cmd.c:1661
static int Cmd_Argc(cmd_state_t *cmd)
Definition cmd.h:249
static const char * Cmd_Argv(cmd_state_t *cmd, int arg)
Cmd_Argv(cmd, ) will return an empty string (not a NULL) if arg > argc, so string operations are alwa...
Definition cmd.h:254
#define CF_CLIENT
cvar/command that only the client can change/execute
Definition cmd.h:48
#define CF_ARCHIVE
cvar should have its set value saved to config.cfg and persist across sessions
Definition cmd.h:53
cvar_t collision_extendmovelength
Definition collision.c:14
gamemode_t gamemode
Definition com_game.c:26
@ GAME_GOODVSBAD2
Definition com_game.h:36
@ GAME_PRYDON
Definition com_game.h:44
void MSG_ReadVector(sizebuf_t *sb, vec3_t v, protocolversion_t protocol)
Definition com_msg.c:401
char com_token[MAX_INPUTLINE]
Definition common.c:39
char * va(char *buf, size_t buflen, const char *format,...)
Definition common.c:972
qbool COM_ParseToken_Simple(const char **datapointer, qbool returnnewline, qbool parsebackslash, qbool parsecomments)
Definition common.c:463
int dpsnprintf(char *buffer, size_t buffersize, const char *format,...)
Returns the number of printed characters, excluding the final '\0' or returns -1 if the buffer isn't ...
Definition common.c:997
#define MSG_ReadChar(sb)
Definition common.h:187
#define dp_strlcpy(dst, src, dsize)
Definition common.h:303
#define MSG_ReadByte(sb)
Definition common.h:188
void Con_DPrintf(const char *fmt,...)
A Con_Printf that only shows up if the "developer" cvar is set.
Definition console.c:1544
void Con_Printf(const char *fmt,...)
Prints to all appropriate console targets.
Definition console.c:1514
vector size
float flags
float particle_size
float frametime
vector mins
vector velocity
float time
vector maxs
vector angles
vector origin
void Cvar_RegisterVariable(cvar_t *variable)
registers a cvar that already has the name, string, and optionally the archive elements set.
Definition cvar.c:599
vector color
float alpha
void fractalnoise(unsigned char *noise, int size, int startgrid)
Definition fractalnoise.c:4
unsigned char * FS_LoadFile(const char *path, mempool_t *pool, qbool quiet, fs_offset_t *filesizepointer)
Definition fs.c:3540
static int(ZEXPORT *qz_inflate)(z_stream *strm
int64_t fs_offset_t
Definition fs.h:37
void GL_CullFace(int state)
void GL_DepthMask(int state)
void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const r_meshbuffer_t *element3i_indexbuffer, int element3i_bufferoffset, const unsigned short *element3s, const r_meshbuffer_t *element3s_indexbuffer, int element3s_bufferoffset)
void GL_DepthTest(int state)
void GL_DepthRange(float nearfrac, float farfrac)
void GL_PolygonOffset(float planeoffset, float depthoffset)
void GL_BlendFunc(int blendfunc1, int blendfunc2)
void R_Mesh_PrepareVertices_Generic_Arrays(int numvertices, const float *vertex3f, const float *color4f, const float *texcoord2f)
float RSurf_FogVertex(const float *v)
Definition gl_rmain.c:7324
void R_SetupShader_Generic(rtexture_t *t, qbool usegamma, qbool notrippy, qbool suppresstexalpha)
Definition gl_rmain.c:1461
skinframe_t * R_SkinFrame_LoadExternal(const char *name, int textureflags, qbool complain, qbool fallbacknotexture)
Definition gl_rmain.c:2314
skinframe_t * R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, const unsigned char *skindata, int width, int height, int comparewidth, int compareheight, int comparecrc, qbool sRGB)
Definition gl_rmain.c:2546
cvar_t r_showparticleedges
Definition gl_rmain.c:94
void R_DebugLine(vec3_t start, vec3_t end)
Definition gl_rmain.c:10107
void R_CalcBeam_Vertex3f(float *vert, const float *org1, const float *org2, float width)
Definition gl_rmain.c:6253
void RSurf_ActiveModelEntity(const entity_render_t *ent, qbool wantnormals, qbool wanttangents, qbool prepass)
Definition gl_rmain.c:6921
void R_SkinFrame_MarkUsed(skinframe_t *skinframe)
Definition gl_rmain.c:2176
void R_DecalSystem_SplatEntities(const vec3_t worldorigin, const vec3_t worldnormal, float r, float g, float b, float a, float s1, float t1, float s2, float t2, float worldsize)
Definition gl_rmain.c:9506
r_refdef_t r_refdef
Definition gl_rmain.c:57
void R_Stain(const vec3_t origin, float radius, int cr1, int cg1, int cb1, int ca1, int cr2, int cg2, int cb2, int ca2)
Definition gl_rsurf.c:298
void R_FreeTexturePool(rtexturepool_t **rtexturepool)
rtexturepool_t * R_AllocTexturePool(void)
rtexture_t * R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, int miplevel, const unsigned int *palette)
GLenum GLenum GLuint texture
Definition glquake.h:613
#define GL_NONE
Definition glquake.h:127
GLenum GLsizei width
Definition glquake.h:622
GLenum GLsizei GLsizei height
Definition glquake.h:622
GLfloat GLfloat GLfloat v2
Definition glquake.h:747
GLubyte GLubyte GLubyte GLubyte w
Definition glquake.h:782
const GLdouble * v
Definition glquake.h:762
GLenum GLenum GLsizei count
Definition glquake.h:656
GLint GLenum GLint GLint y
Definition glquake.h:651
#define GL_ZERO
Definition glquake.h:73
GLint GLenum GLint x
Definition glquake.h:651
GLsizeiptr const GLvoid * data
Definition glquake.h:639
#define GL_ONE_MINUS_SRC_ALPHA
Definition glquake.h:80
#define GL_ONE
Definition glquake.h:74
GLclampf GLclampf blue
Definition glquake.h:642
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition glquake.h:657
const GLchar * name
Definition glquake.h:601
#define GL_ONE_MINUS_SRC_COLOR
Definition glquake.h:76
GLclampf green
Definition glquake.h:642
GLenum type
Definition glquake.h:656
rtexture_t * loadtextureimage(rtexturepool_t *pool, const char *filename, qbool complain, int flags, qbool allowFixtrans, qbool sRGB)
Definition image.c:1245
int image_height
Definition image.c:10
qbool Image_WriteTGABGRA(const char *filename, int width, int height, const unsigned char *data)
Definition image.c:1436
int image_width
Definition image.c:9
#define Image_LinearFloatFromsRGB(c)
Definition image.h:69
void AnglesFromVectors(vec3_t angles, const vec3_t forward, const vec3_t up, qbool flippitch)
LadyHavoc: calculates pitch/yaw/roll angles from forward and up vectors.
Definition mathlib.c:650
float m_bytenormals[NUMVERTEXNORMALS][3]
Definition mathlib.c:31
float VectorNormalizeLength(vec3_t v)
returns vector length
Definition mathlib.c:763
void AngleVectors(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
Definition mathlib.c:444
void VectorVectors(const vec3_t forward, vec3_t right, vec3_t up)
LadyHavoc: like AngleVectors, but taking a forward vector instead of angles, useful!
Definition mathlib.c:199
#define VectorLerp(v1, lerp, v2, out)
Definition mathlib.h:120
#define max(A, B)
Definition mathlib.h:38
#define VectorRandom(v)
Definition mathlib.h:119
#define min(A, B)
Definition mathlib.h:37
#define VectorNormalize(v)
Definition mathlib.h:104
#define VectorClear(a)
Definition mathlib.h:97
#define bound(min, num, max)
Definition mathlib.h:34
#define VectorLength(a)
Definition mathlib.h:109
#define lhrandom(MIN, MAX)
LadyHavoc: this function never returns exactly MIN or exactly MAX, because of a QuakeC bug in id1 whe...
Definition mathlib.h:48
#define VectorMAMAM(scale1, b1, scale2, b2, scale3, b3, out)
Definition mathlib.h:117
#define Vector4Set(vec, r, g, b, a)
Definition mathlib.h:86
#define VectorLength2(a)
Definition mathlib.h:110
#define VectorSet(vec, x, y, z)
Definition mathlib.h:96
#define VectorNormalize2(v, dest)
Definition mathlib.h:105
#define Vector4Copy(in, out)
Definition mathlib.h:84
#define VectorDistance2(a, b)
Definition mathlib.h:107
#define VectorSubtract(a, b, out)
Definition mathlib.h:99
#define NUMVERTEXNORMALS
Definition mathlib.h:225
#define Vector4Lerp(v1, lerp, v2, out)
Definition mathlib.h:93
#define DotProduct(a, b)
Definition mathlib.h:98
#define VectorCopy(in, out)
Definition mathlib.h:101
#define VectorScale(in, scale, out)
Definition mathlib.h:111
#define VectorMAM(scale1, b1, scale2, b2, out)
Definition mathlib.h:116
#define M_PI
Definition mathlib.h:28
#define VectorMA(a, scale, b, out)
Definition mathlib.h:114
#define VectorMAMAMAM(scale1, b1, scale2, b2, scale3, b3, scale4, b4, out)
Definition mathlib.h:118
void Matrix4x4_CreateTranslate(matrix4x4_t *out, double x, double y, double z)
Definition matrixlib.c:584
void Matrix4x4_CreateFromQuakeEntity(matrix4x4_t *out, double x, double y, double z, double pitch, double yaw, double roll, double scale)
Definition matrixlib.c:715
void Matrix4x4_Scale(matrix4x4_t *out, double rotatescale, double originscale)
Definition matrixlib.c:1837
void Matrix4x4_OriginFromMatrix(const matrix4x4_t *in, float *out)
Definition matrixlib.c:1792
float cos(float f)
float sqrt(float f)
void cmd(string command,...)
float sin(float f)
float fabs(float f)
float floor(float f)
string argv(float n)
void R_MeshQueue_AddTransparent(dptransparentsortcategory_t category, const vec3_t center, void(*callback)(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist), const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
Definition meshqueue.c:33
#define MESHQUEUE_TRANSPARENT_BATCHSIZE
Definition meshqueue.h:6
#define CHECKPVSBIT(pvs, b)
#define Q3SURFACEFLAG_NOMARKS
#define Q3SURFACEFLAG_NOIMPACT
sizebuf_t cl_message
Definition netconn.c:71
unsigned char palette_rgb[256][3]
Definition palette.c:7
string noise1
Definition progsdefs.qc:209
string noise3
Definition progsdefs.qc:209
string noise2
Definition progsdefs.qc:209
int i
#define MAX_PARTICLES
upper limit on cl.particles size
Definition qdefs.h:154
#define MAX_DLIGHTS
max number of dynamic lights (rocket flashes, etc) in scene at once
Definition qdefs.h:132
#define MAX_PARTICLETEXTURES
maximum number of unique particle textures in the particle font
Definition qdefs.h:137
#define MAX_PARTICLEEFFECTINFO
maximum number of unique particle effects (each name may associate with several of these)
Definition qdefs.h:136
#define MAX_QPATH
max length of a quake game pathname
Definition qdefs.h:169
#define MAX_PARTICLEEFFECTNAME
maximum number of unique names of particle effects (for particleeffectnum)
Definition qdefs.h:135
#define NULL
Definition qtypes.h:12
float vec_t
Definition qtypes.h:68
vec_t vec3_t[3]
Definition qtypes.h:71
bool qbool
Definition qtypes.h:9
vec_t vec4_t[4]
Definition qtypes.h:72
void R_NewExplosion(const vec3_t org)
void R_RegisterModule(const char *name, void(*start)(void), void(*shutdown)(void), void(*newmap)(void), void(*devicelost)(void), void(*devicerestored)(void))
Definition r_modules.c:25
@ TRANSPARENTSORT_DISTANCE
Definition r_qshader.h:194
void R_RTLight_Update(rtlight_t *rtlight, int isstatic, matrix4x4_t *matrix, vec3_t color, int style, const char *cubemapname, int shadow, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags)
Definition r_shadow.c:2980
void R_CompleteLightPoint(float *ambient, float *diffuse, float *lightdir, const vec3_t p, const int flags, float lightmapintensity, float ambientintensity)
Definition r_shadow.c:6014
#define LP_DYNLIGHT
Definition r_shadow.h:194
#define LP_LIGHTMAP
Definition r_shadow.h:192
#define LP_RTWORLD
Definition r_shadow.h:193
@ r_stat_particles
Definition r_stats.h:21
#define TEXF_RGBMULTIPLYBYALPHA
Definition r_textures.h:13
#define TEXF_ALPHA
Definition r_textures.h:9
#define TEXF_FORCELINEAR
Definition r_textures.h:19
@ TEXTYPE_BGRA
Definition r_textures.h:53
dp_FragColor r
vec2 dir
dp_FragColor g
float f
vec3 normal
dp_FragColor b
ret a
vec2 px
static struct @33 underwater
double oldtime
Definition client.h:868
particle_t * particles
Definition client.h:1000
float movevars_gravity
Definition client.h:1059
char worldbasename[MAX_QPATH]
Definition client.h:898
entity_t * entities
Definition client.h:991
struct model_s * worldmodel
Definition client.h:934
int free_particle
Definition client.h:1015
double time
Definition client.h:868
int num_particles
Definition client.h:1009
double particles_updatetime
Definition client.h:1013
int max_particles
Definition client.h:988
char worldnamenoextension[MAX_QPATH]
Definition client.h:900
mempool_t * levelmempool
Definition client.h:571
protocolversion_t protocol
Definition client.h:617
command interpreter state - the tokenizing and execution of commands, as well as pointers to which cv...
Definition cmd.h:127
Definition cvar.h:66
float value
Definition cvar.h:74
int integer
Definition cvar.h:73
matrix4x4_t matrix
Definition client.h:332
entity_persistent_t persistent
Definition client.h:474
entity_render_t render
Definition client.h:477
int clusterindex
mleaf_t *(* PointInLeaf)(struct model_s *model, const vec3_t p)
model_brush_t brush
unsigned char staincolor[3]
unsigned char typeindex
float alphafade
how much alpha reduces per second
unsigned char texnum
float stretch
only for sparks
float die
time when this particle should be removed, regardless of alpha
float bounce
how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide,...
float airfriction
how much air friction affects this object (objects with a low mass/size ratio tend to get more air fr...
unsigned char orientation
float gravity
how much gravity affects this particle (1.0 = normal gravity, 0.0 = none)
float stainsize
vec3_t vel
velocity of particle, or orientation of decal, or end point of beam
vec3_t sortorigin
sort by this group origin, not particle org
float alpha
0-255
unsigned char qualityreduction
enables skipping of this particle according to r_refdef.view.qualityreduction
float sizeincrease
rate of size change per second
signed char staintexnum
float liquidfriction
how much liquid friction affects this object (objects with a low mass/size ratio tend to get more liq...
unsigned char blendmode
float delayedspawn
time that particle appears and begins moving
unsigned char color[3]
float stainalpha
float time2
used for snow fluttering, decal fade, explosion colour ramp
short spin
geometry rotation speed around the particle center normal
short angle
base rotation of particle
porientation_t orientation
float relativeoriginoffset[3]
unsigned int staincolor[2]
float relativevelocityoffset[3]
unsigned int color[2]
rtexture_t * texture
porientation_t orientation
pblend_t blendmode
float ambientintensity
Definition render.h:384
float lightmapintensity
Definition render.h:387
model_t * worldmodel
same as worldentity->model
Definition render.h:358
entity_render_t * worldentity
the world
Definition render.h:355
rtlight_t templights[MAX_DLIGHTS]
Definition render.h:373
double time
(client gameworld) time for rendering time based effects
Definition render.h:352
rtlight_t * lights[MAX_DLIGHTS]
Definition render.h:372
r_refdef_viewcache_t viewcache
Definition render.h:407
qbool fogenabled
Definition render.h:440
r_refdef_view_t view
Definition render.h:406
float fogcolor[3]
Definition render.h:424
int stats[r_stat_count]
Definition render.h:466
r_refdef_scene_t scene
Definition render.h:418
vec3_t left
Definition render.h:269
float colorscale
global RGB color multiplier for rendering
Definition render.h:302
float quality
render quality (0 to 1) - affects r_drawparticles_drawdistance and others
Definition render.h:321
vec3_t origin
Definition render.h:267
int useperspective
if turned off it renders an ortho view
Definition render.h:280
vec3_t forward
Definition render.h:268
unsigned char * world_pvsbits
Definition render.h:340
qbool world_novis
if true, the view is currently in a leaf without pvs data
Definition render.h:344
struct rtexture_s * base
Definition r_textures.h:129
double fraction
Definition collision.h:40
int hitq3surfaceflags
Definition collision.h:60
double endpos[3]
Definition collision.h:42
int startsupercontents
Definition collision.h:56
plane_t plane
Definition collision.h:44
int hitsupercontents
Definition collision.h:58
qbool sRGB3D
whether 3D rendering is sRGB corrected (based on sRGBcapable3D)
Definition vid.h:76
static vec3_t forward
Definition sv_user.c:305
static vec3_t right
Definition sv_user.c:305
static vec3_t up
Definition sv_user.c:305
void Sys_Error(const char *error,...) DP_FUNC_PRINTF(1) DP_FUNC_NORETURN
Causes the entire program to exit ASAP.
Definition sys_shared.c:724
vec3_t normal
Definition collision.h:13
viddef_t vid
global video state
Definition vid_shared.c:64
#define MOVE_NORMAL
Definition world.h:28
#define MOVE_NOMONSTERS
Definition world.h:29
mempool_t * tempmempool
Definition zone.c:794
#define Mem_Free(mem)
Definition zone.h:96
#define Mem_Alloc(pool, size)
Definition zone.h:92