2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "cl_collision.h"
27 // must match ptype_t values
28 particletype_t particletype[pt_total] =
30 {PBLEND_INVALID, PARTICLE_INVALID, false}, //pt_dead (should never happen)
31 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
32 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
33 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
34 {PBLEND_ADD, PARTICLE_HBEAM, false}, //pt_beam
35 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
36 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
37 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
38 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
39 {PBLEND_INVMOD, PARTICLE_BILLBOARD, false}, //pt_blood
40 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
41 {PBLEND_INVMOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
42 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
45 #define PARTICLEEFFECT_UNDERWATER 1
46 #define PARTICLEEFFECT_NOTUNDERWATER 2
48 typedef struct particleeffectinfo_s
50 int effectnameindex; // which effect this belongs to
51 // PARTICLEEFFECT_* bits
53 // blood effects may spawn very few particles, so proper fraction-overflow
54 // handling is very important, this variable keeps track of the fraction
55 double particleaccumulator;
56 // the math is: countabsolute + requestedcount * countmultiplier * quality
57 // absolute number of particles to spawn, often used for decals
58 // (unaffected by quality and requestedcount)
60 // multiplier for the number of particles CL_ParticleEffect was told to
61 // spawn, most effects do not really have a count and hence use 1, so
62 // this is often the actual count to spawn, not merely a multiplier
63 float countmultiplier;
64 // if > 0 this causes the particle to spawn in an evenly spaced line from
65 // originmins to originmaxs (causing them to describe a trail, not a box)
67 // type of particle to spawn (defines some aspects of behavior)
69 // blending mode used on this particle type
71 // orientation of this particle type (BILLBOARD, SPARK, BEAM, etc)
72 porientation_t orientation;
73 // range of colors to choose from in hex RRGGBB (like HTML color tags),
74 // randomly interpolated at spawn
75 unsigned int color[2];
76 // a random texture is chosen in this range (note the second value is one
77 // past the last choosable, so for example 8,16 chooses any from 8 up and
79 // if start and end of the range are the same, no randomization is done
81 // range of size values randomly chosen when spawning, plus size increase over time
83 // range of alpha values randomly chosen when spawning, plus alpha fade
85 // how long the particle should live (note it is also removed if alpha drops to 0)
87 // how much gravity affects this particle (negative makes it fly up!)
89 // how much bounce the particle has when it hits a surface
90 // if negative the particle is removed on impact
92 // if in air this friction is applied
93 // if negative the particle accelerates
95 // if in liquid (water/slime/lava) this friction is applied
96 // if negative the particle accelerates
98 // these offsets are added to the values given to particleeffect(), and
99 // then an ellipsoid-shaped jitter is added as defined by these
100 // (they are the 3 radii)
102 // stretch velocity factor (used for sparks)
103 float originoffset[3];
104 float velocityoffset[3];
105 float originjitter[3];
106 float velocityjitter[3];
107 float velocitymultiplier;
108 // an effect can also spawn a dlight
109 float lightradiusstart;
110 float lightradiusfade;
113 qboolean lightshadow;
115 unsigned int staincolor[2]; // note: 0x808080 = neutral (particle's own color), these are modding factors for the particle's original color!
118 particleeffectinfo_t;
120 char particleeffectname[MAX_PARTICLEEFFECTNAME][64];
123 particleeffectinfo_t particleeffectinfo[MAX_PARTICLEEFFECTINFO];
125 static int particlepalette[256];
127 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b, // 0-7
128 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb, // 8-15
129 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b, // 16-23
130 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23, // 24-31
131 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767, // 32-39
132 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb, // 40-47
133 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07, // 48-55
134 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f, // 56-63
135 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000, // 64-71
136 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000, // 72-79
137 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307, // 80-87
138 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723, // 88-95
139 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b, // 96-103
140 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b, // 104-111
141 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733, // 112-119
142 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397, // 120-127
143 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b, // 128-135
144 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707, // 136-143
145 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b, // 144-151
146 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707, // 152-159
147 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353, // 160-167
148 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07, // 168-175
149 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f, // 176-183
150 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07, // 184-191
151 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307, // 192-199
152 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700, // 200-207
153 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f, // 208-215
154 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f, // 216-223
155 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b, // 224-231
156 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b, // 232-239
157 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000, // 240-247
158 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53 // 248-255
161 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
162 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
163 int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
165 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
167 // particletexture_t is a rectangle in the particlefonttexture
168 typedef struct particletexture_s
171 float s1, t1, s2, t2;
175 static rtexturepool_t *particletexturepool;
176 static rtexture_t *particlefonttexture;
177 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
178 skinframe_t *decalskinframe;
180 // texture numbers in particle font
181 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
182 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
183 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
184 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
185 static const int tex_rainsplash = 32;
186 static const int tex_particle = 63;
187 static const int tex_bubble = 62;
188 static const int tex_raindrop = 61;
189 static const int tex_beam = 60;
191 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1", "enables particle effects"};
192 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1", "multiplies number of particles"};
193 cvar_t cl_particles_alpha = {CVAR_SAVE, "cl_particles_alpha", "1", "multiplies opacity of particles"};
194 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1", "multiplies particle size"};
195 cvar_t cl_particles_quake = {CVAR_SAVE, "cl_particles_quake", "0", "makes particle effects look mostly like the ones in Quake"};
196 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1", "enables blood effects"};
197 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "1", "opacity of blood"};
198 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1", "make certain quake particle() calls create blood effects instead"};
199 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1", "enables bulletimpact effects"};
200 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1", "enables sparks from explosions"};
201 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0", "enables polygonal shell from explosions"};
202 cvar_t cl_particles_rain = {CVAR_SAVE, "cl_particles_rain", "1", "enables rain effects"};
203 cvar_t cl_particles_snow = {CVAR_SAVE, "cl_particles_snow", "1", "enables snow effects"};
204 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1", "enables smoke (used by multiple effects)"};
205 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5", "smoke brightness"};
206 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55", "brightness fade per second"};
207 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1", "enables sparks (used by multiple effects)"};
208 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1", "enables bubbles (used by multiple effects)"};
209 cvar_t cl_particles_visculling = {CVAR_SAVE, "cl_particles_visculling", "0", "perform a costly check if each particle is visible before drawing"};
210 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "1", "enables decals (bullet holes, blood, etc)"};
211 cvar_t cl_decals_visculling = {CVAR_SAVE, "cl_decals_visculling", "1", "perform a very cheap check if each decal is visible before drawing"};
212 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "20", "how long before decals start to fade away"};
213 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "1", "how long decals take to fade away"};
214 cvar_t cl_decals_newsystem = {CVAR_SAVE, "cl_decals_newsystem", "0", "enables new advanced decal system"};
215 cvar_t cl_decals_newsystem_intensitymultiplier = {CVAR_SAVE, "cl_decals_newsystem_intensitymultiplier", "2", "boosts intensity of decals (because the distance fade can make them hard to see otherwise)"};
216 cvar_t cl_decals_models = {CVAR_SAVE, "cl_decals_models", "0", "enables decals on animated models (if newsystem is also 1)"};
217 cvar_t cl_decals_bias = {CVAR_SAVE, "cl_decals_bias", "0.125", "distance to bias decals from surface to prevent depth fighting"};
218 cvar_t cl_decals_max = {CVAR_SAVE, "cl_decals_max", "4096", "maximum number of decals allowed to exist in the world at once"};
221 void CL_Particles_ParseEffectInfo(const char *textstart, const char *textend, const char *filename)
227 particleeffectinfo_t *info = NULL;
228 const char *text = textstart;
230 effectinfoindex = -1;
231 for (linenumber = 1;;linenumber++)
234 for (arrayindex = 0;arrayindex < 16;arrayindex++)
235 argv[arrayindex][0] = 0;
238 if (!COM_ParseToken_Simple(&text, true, false))
240 if (!strcmp(com_token, "\n"))
244 strlcpy(argv[argc], com_token, sizeof(argv[argc]));
250 #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;}
251 #define readints(array, n) checkparms(n+1);for (arrayindex = 0;arrayindex < argc - 1;arrayindex++) array[arrayindex] = strtol(argv[1+arrayindex], NULL, 0)
252 #define readfloats(array, n) checkparms(n+1);for (arrayindex = 0;arrayindex < argc - 1;arrayindex++) array[arrayindex] = atof(argv[1+arrayindex])
253 #define readint(var) checkparms(2);var = strtol(argv[1], NULL, 0)
254 #define readfloat(var) checkparms(2);var = atof(argv[1])
255 #define readbool(var) checkparms(2);var = strtol(argv[1], NULL, 0) != 0
256 if (!strcmp(argv[0], "effect"))
261 if (effectinfoindex >= MAX_PARTICLEEFFECTINFO)
263 Con_Printf("%s:%i: too many effects!\n", filename, linenumber);
266 for (effectnameindex = 1;effectnameindex < MAX_PARTICLEEFFECTNAME;effectnameindex++)
268 if (particleeffectname[effectnameindex][0])
270 if (!strcmp(particleeffectname[effectnameindex], argv[1]))
275 strlcpy(particleeffectname[effectnameindex], argv[1], sizeof(particleeffectname[effectnameindex]));
279 // if we run out of names, abort
280 if (effectnameindex == MAX_PARTICLEEFFECTNAME)
282 Con_Printf("%s:%i: too many effects!\n", filename, linenumber);
285 info = particleeffectinfo + effectinfoindex;
286 info->effectnameindex = effectnameindex;
287 info->particletype = pt_alphastatic;
288 info->blendmode = particletype[info->particletype].blendmode;
289 info->orientation = particletype[info->particletype].orientation;
290 info->tex[0] = tex_particle;
291 info->tex[1] = tex_particle;
292 info->color[0] = 0xFFFFFF;
293 info->color[1] = 0xFFFFFF;
297 info->alpha[1] = 256;
298 info->alpha[2] = 256;
299 info->time[0] = 9999;
300 info->time[1] = 9999;
301 VectorSet(info->lightcolor, 1, 1, 1);
302 info->lightshadow = true;
303 info->lighttime = 9999;
304 info->stretchfactor = 1;
305 info->staincolor[0] = (unsigned int)-1;
306 info->staincolor[1] = (unsigned int)-1;
307 info->staintex[0] = -1;
308 info->staintex[1] = -1;
310 else if (info == NULL)
312 Con_Printf("%s:%i: command %s encountered before effect\n", filename, linenumber, argv[0]);
315 else if (!strcmp(argv[0], "countabsolute")) {readfloat(info->countabsolute);}
316 else if (!strcmp(argv[0], "count")) {readfloat(info->countmultiplier);}
317 else if (!strcmp(argv[0], "type"))
320 if (!strcmp(argv[1], "alphastatic")) info->particletype = pt_alphastatic;
321 else if (!strcmp(argv[1], "static")) info->particletype = pt_static;
322 else if (!strcmp(argv[1], "spark")) info->particletype = pt_spark;
323 else if (!strcmp(argv[1], "beam")) info->particletype = pt_beam;
324 else if (!strcmp(argv[1], "rain")) info->particletype = pt_rain;
325 else if (!strcmp(argv[1], "raindecal")) info->particletype = pt_raindecal;
326 else if (!strcmp(argv[1], "snow")) info->particletype = pt_snow;
327 else if (!strcmp(argv[1], "bubble")) info->particletype = pt_bubble;
328 else if (!strcmp(argv[1], "blood")) {info->particletype = pt_blood;info->gravity = 1;}
329 else if (!strcmp(argv[1], "smoke")) info->particletype = pt_smoke;
330 else if (!strcmp(argv[1], "decal")) info->particletype = pt_decal;
331 else if (!strcmp(argv[1], "entityparticle")) info->particletype = pt_entityparticle;
332 else Con_Printf("%s:%i: unrecognized particle type %s\n", filename, linenumber, argv[1]);
333 info->blendmode = particletype[info->particletype].blendmode;
334 info->orientation = particletype[info->particletype].orientation;
336 else if (!strcmp(argv[0], "blend"))
339 if (!strcmp(argv[1], "alpha")) info->blendmode = PBLEND_ALPHA;
340 else if (!strcmp(argv[1], "add")) info->blendmode = PBLEND_ADD;
341 else if (!strcmp(argv[1], "invmod")) info->blendmode = PBLEND_INVMOD;
342 else Con_Printf("%s:%i: unrecognized blendmode %s\n", filename, linenumber, argv[1]);
344 else if (!strcmp(argv[0], "orientation"))
347 if (!strcmp(argv[1], "billboard")) info->orientation = PARTICLE_BILLBOARD;
348 else if (!strcmp(argv[1], "spark")) info->orientation = PARTICLE_SPARK;
349 else if (!strcmp(argv[1], "oriented")) info->orientation = PARTICLE_ORIENTED_DOUBLESIDED;
350 else if (!strcmp(argv[1], "beam")) info->orientation = PARTICLE_HBEAM;
351 else Con_Printf("%s:%i: unrecognized orientation %s\n", filename, linenumber, argv[1]);
353 else if (!strcmp(argv[0], "color")) {readints(info->color, 2);}
354 else if (!strcmp(argv[0], "tex")) {readints(info->tex, 2);}
355 else if (!strcmp(argv[0], "size")) {readfloats(info->size, 2);}
356 else if (!strcmp(argv[0], "sizeincrease")) {readfloat(info->size[2]);}
357 else if (!strcmp(argv[0], "alpha")) {readfloats(info->alpha, 3);}
358 else if (!strcmp(argv[0], "time")) {readfloats(info->time, 2);}
359 else if (!strcmp(argv[0], "gravity")) {readfloat(info->gravity);}
360 else if (!strcmp(argv[0], "bounce")) {readfloat(info->bounce);}
361 else if (!strcmp(argv[0], "airfriction")) {readfloat(info->airfriction);}
362 else if (!strcmp(argv[0], "liquidfriction")) {readfloat(info->liquidfriction);}
363 else if (!strcmp(argv[0], "originoffset")) {readfloats(info->originoffset, 3);}
364 else if (!strcmp(argv[0], "velocityoffset")) {readfloats(info->velocityoffset, 3);}
365 else if (!strcmp(argv[0], "originjitter")) {readfloats(info->originjitter, 3);}
366 else if (!strcmp(argv[0], "velocityjitter")) {readfloats(info->velocityjitter, 3);}
367 else if (!strcmp(argv[0], "velocitymultiplier")) {readfloat(info->velocitymultiplier);}
368 else if (!strcmp(argv[0], "lightradius")) {readfloat(info->lightradiusstart);}
369 else if (!strcmp(argv[0], "lightradiusfade")) {readfloat(info->lightradiusfade);}
370 else if (!strcmp(argv[0], "lighttime")) {readfloat(info->lighttime);}
371 else if (!strcmp(argv[0], "lightcolor")) {readfloats(info->lightcolor, 3);}
372 else if (!strcmp(argv[0], "lightshadow")) {readbool(info->lightshadow);}
373 else if (!strcmp(argv[0], "lightcubemapnum")) {readint(info->lightcubemapnum);}
374 else if (!strcmp(argv[0], "underwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_UNDERWATER;}
375 else if (!strcmp(argv[0], "notunderwater")) {checkparms(1);info->flags |= PARTICLEEFFECT_NOTUNDERWATER;}
376 else if (!strcmp(argv[0], "trailspacing")) {readfloat(info->trailspacing);if (info->trailspacing > 0) info->countmultiplier = 1.0f / info->trailspacing;}
377 else if (!strcmp(argv[0], "stretchfactor")) {readfloat(info->stretchfactor);}
378 else if (!strcmp(argv[0], "staincolor")) {readints(info->staincolor, 2);}
379 else if (!strcmp(argv[0], "staintex")) {readints(info->staintex, 2);}
380 else if (!strcmp(argv[0], "stainless")) {info->staintex[0] = -2; info->staincolor[0] = (unsigned int)-1; info->staincolor[1] = (unsigned int)-1;}
382 Con_Printf("%s:%i: skipping unknown command %s\n", filename, linenumber, argv[0]);
391 int CL_ParticleEffectIndexForName(const char *name)
394 for (i = 1;i < MAX_PARTICLEEFFECTNAME && particleeffectname[i][0];i++)
395 if (!strcmp(particleeffectname[i], name))
400 const char *CL_ParticleEffectNameForIndex(int i)
402 if (i < 1 || i >= MAX_PARTICLEEFFECTNAME)
404 return particleeffectname[i];
407 // MUST match effectnameindex_t in client.h
408 static const char *standardeffectnames[EFFECT_TOTAL] =
432 "TE_TEI_BIGEXPLOSION",
448 void CL_Particles_LoadEffectInfo(void)
452 unsigned char *filedata;
453 fs_offset_t filesize;
454 char filename[MAX_QPATH];
455 memset(particleeffectinfo, 0, sizeof(particleeffectinfo));
456 memset(particleeffectname, 0, sizeof(particleeffectname));
457 for (i = 0;i < EFFECT_TOTAL;i++)
458 strlcpy(particleeffectname[i], standardeffectnames[i], sizeof(particleeffectname[i]));
459 for (filepass = 0;;filepass++)
462 dpsnprintf(filename, sizeof(filename), "effectinfo.txt");
463 else if (filepass == 1)
464 dpsnprintf(filename, sizeof(filename), "maps/%s_effectinfo.txt", cl.levelname);
467 filedata = FS_LoadFile(filename, tempmempool, true, &filesize);
470 CL_Particles_ParseEffectInfo((const char *)filedata, (const char *)filedata + filesize, filename);
480 void CL_ReadPointFile_f (void);
481 void CL_Particles_Init (void)
483 Cmd_AddCommand ("pointfile", CL_ReadPointFile_f, "display point file produced by qbsp when a leak was detected in the map (a line leading through the leak hole, to an entity inside the level)");
484 Cmd_AddCommand ("cl_particles_reloadeffects", CL_Particles_LoadEffectInfo, "reloads effectinfo.txt and maps/levelname_effectinfo.txt (where levelname is the current map)");
486 Cvar_RegisterVariable (&cl_particles);
487 Cvar_RegisterVariable (&cl_particles_quality);
488 Cvar_RegisterVariable (&cl_particles_alpha);
489 Cvar_RegisterVariable (&cl_particles_size);
490 Cvar_RegisterVariable (&cl_particles_quake);
491 Cvar_RegisterVariable (&cl_particles_blood);
492 Cvar_RegisterVariable (&cl_particles_blood_alpha);
493 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
494 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
495 Cvar_RegisterVariable (&cl_particles_explosions_shell);
496 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
497 Cvar_RegisterVariable (&cl_particles_rain);
498 Cvar_RegisterVariable (&cl_particles_snow);
499 Cvar_RegisterVariable (&cl_particles_smoke);
500 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
501 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
502 Cvar_RegisterVariable (&cl_particles_sparks);
503 Cvar_RegisterVariable (&cl_particles_bubbles);
504 Cvar_RegisterVariable (&cl_particles_visculling);
505 Cvar_RegisterVariable (&cl_decals);
506 Cvar_RegisterVariable (&cl_decals_visculling);
507 Cvar_RegisterVariable (&cl_decals_time);
508 Cvar_RegisterVariable (&cl_decals_fadetime);
509 Cvar_RegisterVariable (&cl_decals_newsystem);
510 Cvar_RegisterVariable (&cl_decals_newsystem_intensitymultiplier);
511 Cvar_RegisterVariable (&cl_decals_models);
512 Cvar_RegisterVariable (&cl_decals_bias);
513 Cvar_RegisterVariable (&cl_decals_max);
516 void CL_Particles_Shutdown (void)
520 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha);
521 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2);
523 // list of all 26 parameters:
524 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
525 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
526 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
527 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_*BEAM)
528 // palpha - opacity of particle as 0-255 (can be more than 255)
529 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
530 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
531 // pgravity - how much effect gravity has on the particle (0-1)
532 // pbounce - how much bounce the particle has when it hits a surface (0-1), -1 makes a blood splat when it hits a surface, 0 does not even check for collisions
533 // px,py,pz - starting origin of particle
534 // pvx,pvy,pvz - starting velocity of particle
535 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
536 // blendmode - one of the PBLEND_ values
537 // orientation - one of the PARTICLE_ values
538 // staincolor1, staincolor2: minimum and maximum ranges of stain color, randomly interpolated to decide particle color (-1 to use none)
539 // staintex: any of the tex_ values such as tex_smoke[rand()&7] or tex_particle (-1 to use none)
540 particle_t *CL_NewParticle(unsigned short ptypeindex, int pcolor1, int pcolor2, int ptex, float psize, float psizeincrease, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pairfriction, float pliquidfriction, float originjitter, float velocityjitter, qboolean pqualityreduction, float lifetime, float stretch, pblend_t blendmode, porientation_t orientation, int staincolor1, int staincolor2, int staintex)
545 if (!cl_particles.integer)
547 for (;cl.free_particle < cl.max_particles && cl.particles[cl.free_particle].typeindex;cl.free_particle++);
548 if (cl.free_particle >= cl.max_particles)
551 lifetime = palpha / min(1, palphafade);
552 part = &cl.particles[cl.free_particle++];
553 if (cl.num_particles < cl.free_particle)
554 cl.num_particles = cl.free_particle;
555 memset(part, 0, sizeof(*part));
556 part->typeindex = ptypeindex;
557 part->blendmode = blendmode;
558 if(orientation == PARTICLE_HBEAM || orientation == PARTICLE_VBEAM)
560 particletexture_t *tex = &particletexture[ptex];
561 if(tex->t1 == 0 && tex->t2 == 1) // full height of texture?
562 part->orientation = PARTICLE_VBEAM;
564 part->orientation = PARTICLE_HBEAM;
567 part->orientation = orientation;
568 l2 = (int)lhrandom(0.5, 256.5);
570 part->color[0] = ((((pcolor1 >> 16) & 0xFF) * l1 + ((pcolor2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
571 part->color[1] = ((((pcolor1 >> 8) & 0xFF) * l1 + ((pcolor2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
572 part->color[2] = ((((pcolor1 >> 0) & 0xFF) * l1 + ((pcolor2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
573 part->staintexnum = staintex;
574 if(staincolor1 >= 0 && staincolor2 >= 0)
576 l2 = (int)lhrandom(0.5, 256.5);
578 if(blendmode == PBLEND_INVMOD)
580 r = ((((staincolor1 >> 16) & 0xFF) * l1 + ((staincolor2 >> 16) & 0xFF) * l2) * (255 - part->color[0])) / 0x8000; // staincolor 0x808080 keeps color invariant
581 g = ((((staincolor1 >> 8) & 0xFF) * l1 + ((staincolor2 >> 8) & 0xFF) * l2) * (255 - part->color[1])) / 0x8000;
582 b = ((((staincolor1 >> 0) & 0xFF) * l1 + ((staincolor2 >> 0) & 0xFF) * l2) * (255 - part->color[2])) / 0x8000;
586 r = ((((staincolor1 >> 16) & 0xFF) * l1 + ((staincolor2 >> 16) & 0xFF) * l2) * part->color[0]) / 0x8000; // staincolor 0x808080 keeps color invariant
587 g = ((((staincolor1 >> 8) & 0xFF) * l1 + ((staincolor2 >> 8) & 0xFF) * l2) * part->color[1]) / 0x8000;
588 b = ((((staincolor1 >> 0) & 0xFF) * l1 + ((staincolor2 >> 0) & 0xFF) * l2) * part->color[2]) / 0x8000;
590 if(r > 0xFF) r = 0xFF;
591 if(g > 0xFF) g = 0xFF;
592 if(b > 0xFF) b = 0xFF;
596 r = part->color[0]; // -1 is shorthand for stain = particle color
600 part->staincolor = r * 65536 + g * 256 + b;
603 part->sizeincrease = psizeincrease;
604 part->alpha = palpha;
605 part->alphafade = palphafade;
606 part->gravity = pgravity;
607 part->bounce = pbounce;
608 part->stretch = stretch;
610 part->org[0] = px + originjitter * v[0];
611 part->org[1] = py + originjitter * v[1];
612 part->org[2] = pz + originjitter * v[2];
613 part->vel[0] = pvx + velocityjitter * v[0];
614 part->vel[1] = pvy + velocityjitter * v[1];
615 part->vel[2] = pvz + velocityjitter * v[2];
617 part->airfriction = pairfriction;
618 part->liquidfriction = pliquidfriction;
619 part->die = cl.time + lifetime;
620 part->delayedcollisions = 0;
621 part->qualityreduction = pqualityreduction;
622 // if it is rain or snow, trace ahead and shut off collisions until an actual collision event needs to occur to improve performance
623 if (part->typeindex == pt_rain)
627 float lifetime = part->die - cl.time;
630 // turn raindrop into simple spark and create delayedspawn splash effect
631 part->typeindex = pt_spark;
633 VectorMA(part->org, lifetime, part->vel, endvec);
634 trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK, true, false, NULL, false);
635 part->die = cl.time + lifetime * trace.fraction;
636 part2 = CL_NewParticle(pt_raindecal, pcolor1, pcolor2, tex_rainsplash, part->size, part->size * 20, part->alpha, part->alpha / 0.4, 0, 0, trace.endpos[0] + trace.plane.normal[0], trace.endpos[1] + trace.plane.normal[1], trace.endpos[2] + trace.plane.normal[2], trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2], 0, 0, 0, 0, pqualityreduction, 0, 1, PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, -1, -1, -1);
639 part2->delayedspawn = part->die;
640 part2->die += part->die - cl.time;
641 for (i = rand() & 7;i < 10;i++)
643 part2 = CL_NewParticle(pt_spark, pcolor1, pcolor2, tex_particle, 0.25f, 0, part->alpha * 2, part->alpha * 4, 1, 0, trace.endpos[0] + trace.plane.normal[0], trace.endpos[1] + trace.plane.normal[1], trace.endpos[2] + trace.plane.normal[2], trace.plane.normal[0] * 16, trace.plane.normal[1] * 16, trace.plane.normal[2] * 16 + cl.movevars_gravity * 0.04, 0, 0, 0, 32, pqualityreduction, 0, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
646 part2->delayedspawn = part->die;
647 part2->die += part->die - cl.time;
652 else if (part->bounce != 0 && part->gravity == 0 && part->typeindex != pt_snow)
654 float lifetime = part->alpha / (part->alphafade ? part->alphafade : 1);
657 VectorMA(part->org, lifetime, part->vel, endvec);
658 trace = CL_TraceLine(part->org, endvec, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY, true, false, NULL, false);
659 part->delayedcollisions = cl.time + lifetime * trace.fraction - 0.1;
665 static void CL_ImmediateBloodStain(particle_t *part)
670 // blood creates a splash at spawn, not just at impact, this makes monsters bloody where they are shot
671 if (part->staintexnum >= 0 && cl_decals_newsystem.integer && cl_decals.integer)
673 VectorCopy(part->vel, v);
675 staintex = part->staintexnum;
676 R_DecalSystem_SplatEntities(part->org, v, 1-((part->staincolor>>16)&255)*(1.0f/255.0f), 1-((part->staincolor>>8)&255)*(1.0f/255.0f), 1-((part->staincolor)&255)*(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);
679 // blood creates a splash at spawn, not just at impact, this makes monsters bloody where they are shot
680 if (part->typeindex == pt_blood && cl_decals_newsystem.integer && cl_decals.integer)
682 VectorCopy(part->vel, v);
684 staintex = tex_blooddecal[rand()&7];
685 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);
689 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
693 entity_render_t *ent = &cl.entities[hitent].render;
694 unsigned char color[3];
695 if (!cl_decals.integer)
697 if (!ent->allowdecals)
700 l2 = (int)lhrandom(0.5, 256.5);
702 color[0] = ((((color1 >> 16) & 0xFF) * l1 + ((color2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
703 color[1] = ((((color1 >> 8) & 0xFF) * l1 + ((color2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
704 color[2] = ((((color1 >> 0) & 0xFF) * l1 + ((color2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
706 if (cl_decals_newsystem.integer)
708 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);
712 for (;cl.free_decal < cl.max_decals && cl.decals[cl.free_decal].typeindex;cl.free_decal++);
713 if (cl.free_decal >= cl.max_decals)
715 decal = &cl.decals[cl.free_decal++];
716 if (cl.num_decals < cl.free_decal)
717 cl.num_decals = cl.free_decal;
718 memset(decal, 0, sizeof(*decal));
719 decal->decalsequence = cl.decalsequence++;
720 decal->typeindex = pt_decal;
721 decal->texnum = texnum;
722 VectorMA(org, cl_decals_bias.value, normal, decal->org);
723 VectorCopy(normal, decal->normal);
725 decal->alpha = alpha;
726 decal->time2 = cl.time;
727 decal->color[0] = color[0];
728 decal->color[1] = color[1];
729 decal->color[2] = color[2];
730 decal->owner = hitent;
731 decal->clusterindex = -1000; // no vis culling unless we're sure
734 // these relative things are only used to regenerate p->org and p->vel if decal->owner is not world (0)
735 decal->ownermodel = cl.entities[decal->owner].render.model;
736 Matrix4x4_Transform(&cl.entities[decal->owner].render.inversematrix, org, decal->relativeorigin);
737 Matrix4x4_Transform3x3(&cl.entities[decal->owner].render.inversematrix, normal, decal->relativenormal);
741 if(r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.PointInLeaf)
743 mleaf_t *leaf = r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, decal->org);
745 decal->clusterindex = leaf->clusterindex;
750 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
753 float bestfrac, bestorg[3], bestnormal[3];
755 int besthitent = 0, hitent;
758 for (i = 0;i < 32;i++)
761 VectorMA(org, maxdist, org2, org2);
762 trace = CL_TraceLine(org, org2, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_SKY, true, false, &hitent, false);
763 // take the closest trace result that doesn't end up hitting a NOMARKS
764 // surface (sky for example)
765 if (bestfrac > trace.fraction && !(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
767 bestfrac = trace.fraction;
769 VectorCopy(trace.endpos, bestorg);
770 VectorCopy(trace.plane.normal, bestnormal);
774 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
777 static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount);
778 static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount);
779 void CL_ParticleEffect_Fallback(int effectnameindex, float count, const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, entity_t *ent, int palettecolor, qboolean spawndlight, qboolean spawnparticles)
782 matrix4x4_t tempmatrix;
784 VectorLerp(originmins, 0.5, originmaxs, center);
785 Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
786 if (effectnameindex == EFFECT_SVC_PARTICLE)
788 if (cl_particles.integer)
790 // bloodhack checks if this effect's color matches regular or lightning blood and if so spawns a blood effect instead
792 CL_ParticleExplosion(center);
793 else if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer && (palettecolor == 73 || palettecolor == 225))
794 CL_ParticleEffect(EFFECT_TE_BLOOD, count / 2.0f, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
797 count *= cl_particles_quality.value;
798 for (;count > 0;count--)
800 int k = particlepalette[(palettecolor & ~7) + (rand()&7)];
801 CL_NewParticle(pt_alphastatic, k, k, tex_particle, 1.5, 0, 255, 0, 0.05, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 8, 0, true, lhrandom(0.1, 0.5), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
806 else if (effectnameindex == EFFECT_TE_WIZSPIKE)
807 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 30*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 20);
808 else if (effectnameindex == EFFECT_TE_KNIGHTSPIKE)
809 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 226);
810 else if (effectnameindex == EFFECT_TE_SPIKE)
812 if (cl_particles_bulletimpacts.integer)
814 if (cl_particles_quake.integer)
816 if (cl_particles_smoke.integer)
817 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
821 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
822 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
823 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
827 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
828 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
830 else if (effectnameindex == EFFECT_TE_SPIKEQUAD)
832 if (cl_particles_bulletimpacts.integer)
834 if (cl_particles_quake.integer)
836 if (cl_particles_smoke.integer)
837 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 10*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
841 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
842 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 15*count);
843 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
847 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
848 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
849 CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
851 else if (effectnameindex == EFFECT_TE_SUPERSPIKE)
853 if (cl_particles_bulletimpacts.integer)
855 if (cl_particles_quake.integer)
857 if (cl_particles_smoke.integer)
858 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
862 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
863 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
864 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
868 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
869 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
871 else if (effectnameindex == EFFECT_TE_SUPERSPIKEQUAD)
873 if (cl_particles_bulletimpacts.integer)
875 if (cl_particles_quake.integer)
877 if (cl_particles_smoke.integer)
878 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
882 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 8*count);
883 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 30*count);
884 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
888 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
889 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
890 CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
892 else if (effectnameindex == EFFECT_TE_BLOOD)
894 if (!cl_particles_blood.integer)
896 if (cl_particles_quake.integer)
897 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 2*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 73);
900 static double bloodaccumulator = 0;
901 qboolean immediatebloodstain = true;
902 //CL_NewParticle(pt_alphastatic, 0x4f0000,0x7f0000, tex_particle, 2.5, 0, 256, 256, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 1, 4, 0, 0, true, 0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD);
903 bloodaccumulator += count * 0.333 * cl_particles_quality.value;
904 for (;bloodaccumulator > 0;bloodaccumulator--)
906 part = CL_NewParticle(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);
907 if (immediatebloodstain && part)
909 immediatebloodstain = false;
910 CL_ImmediateBloodStain(part);
915 else if (effectnameindex == EFFECT_TE_SPARK)
916 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, count);
917 else if (effectnameindex == EFFECT_TE_PLASMABURN)
919 // plasma scorch mark
920 R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
921 CL_SpawnDecalParticleForPoint(center, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
922 CL_AllocLightFlash(NULL, &tempmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
924 else if (effectnameindex == EFFECT_TE_GUNSHOT)
926 if (cl_particles_bulletimpacts.integer)
928 if (cl_particles_quake.integer)
929 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
932 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
933 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
934 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
938 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
939 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
941 else if (effectnameindex == EFFECT_TE_GUNSHOTQUAD)
943 if (cl_particles_bulletimpacts.integer)
945 if (cl_particles_quake.integer)
946 CL_ParticleEffect(EFFECT_SVC_PARTICLE, 20*count, originmins, originmaxs, velocitymins, velocitymaxs, NULL, 0);
949 CL_Smoke(originmins, originmaxs, velocitymins, velocitymaxs, 4*count);
950 CL_Sparks(originmins, originmaxs, velocitymins, velocitymaxs, 20*count);
951 CL_NewParticle(pt_static, 0x808080,0x808080, tex_particle, 3, 0, 256, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
955 R_Stain(center, 16, 40, 40, 40, 64, 88, 88, 88, 64);
956 CL_SpawnDecalParticleForPoint(center, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
957 CL_AllocLightFlash(NULL, &tempmatrix, 100, 0.15f, 0.15f, 1.5f, 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
959 else if (effectnameindex == EFFECT_TE_EXPLOSION)
961 CL_ParticleExplosion(center);
962 CL_AllocLightFlash(NULL, &tempmatrix, 350, 4.0f, 2.0f, 0.50f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
964 else if (effectnameindex == EFFECT_TE_EXPLOSIONQUAD)
966 CL_ParticleExplosion(center);
967 CL_AllocLightFlash(NULL, &tempmatrix, 350, 2.5f, 2.0f, 4.0f, 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
969 else if (effectnameindex == EFFECT_TE_TAREXPLOSION)
971 if (cl_particles_quake.integer)
974 for (i = 0;i < 1024 * cl_particles_quality.value;i++)
977 CL_NewParticle(pt_alphastatic, particlepalette[66], particlepalette[71], tex_particle, 1.5f, 0, 255, 0, 0, 0, center[0], center[1], center[2], 0, 0, 0, -4, -4, 16, 256, true, (rand() & 1) ? 1.4 : 1.0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
979 CL_NewParticle(pt_alphastatic, particlepalette[150], particlepalette[155], tex_particle, 1.5f, 0, 255, 0, 0, 0, center[0], center[1], center[2], 0, 0, lhrandom(-256, 256), 0, 0, 16, 0, true, (rand() & 1) ? 1.4 : 1.0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
983 CL_ParticleExplosion(center);
984 CL_AllocLightFlash(NULL, &tempmatrix, 600, 1.6f, 0.8f, 2.0f, 1200, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
986 else if (effectnameindex == EFFECT_TE_SMALLFLASH)
987 CL_AllocLightFlash(NULL, &tempmatrix, 200, 2, 2, 2, 1000, 0.2, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
988 else if (effectnameindex == EFFECT_TE_FLAMEJET)
990 count *= cl_particles_quality.value;
992 CL_NewParticle(pt_smoke, 0x6f0f00, 0xe3974f, tex_particle, 4, 0, lhrandom(64, 128), 384, -1, 1.1, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 0, 128, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
994 else if (effectnameindex == EFFECT_TE_LAVASPLASH)
996 float i, j, inc, vel;
999 inc = 8 / cl_particles_quality.value;
1000 for (i = -128;i < 128;i += inc)
1002 for (j = -128;j < 128;j += inc)
1004 dir[0] = j + lhrandom(0, inc);
1005 dir[1] = i + lhrandom(0, inc);
1007 org[0] = center[0] + dir[0];
1008 org[1] = center[1] + dir[1];
1009 org[2] = center[2] + lhrandom(0, 64);
1010 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
1011 CL_NewParticle(pt_alphastatic, particlepalette[224], particlepalette[231], tex_particle, 1.5f, 0, 255, 0, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, true, lhrandom(2, 2.62), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1015 else if (effectnameindex == EFFECT_TE_TELEPORT)
1017 float i, j, k, inc, vel;
1020 if (cl_particles_quake.integer)
1021 inc = 4 / cl_particles_quality.value;
1023 inc = 8 / cl_particles_quality.value;
1024 for (i = -16;i < 16;i += inc)
1026 for (j = -16;j < 16;j += inc)
1028 for (k = -24;k < 32;k += inc)
1030 VectorSet(dir, i*8, j*8, k*8);
1031 VectorNormalize(dir);
1032 vel = lhrandom(50, 113);
1033 if (cl_particles_quake.integer)
1034 CL_NewParticle(pt_alphastatic, particlepalette[7], particlepalette[14], tex_particle, 1.5f, 0, 255, 0, 0, 0, center[0] + i + lhrandom(0, inc), center[1] + j + lhrandom(0, inc), center[2] + k + lhrandom(0, inc), dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, true, lhrandom(0.2, 0.34), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1036 CL_NewParticle(pt_alphastatic, particlepalette[7], particlepalette[14], tex_particle, 1.5f, 0, inc * lhrandom(37, 63), inc * 187, 0, 0, center[0] + i + lhrandom(0, inc), center[1] + j + lhrandom(0, inc), center[2] + k + lhrandom(0, inc), dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0, 0, true, 0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1040 if (!cl_particles_quake.integer)
1041 CL_NewParticle(pt_static, 0xffffff, 0xffffff, tex_particle, 30, 0, 256, 512, 0, 0, center[0], center[1], center[2], 0, 0, 0, 0, 0, 0, 0, false, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1042 CL_AllocLightFlash(NULL, &tempmatrix, 200, 2.0f, 2.0f, 2.0f, 400, 99.0f, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1044 else if (effectnameindex == EFFECT_TE_TEI_G3)
1045 CL_NewParticle(pt_beam, 0xFFFFFF, 0xFFFFFF, tex_beam, 8, 0, 256, 256, 0, 0, originmins[0], originmins[1], originmins[2], originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, 0, false, 0, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1);
1046 else if (effectnameindex == EFFECT_TE_TEI_SMOKE)
1048 if (cl_particles_smoke.integer)
1050 count *= 0.25f * cl_particles_quality.value;
1052 CL_NewParticle(pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 0, 255, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 1.5f, 6.0f, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1055 else if (effectnameindex == EFFECT_TE_TEI_BIGEXPLOSION)
1057 CL_ParticleExplosion(center);
1058 CL_AllocLightFlash(NULL, &tempmatrix, 500, 2.5f, 2.0f, 1.0f, 500, 9999, 0, -1, true, 1, 0.25, 0.5, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1060 else if (effectnameindex == EFFECT_TE_TEI_PLASMAHIT)
1063 R_Stain(center, 40, 40, 40, 40, 64, 88, 88, 88, 64);
1064 CL_SpawnDecalParticleForPoint(center, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1065 if (cl_particles_smoke.integer)
1066 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1067 CL_NewParticle(pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 0, 255, 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 20, 155, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1068 if (cl_particles_sparks.integer)
1069 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1070 CL_NewParticle(pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, 0, lhrandom(64, 255), 512, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 0, 465, true, 0, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
1071 CL_AllocLightFlash(NULL, &tempmatrix, 500, 0.6f, 1.2f, 2.0f, 2000, 9999, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1073 else if (effectnameindex == EFFECT_EF_FLAME)
1075 count *= 300 * cl_particles_quality.value;
1077 CL_NewParticle(pt_smoke, 0x6f0f00, 0xe3974f, tex_particle, 4, 0, lhrandom(64, 128), 384, -1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 1, 4, 16, 128, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1078 CL_AllocLightFlash(NULL, &tempmatrix, 200, 2.0f, 1.5f, 0.5f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1080 else if (effectnameindex == EFFECT_EF_STARDUST)
1082 count *= 200 * cl_particles_quality.value;
1084 CL_NewParticle(pt_static, 0x903010, 0xFFD030, tex_particle, 4, 0, lhrandom(64, 128), 128, 1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0.2, 0.8, 16, 128, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1085 CL_AllocLightFlash(NULL, &tempmatrix, 200, 1.0f, 0.7f, 0.3f, 0, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1087 else if (!strncmp(particleeffectname[effectnameindex], "TR_", 3))
1091 int smoke, blood, bubbles, r, color;
1093 if (spawndlight && r_refdef.scene.numlights < MAX_DLIGHTS)
1096 Vector4Set(light, 0, 0, 0, 0);
1098 if (effectnameindex == EFFECT_TR_ROCKET)
1099 Vector4Set(light, 3.0f, 1.5f, 0.5f, 200);
1100 else if (effectnameindex == EFFECT_TR_VORESPIKE)
1102 if (gamemode == GAME_PRYDON && !cl_particles_quake.integer)
1103 Vector4Set(light, 0.3f, 0.6f, 1.2f, 100);
1105 Vector4Set(light, 1.2f, 0.5f, 1.0f, 200);
1107 else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
1108 Vector4Set(light, 0.75f, 1.5f, 3.0f, 200);
1112 matrix4x4_t tempmatrix;
1113 Matrix4x4_CreateFromQuakeEntity(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, light[3]);
1114 R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &tempmatrix, light, -1, NULL, true, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1115 r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
1119 if (!spawnparticles)
1122 if (originmaxs[0] == originmins[0] && originmaxs[1] == originmins[1] && originmaxs[2] == originmins[2])
1125 VectorSubtract(originmaxs, originmins, dir);
1126 len = VectorNormalizeLength(dir);
1129 dec = -ent->persistent.trail_time;
1130 ent->persistent.trail_time += len;
1131 if (ent->persistent.trail_time < 0.01f)
1134 // if we skip out, leave it reset
1135 ent->persistent.trail_time = 0.0f;
1140 // advance into this frame to reach the first puff location
1141 VectorMA(originmins, dec, dir, pos);
1144 smoke = cl_particles.integer && cl_particles_smoke.integer;
1145 blood = cl_particles.integer && cl_particles_blood.integer;
1146 bubbles = cl_particles.integer && cl_particles_bubbles.integer && !cl_particles_quake.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
1147 qd = 1.0f / cl_particles_quality.value;
1154 if (effectnameindex == EFFECT_TR_BLOOD)
1156 if (cl_particles_quake.integer)
1158 color = particlepalette[67 + (rand()&3)];
1159 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 2, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1164 CL_NewParticle(pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 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);
1167 else if (effectnameindex == EFFECT_TR_SLIGHTBLOOD)
1169 if (cl_particles_quake.integer)
1172 color = particlepalette[67 + (rand()&3)];
1173 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 2, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1178 CL_NewParticle(pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, 0, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 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);
1184 if (effectnameindex == EFFECT_TR_ROCKET)
1186 if (cl_particles_quake.integer)
1189 color = particlepalette[ramp3[r]];
1190 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, -0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0.1372549*(6-r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1194 CL_NewParticle(pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*62, cl_particles_smoke_alphafade.value*62, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1195 CL_NewParticle(pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*288, cl_particles_smoke_alphafade.value*1400, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 20, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1198 else if (effectnameindex == EFFECT_TR_GRENADE)
1200 if (cl_particles_quake.integer)
1203 color = particlepalette[ramp3[r]];
1204 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, -0.05, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 3, 0, true, 0.1372549*(6-r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1208 CL_NewParticle(pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, 0, cl_particles_smoke_alpha.value*50, cl_particles_smoke_alphafade.value*75, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1211 else if (effectnameindex == EFFECT_TR_WIZSPIKE)
1213 if (cl_particles_quake.integer)
1216 color = particlepalette[52 + (rand()&7)];
1217 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1218 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1220 else if (gamemode == GAME_GOODVSBAD2)
1223 CL_NewParticle(pt_static, 0x00002E, 0x000030, tex_particle, 6, 0, 128, 384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1227 color = particlepalette[20 + (rand()&7)];
1228 CL_NewParticle(pt_static, color, color, tex_particle, 2, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1231 else if (effectnameindex == EFFECT_TR_KNIGHTSPIKE)
1233 if (cl_particles_quake.integer)
1236 color = particlepalette[230 + (rand()&7)];
1237 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*dir[1], 30*-dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1238 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 30*-dir[1], 30*dir[0], 0, 0, 0, 0, 0, true, 0.5, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1242 color = particlepalette[226 + (rand()&7)];
1243 CL_NewParticle(pt_static, color, color, tex_particle, 2, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1246 else if (effectnameindex == EFFECT_TR_VORESPIKE)
1248 if (cl_particles_quake.integer)
1250 color = particlepalette[152 + (rand()&3)];
1251 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 8, 0, true, 0.3, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1253 else if (gamemode == GAME_GOODVSBAD2)
1256 CL_NewParticle(pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, 0, 255, 384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1258 else if (gamemode == GAME_PRYDON)
1261 CL_NewParticle(pt_static, 0x103040, 0x204050, tex_particle, 6, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1264 CL_NewParticle(pt_static, 0x502030, 0x502030, tex_particle, 3, 0, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1266 else if (effectnameindex == EFFECT_TR_NEHAHRASMOKE)
1269 CL_NewParticle(pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, 0, 64, 320, 0, 0, pos[0], pos[1], pos[2], 0, 0, lhrandom(4, 12), 0, 0, 0, 4, false, 0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1271 else if (effectnameindex == EFFECT_TR_NEXUIZPLASMA)
1274 CL_NewParticle(pt_static, 0x283880, 0x283880, tex_particle, 4, 0, 255, 1024, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 16, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1276 else if (effectnameindex == EFFECT_TR_GLOWTRAIL)
1277 CL_NewParticle(pt_alphastatic, particlepalette[palettecolor], particlepalette[palettecolor], tex_particle, 5, 0, 128, 320, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1281 if (effectnameindex == EFFECT_TR_ROCKET)
1282 CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(128, 512), 512, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, 0.0625, 0.25, 0, 16, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1283 else if (effectnameindex == EFFECT_TR_GRENADE)
1284 CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(128, 512), 512, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, 0.0625, 0.25, 0, 16, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1286 // advance to next time and position
1289 VectorMA (pos, dec, dir, pos);
1292 ent->persistent.trail_time = len;
1295 Con_DPrintf("CL_ParticleEffect_Fallback: no fallback found for effect %s\n", particleeffectname[effectnameindex]);
1298 // this is also called on point effects with spawndlight = true and
1299 // spawnparticles = true
1300 // it is called CL_ParticleTrail because most code does not want to supply
1301 // these parameters, only trail handling does
1302 void CL_ParticleTrail(int effectnameindex, float pcount, const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, entity_t *ent, int palettecolor, qboolean spawndlight, qboolean spawnparticles)
1305 qboolean found = false;
1306 if (effectnameindex < 1 || effectnameindex >= MAX_PARTICLEEFFECTNAME || !particleeffectname[effectnameindex][0])
1308 Con_DPrintf("Unknown effect number %i received from server\n", effectnameindex);
1309 return; // no such effect
1311 VectorLerp(originmins, 0.5, originmaxs, center);
1312 if (!cl_particles_quake.integer && particleeffectinfo[0].effectnameindex)
1314 int effectinfoindex;
1317 particleeffectinfo_t *info;
1319 vec3_t centervelocity;
1325 qboolean underwater;
1326 qboolean immediatebloodstain;
1328 // note this runs multiple effects with the same name, each one spawns only one kind of particle, so some effects need more than one
1329 VectorLerp(originmins, 0.5, originmaxs, center);
1330 VectorLerp(velocitymins, 0.5, velocitymaxs, centervelocity);
1331 supercontents = CL_PointSuperContents(center);
1332 underwater = (supercontents & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)) != 0;
1333 VectorSubtract(originmaxs, originmins, traildir);
1334 traillen = VectorLength(traildir);
1335 VectorNormalize(traildir);
1336 for (effectinfoindex = 0, info = particleeffectinfo;effectinfoindex < MAX_PARTICLEEFFECTINFO && info->effectnameindex;effectinfoindex++, info++)
1338 if (info->effectnameindex == effectnameindex)
1341 if ((info->flags & PARTICLEEFFECT_UNDERWATER) && !underwater)
1343 if ((info->flags & PARTICLEEFFECT_NOTUNDERWATER) && underwater)
1346 // spawn a dlight if requested
1347 if (info->lightradiusstart > 0 && spawndlight)
1349 matrix4x4_t tempmatrix;
1350 if (info->trailspacing > 0)
1351 Matrix4x4_CreateTranslate(&tempmatrix, originmaxs[0], originmaxs[1], originmaxs[2]);
1353 Matrix4x4_CreateTranslate(&tempmatrix, center[0], center[1], center[2]);
1354 if (info->lighttime > 0 && info->lightradiusfade > 0)
1356 // light flash (explosion, etc)
1357 // called when effect starts
1358 CL_AllocLightFlash(NULL, &tempmatrix, info->lightradiusstart, info->lightcolor[0], info->lightcolor[1], info->lightcolor[2], info->lightradiusfade, info->lighttime, info->lightcubemapnum, -1, info->lightshadow, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1363 // called by CL_LinkNetworkEntity
1364 Matrix4x4_Scale(&tempmatrix, info->lightradiusstart, 1);
1365 R_RTLight_Update(&r_refdef.scene.templights[r_refdef.scene.numlights], false, &tempmatrix, info->lightcolor, -1, info->lightcubemapnum > 0 ? va("cubemaps/%i", info->lightcubemapnum) : NULL, info->lightshadow, 1, 0.25, 0, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
1366 r_refdef.scene.lights[r_refdef.scene.numlights] = &r_refdef.scene.templights[r_refdef.scene.numlights];r_refdef.scene.numlights++;
1370 if (!spawnparticles)
1375 if (info->tex[1] > info->tex[0])
1377 tex = (int)lhrandom(info->tex[0], info->tex[1]);
1378 tex = min(tex, info->tex[1] - 1);
1380 if(info->staintex[0] < 0)
1381 staintex = info->staintex[0];
1384 staintex = (int)lhrandom(info->staintex[0], info->staintex[1]);
1385 staintex = min(staintex, info->staintex[1] - 1);
1387 if (info->particletype == pt_decal)
1388 CL_SpawnDecalParticleForPoint(center, info->originjitter[0], lhrandom(info->size[0], info->size[1]), lhrandom(info->alpha[0], info->alpha[1]), tex, info->color[0], info->color[1]);
1389 else if (info->orientation == PARTICLE_HBEAM)
1390 CL_NewParticle(info->particletype, info->color[0], info->color[1], tex, lhrandom(info->size[0], info->size[1]), info->size[2], lhrandom(info->alpha[0], info->alpha[1]), info->alpha[2], 0, 0, originmins[0], originmins[1], originmins[2], originmaxs[0], originmaxs[1], originmaxs[2], 0, 0, 0, 0, false, lhrandom(info->time[0], info->time[1]), info->stretchfactor, info->blendmode, info->orientation, info->staincolor[0], info->staincolor[1], staintex);
1393 if (!cl_particles.integer)
1395 switch (info->particletype)
1397 case pt_smoke: if (!cl_particles_smoke.integer) continue;break;
1398 case pt_spark: if (!cl_particles_sparks.integer) continue;break;
1399 case pt_bubble: if (!cl_particles_bubbles.integer) continue;break;
1400 case pt_blood: if (!cl_particles_blood.integer) continue;break;
1401 case pt_rain: if (!cl_particles_rain.integer) continue;break;
1402 case pt_snow: if (!cl_particles_snow.integer) continue;break;
1405 VectorCopy(originmins, trailpos);
1406 if (info->trailspacing > 0)
1408 info->particleaccumulator += traillen / info->trailspacing * cl_particles_quality.value;
1409 trailstep = info->trailspacing / cl_particles_quality.value;
1410 immediatebloodstain = false;
1414 info->particleaccumulator += info->countabsolute + pcount * info->countmultiplier * cl_particles_quality.value;
1416 immediatebloodstain = info->particletype == pt_blood || staintex;
1418 info->particleaccumulator = bound(0, info->particleaccumulator, 16384);
1419 for (;info->particleaccumulator >= 1;info->particleaccumulator--)
1421 if (info->tex[1] > info->tex[0])
1423 tex = (int)lhrandom(info->tex[0], info->tex[1]);
1424 tex = min(tex, info->tex[1] - 1);
1428 trailpos[0] = lhrandom(originmins[0], originmaxs[0]);
1429 trailpos[1] = lhrandom(originmins[1], originmaxs[1]);
1430 trailpos[2] = lhrandom(originmins[2], originmaxs[2]);
1433 part = CL_NewParticle(info->particletype, info->color[0], info->color[1], tex, lhrandom(info->size[0], info->size[1]), info->size[2], lhrandom(info->alpha[0], info->alpha[1]), info->alpha[2], info->gravity, info->bounce, trailpos[0] + info->originoffset[0] + info->originjitter[0] * rvec[0], trailpos[1] + info->originoffset[1] + info->originjitter[1] * rvec[1], trailpos[2] + info->originoffset[2] + info->originjitter[2] * rvec[2], lhrandom(velocitymins[0], velocitymaxs[0]) * info->velocitymultiplier + info->velocityoffset[0] + info->velocityjitter[0] * rvec[0], lhrandom(velocitymins[1], velocitymaxs[1]) * info->velocitymultiplier + info->velocityoffset[1] + info->velocityjitter[1] * rvec[1], lhrandom(velocitymins[2], velocitymaxs[2]) * info->velocitymultiplier + info->velocityoffset[2] + info->velocityjitter[2] * rvec[2], info->airfriction, info->liquidfriction, 0, 0, info->countabsolute <= 0, lhrandom(info->time[0], info->time[1]), info->stretchfactor, info->blendmode, info->orientation, info->staincolor[0], info->staincolor[1], staintex);
1434 if (immediatebloodstain && part)
1436 immediatebloodstain = false;
1437 CL_ImmediateBloodStain(part);
1440 VectorMA(trailpos, trailstep, traildir, trailpos);
1447 CL_ParticleEffect_Fallback(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, spawndlight, spawnparticles);
1450 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)
1452 CL_ParticleTrail(effectnameindex, pcount, originmins, originmaxs, velocitymins, velocitymaxs, ent, palettecolor, true, true);
1460 void CL_EntityParticles (const entity_t *ent)
1463 float pitch, yaw, dist = 64, beamlength = 16, org[3], v[3];
1464 static vec3_t avelocities[NUMVERTEXNORMALS];
1465 if (!cl_particles.integer) return;
1466 if (cl.time <= cl.oldtime) return; // don't spawn new entity particles while paused
1468 Matrix4x4_OriginFromMatrix(&ent->render.matrix, org);
1470 if (!avelocities[0][0])
1471 for (i = 0;i < NUMVERTEXNORMALS * 3;i++)
1472 avelocities[0][i] = lhrandom(0, 2.55);
1474 for (i = 0;i < NUMVERTEXNORMALS;i++)
1476 yaw = cl.time * avelocities[i][0];
1477 pitch = cl.time * avelocities[i][1];
1478 v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
1479 v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
1480 v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
1481 CL_NewParticle(pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 1, 0, 255, 0, 0, 0, v[0], v[1], v[2], 0, 0, 0, 0, 0, 0, 0, true, 0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1486 void CL_ReadPointFile_f (void)
1488 vec3_t org, leakorg;
1490 char *pointfile = NULL, *pointfilepos, *t, tchar;
1491 char name[MAX_OSPATH];
1496 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
1497 strlcat (name, ".pts", sizeof (name));
1498 pointfile = (char *)FS_LoadFile(name, tempmempool, true, NULL);
1501 Con_Printf("Could not open %s\n", name);
1505 Con_Printf("Reading %s...\n", name);
1506 VectorClear(leakorg);
1509 pointfilepos = pointfile;
1510 while (*pointfilepos)
1512 while (*pointfilepos == '\n' || *pointfilepos == '\r')
1517 while (*t && *t != '\n' && *t != '\r')
1521 #if _MSC_VER >= 1400
1522 #define sscanf sscanf_s
1524 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
1530 VectorCopy(org, leakorg);
1533 if (cl.num_particles < cl.max_particles - 3)
1536 CL_NewParticle(pt_alphastatic, particlepalette[(-c)&15], particlepalette[(-c)&15], tex_particle, 2, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0, 0, 0, 0, true, 1<<30, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1539 Mem_Free(pointfile);
1540 VectorCopy(leakorg, org);
1541 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
1543 CL_NewParticle(pt_beam, 0xFF0000, 0xFF0000, tex_beam, 64, 0, 255, 0, 0, 0, org[0] - 4096, org[1], org[2], org[0] + 4096, org[1], org[2], 0, 0, 0, 0, false, 1<<30, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1);
1544 CL_NewParticle(pt_beam, 0x00FF00, 0x00FF00, tex_beam, 64, 0, 255, 0, 0, 0, org[0], org[1] - 4096, org[2], org[0], org[1] + 4096, org[2], 0, 0, 0, 0, false, 1<<30, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1);
1545 CL_NewParticle(pt_beam, 0x0000FF, 0x0000FF, tex_beam, 64, 0, 255, 0, 0, 0, org[0], org[1], org[2] - 4096, org[0], org[1], org[2] + 4096, 0, 0, 0, 0, false, 1<<30, 1, PBLEND_ADD, PARTICLE_HBEAM, -1, -1, -1);
1550 CL_ParseParticleEffect
1552 Parse an effect out of the server message
1555 void CL_ParseParticleEffect (void)
1558 int i, count, msgcount, color;
1560 MSG_ReadVector(org, cls.protocol);
1561 for (i=0 ; i<3 ; i++)
1562 dir[i] = MSG_ReadChar () * (1.0 / 16.0);
1563 msgcount = MSG_ReadByte ();
1564 color = MSG_ReadByte ();
1566 if (msgcount == 255)
1571 CL_ParticleEffect(EFFECT_SVC_PARTICLE, count, org, org, dir, dir, NULL, color);
1576 CL_ParticleExplosion
1580 void CL_ParticleExplosion (const vec3_t org)
1586 R_Stain(org, 96, 40, 40, 40, 64, 88, 88, 88, 64);
1587 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1589 if (cl_particles_quake.integer)
1591 for (i = 0;i < 1024;i++)
1597 color = particlepalette[ramp1[r]];
1598 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256, true, 0.1006 * (8 - r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1602 color = particlepalette[ramp2[r]];
1603 CL_NewParticle(pt_alphastatic, color, color, tex_particle, 1.5f, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 1, 1, 16, 256, true, 0.0669 * (8 - r), 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1609 i = CL_PointSuperContents(org);
1610 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
1612 if (cl_particles.integer && cl_particles_bubbles.integer)
1613 for (i = 0;i < 128 * cl_particles_quality.value;i++)
1614 CL_NewParticle(pt_bubble, 0x404040, 0x808080, tex_bubble, 2, 0, lhrandom(128, 255), 128, -0.125, 1.5, org[0], org[1], org[2], 0, 0, 0, 0.0625, 0.25, 16, 96, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1618 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
1620 for (i = 0;i < 512 * cl_particles_quality.value;i++)
1624 for (k = 0;k < 16;k++)
1627 VectorMA(org, 128, v2, v);
1628 trace = CL_TraceLine(org, v, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
1629 if (trace.fraction >= 0.1)
1632 VectorSubtract(trace.endpos, org, v2);
1633 VectorScale(v2, 2.0f, v2);
1634 CL_NewParticle(pt_spark, 0x903010, 0xFFD030, tex_particle, 1.0f, 0, lhrandom(0, 255), 512, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0, 0, 0, 0, true, 0, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
1640 if (cl_particles_explosions_shell.integer)
1641 R_NewExplosion(org);
1646 CL_ParticleExplosion2
1650 void CL_ParticleExplosion2 (const vec3_t org, int colorStart, int colorLength)
1653 if (!cl_particles.integer) return;
1655 for (i = 0;i < 512 * cl_particles_quality.value;i++)
1657 k = particlepalette[colorStart + (i % colorLength)];
1658 if (cl_particles_quake.integer)
1659 CL_NewParticle(pt_alphastatic, k, k, tex_particle, 1, 0, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, -4, 16, 256, true, 0.3, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1661 CL_NewParticle(pt_alphastatic, k, k, tex_particle, lhrandom(0.5, 1.5), 0, 255, 512, 0, 0, org[0], org[1], org[2], 0, 0, 0, lhrandom(1.5, 3), lhrandom(1.5, 3), 8, 192, true, 0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1665 static void CL_Sparks(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float sparkcount)
1667 if (cl_particles_sparks.integer)
1669 sparkcount *= cl_particles_quality.value;
1670 while(sparkcount-- > 0)
1671 CL_NewParticle(pt_spark, particlepalette[0x68], particlepalette[0x6f], tex_particle, 0.5f, 0, lhrandom(64, 255), 512, 1, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]) + cl.movevars_gravity * 0.1f, 0, 0, 0, 64, true, 0, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
1675 static void CL_Smoke(const vec3_t originmins, const vec3_t originmaxs, const vec3_t velocitymins, const vec3_t velocitymaxs, float smokecount)
1677 if (cl_particles_smoke.integer)
1679 smokecount *= cl_particles_quality.value;
1680 while(smokecount-- > 0)
1681 CL_NewParticle(pt_smoke, 0x101010, 0x101010, tex_smoke[rand()&7], 2, 2, 255, 256, 0, 0, lhrandom(originmins[0], originmaxs[0]), lhrandom(originmins[1], originmaxs[1]), lhrandom(originmins[2], originmaxs[2]), lhrandom(velocitymins[0], velocitymaxs[0]), lhrandom(velocitymins[1], velocitymaxs[1]), lhrandom(velocitymins[2], velocitymaxs[2]), 0, 0, 0, smokecount > 0 ? 16 : 0, true, 0, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1685 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)
1688 if (!cl_particles.integer) return;
1690 count = (int)(count * cl_particles_quality.value);
1693 k = particlepalette[colorbase + (rand()&3)];
1694 CL_NewParticle(pt_alphastatic, k, k, tex_particle, 2, 0, 255, 128, gravity, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0], dir[1], dir[2], 0, 0, 0, randomvel, true, 0, 1, PBLEND_ALPHA, PARTICLE_BILLBOARD, -1, -1, -1);
1698 void CL_ParticleRain (const vec3_t mins, const vec3_t maxs, const vec3_t dir, int count, int colorbase, int type)
1701 float minz, maxz, lifetime = 30;
1702 if (!cl_particles.integer) return;
1703 if (dir[2] < 0) // falling
1705 minz = maxs[2] + dir[2] * 0.1;
1708 lifetime = (maxz - cl.worldmodel->normalmins[2]) / max(1, -dir[2]);
1713 maxz = maxs[2] + dir[2] * 0.1;
1715 lifetime = (cl.worldmodel->normalmaxs[2] - minz) / max(1, dir[2]);
1718 count = (int)(count * cl_particles_quality.value);
1723 if (!cl_particles_rain.integer) break;
1724 count *= 4; // ick, this should be in the mod or maps?
1728 k = particlepalette[colorbase + (rand()&3)];
1729 if (gamemode == GAME_GOODVSBAD2)
1730 CL_NewParticle(pt_rain, k, k, tex_particle, 20, 0, lhrandom(32, 64), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
1732 CL_NewParticle(pt_rain, k, k, tex_particle, 0.5, 0, lhrandom(32, 64), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_SPARK, -1, -1, -1);
1736 if (!cl_particles_snow.integer) break;
1739 k = particlepalette[colorbase + (rand()&3)];
1740 if (gamemode == GAME_GOODVSBAD2)
1741 CL_NewParticle(pt_snow, k, k, tex_particle, 20, 0, lhrandom(64, 128), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1743 CL_NewParticle(pt_snow, k, k, tex_particle, 1, 0, lhrandom(64, 128), 0, 0, -1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(minz, maxz), dir[0], dir[1], dir[2], 0, 0, 0, 0, true, lifetime, 1, PBLEND_ADD, PARTICLE_BILLBOARD, -1, -1, -1);
1747 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
1751 static cvar_t r_drawparticles = {0, "r_drawparticles", "1", "enables drawing of particles"};
1752 static cvar_t r_drawparticles_drawdistance = {CVAR_SAVE, "r_drawparticles_drawdistance", "2000", "particles further than drawdistance*size will not be drawn"};
1753 static cvar_t r_drawdecals = {0, "r_drawdecals", "1", "enables drawing of decals"};
1754 static cvar_t r_drawdecals_drawdistance = {CVAR_SAVE, "r_drawdecals_drawdistance", "500", "decals further than drawdistance*size will not be drawn"};
1756 #define PARTICLETEXTURESIZE 64
1757 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1759 static unsigned char shadebubble(float dx, float dy, vec3_t light)
1763 dz = 1 - (dx*dx+dy*dy);
1764 if (dz > 0) // it does hit the sphere
1768 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1769 VectorNormalize(normal);
1770 dot = DotProduct(normal, light);
1771 if (dot > 0.5) // interior reflection
1772 f += ((dot * 2) - 1);
1773 else if (dot < -0.5) // exterior reflection
1774 f += ((dot * -2) - 1);
1776 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1777 VectorNormalize(normal);
1778 dot = DotProduct(normal, light);
1779 if (dot > 0.5) // interior reflection
1780 f += ((dot * 2) - 1);
1781 else if (dot < -0.5) // exterior reflection
1782 f += ((dot * -2) - 1);
1784 f += 16; // just to give it a haze so you can see the outline
1785 f = bound(0, f, 255);
1786 return (unsigned char) f;
1792 int particlefontwidth, particlefontheight, particlefontcellwidth, particlefontcellheight, particlefontrows, particlefontcols;
1793 void CL_Particle_PixelCoordsForTexnum(int texnum, int *basex, int *basey, int *width, int *height)
1795 *basex = (texnum % particlefontcols) * particlefontcellwidth;
1796 *basey = ((texnum / particlefontcols) % particlefontrows) * particlefontcellheight;
1797 *width = particlefontcellwidth;
1798 *height = particlefontcellheight;
1801 static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
1803 int basex, basey, w, h, y;
1804 CL_Particle_PixelCoordsForTexnum(texnum, &basex, &basey, &w, &h);
1805 if(w != PARTICLETEXTURESIZE || h != PARTICLETEXTURESIZE)
1806 Sys_Error("invalid particle texture size for autogenerating");
1807 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1808 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1811 void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
1814 float cx, cy, dx, dy, f, iradius;
1816 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1817 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1818 iradius = 1.0f / radius;
1819 alpha *= (1.0f / 255.0f);
1820 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1822 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1826 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1831 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1832 d[0] += (int)(f * (blue - d[0]));
1833 d[1] += (int)(f * (green - d[1]));
1834 d[2] += (int)(f * (red - d[2]));
1840 void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1843 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1845 data[0] = bound(minb, data[0], maxb);
1846 data[1] = bound(ming, data[1], maxg);
1847 data[2] = bound(minr, data[2], maxr);
1851 void particletextureinvert(unsigned char *data)
1854 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1856 data[0] = 255 - data[0];
1857 data[1] = 255 - data[1];
1858 data[2] = 255 - data[2];
1862 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1863 static void R_InitBloodTextures (unsigned char *particletexturedata)
1866 size_t datasize = PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4;
1867 unsigned char *data = Mem_Alloc(tempmempool, datasize);
1870 for (i = 0;i < 8;i++)
1872 memset(data, 255, datasize);
1873 for (k = 0;k < 24;k++)
1874 particletextureblotch(data, PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1875 //particletextureclamp(data, 32, 32, 32, 255, 255, 255);
1876 particletextureinvert(data);
1877 setuptex(tex_bloodparticle[i], data, particletexturedata);
1881 for (i = 0;i < 8;i++)
1883 memset(data, 255, datasize);
1885 for (j = 1;j < 10;j++)
1886 for (k = min(j, m - 1);k < m;k++)
1887 particletextureblotch(data, (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 320 - j * 8);
1888 //particletextureclamp(data, 32, 32, 32, 255, 255, 255);
1889 particletextureinvert(data);
1890 setuptex(tex_blooddecal[i], data, particletexturedata);
1896 //uncomment this to make engine save out particle font to a tga file when run
1897 //#define DUMPPARTICLEFONT
1899 static void R_InitParticleTexture (void)
1901 int x, y, d, i, k, m;
1902 int basex, basey, w, h;
1906 fs_offset_t filesize;
1908 // a note: decals need to modulate (multiply) the background color to
1909 // properly darken it (stain), and they need to be able to alpha fade,
1910 // this is a very difficult challenge because it means fading to white
1911 // (no change to background) rather than black (darkening everything
1912 // behind the whole decal polygon), and to accomplish this the texture is
1913 // inverted (dark red blood on white background becomes brilliant cyan
1914 // and white on black background) so we can alpha fade it to black, then
1915 // we invert it again during the blendfunc to make it work...
1917 #ifndef DUMPPARTICLEFONT
1918 decalskinframe = R_SkinFrame_LoadExternal("particles/particlefont.tga", TEXF_ALPHA | TEXF_FORCELINEAR, false);
1921 particlefonttexture = decalskinframe->base;
1922 // TODO maybe allow custom grid size?
1923 particlefontwidth = image_width;
1924 particlefontheight = image_height;
1925 particlefontcellwidth = image_width / 8;
1926 particlefontcellheight = image_height / 8;
1927 particlefontcols = 8;
1928 particlefontrows = 8;
1933 unsigned char *particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1934 size_t datasize = PARTICLETEXTURESIZE*PARTICLETEXTURESIZE*4;
1935 unsigned char *data = (unsigned char *)Mem_Alloc(tempmempool, datasize);
1936 unsigned char *noise1 = (unsigned char *)Mem_Alloc(tempmempool, PARTICLETEXTURESIZE*2*PARTICLETEXTURESIZE*2);
1937 unsigned char *noise2 = (unsigned char *)Mem_Alloc(tempmempool, PARTICLETEXTURESIZE*2*PARTICLETEXTURESIZE*2);
1939 particlefontwidth = particlefontheight = PARTICLEFONTSIZE;
1940 particlefontcellwidth = particlefontcellheight = PARTICLETEXTURESIZE;
1941 particlefontcols = 8;
1942 particlefontrows = 8;
1944 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1947 for (i = 0;i < 8;i++)
1949 memset(data, 255, datasize);
1952 fractalnoise(noise1, PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1953 fractalnoise(noise2, PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1955 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1957 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1958 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1960 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1961 d = (noise2[y*PARTICLETEXTURESIZE*2+x] - 128) * 3 + 192;
1963 d = (int)(d * (1-(dx*dx+dy*dy)));
1964 d = (d * noise1[y*PARTICLETEXTURESIZE*2+x]) >> 7;
1965 d = bound(0, d, 255);
1966 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (unsigned char) d;
1973 setuptex(tex_smoke[i], data, particletexturedata);
1977 memset(data, 255, datasize);
1978 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1980 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1981 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1983 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1984 f = 255.0f * (1.0 - 4.0f * fabs(10.0f - sqrt(dx*dx+dy*dy)));
1985 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (int) (bound(0.0f, f, 255.0f));
1988 setuptex(tex_rainsplash, data, particletexturedata);
1991 memset(data, 255, datasize);
1992 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1994 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1995 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1997 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1998 d = (int)(256 * (1 - (dx*dx+dy*dy)));
1999 d = bound(0, d, 255);
2000 data[(y*PARTICLETEXTURESIZE+x)*4+3] = (unsigned char) d;
2003 setuptex(tex_particle, data, particletexturedata);
2006 memset(data, 255, datasize);
2007 light[0] = 1;light[1] = 1;light[2] = 1;
2008 VectorNormalize(light);
2009 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2011 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2012 // stretch upper half of bubble by +50% and shrink lower half by -50%
2013 // (this gives an elongated teardrop shape)
2015 dy = (dy - 0.5f) * 2.0f;
2017 dy = (dy - 0.5f) / 1.5f;
2018 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2020 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2021 // shrink bubble width to half
2023 data[(y*PARTICLETEXTURESIZE+x)*4+3] = shadebubble(dx, dy, light);
2026 setuptex(tex_raindrop, data, particletexturedata);
2029 memset(data, 255, datasize);
2030 light[0] = 1;light[1] = 1;light[2] = 1;
2031 VectorNormalize(light);
2032 for (y = 0;y < PARTICLETEXTURESIZE;y++)
2034 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2035 for (x = 0;x < PARTICLETEXTURESIZE;x++)
2037 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
2038 data[(y*PARTICLETEXTURESIZE+x)*4+3] = shadebubble(dx, dy, light);
2041 setuptex(tex_bubble, data, particletexturedata);
2043 // Blood particles and blood decals
2044 R_InitBloodTextures (particletexturedata);
2047 for (i = 0;i < 8;i++)
2049 memset(data, 255, datasize);
2050 for (k = 0;k < 12;k++)
2051 particletextureblotch(data, PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
2052 for (k = 0;k < 3;k++)
2053 particletextureblotch(data, PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
2054 //particletextureclamp(data, 64, 64, 64, 255, 255, 255);
2055 particletextureinvert(data);
2056 setuptex(tex_bulletdecal[i], data, particletexturedata);
2059 #ifdef DUMPPARTICLEFONT
2060 Image_WriteTGABGRA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
2063 decalskinframe = R_SkinFrame_LoadInternalBGRA("particlefont", TEXF_ALPHA | TEXF_FORCELINEAR, particletexturedata, PARTICLEFONTSIZE, PARTICLEFONTSIZE);
2064 particlefonttexture = decalskinframe->base;
2066 Mem_Free(particletexturedata);
2071 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
2073 CL_Particle_PixelCoordsForTexnum(i, &basex, &basey, &w, &h);
2074 particletexture[i].texture = particlefonttexture;
2075 particletexture[i].s1 = (basex + 1) / (float)particlefontwidth;
2076 particletexture[i].t1 = (basey + 1) / (float)particlefontheight;
2077 particletexture[i].s2 = (basex + w - 1) / (float)particlefontwidth;
2078 particletexture[i].t2 = (basey + h - 1) / (float)particlefontheight;
2081 #ifndef DUMPPARTICLEFONT
2082 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", false, TEXF_ALPHA | TEXF_FORCELINEAR, true);
2083 if (!particletexture[tex_beam].texture)
2086 unsigned char noise3[64][64], data2[64][16][4];
2088 fractalnoise(&noise3[0][0], 64, 4);
2090 for (y = 0;y < 64;y++)
2092 dy = (y - 0.5f*64) / (64*0.5f-1);
2093 for (x = 0;x < 16;x++)
2095 dx = (x - 0.5f*16) / (16*0.5f-2);
2096 d = (int)((1 - sqrt(fabs(dx))) * noise3[y][x]);
2097 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
2098 data2[y][x][3] = 255;
2102 #ifdef DUMPPARTICLEFONT
2103 Image_WriteTGABGRA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
2105 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_FORCELINEAR, NULL);
2107 particletexture[tex_beam].s1 = 0;
2108 particletexture[tex_beam].t1 = 0;
2109 particletexture[tex_beam].s2 = 1;
2110 particletexture[tex_beam].t2 = 1;
2112 // now load an texcoord/texture override file
2113 buf = (char *) FS_LoadFile("particles/particlefont.txt", tempmempool, false, &filesize);
2120 if(!COM_ParseToken_Simple(&bufptr, true, false))
2122 if(!strcmp(com_token, "\n"))
2123 continue; // empty line
2124 i = atoi(com_token) % MAX_PARTICLETEXTURES;
2125 particletexture[i].texture = particlefonttexture;
2127 if (!COM_ParseToken_Simple(&bufptr, true, false))
2129 if (!strcmp(com_token, "\n"))
2131 Con_Printf("particlefont file: syntax should be texnum texturename or texnum x y w h\n");
2134 particletexture[i].s1 = atof(com_token);
2136 if (!COM_ParseToken_Simple(&bufptr, true, false))
2138 if (!strcmp(com_token, "\n"))
2140 Con_Printf("particlefont file: syntax should be texnum texturename or texnum x y w h\n");
2143 particletexture[i].t1 = atof(com_token);
2145 if (!COM_ParseToken_Simple(&bufptr, true, false))
2147 if (!strcmp(com_token, "\n"))
2149 Con_Printf("particlefont file: syntax should be texnum texturename or texnum x y w h\n");
2152 particletexture[i].s2 = atof(com_token);
2154 if (!COM_ParseToken_Simple(&bufptr, true, false))
2156 if (!strcmp(com_token, "\n"))
2158 Con_Printf("particlefont file: syntax should be texnum texturename or texnum x y w h\n");
2161 particletexture[i].t2 = atof(com_token);
2167 static void r_part_start(void)
2170 // generate particlepalette for convenience from the main one
2171 for (i = 0;i < 256;i++)
2172 particlepalette[i] = palette_rgb[i][0] * 65536 + palette_rgb[i][1] * 256 + palette_rgb[i][2];
2173 particletexturepool = R_AllocTexturePool();
2174 R_InitParticleTexture ();
2175 CL_Particles_LoadEffectInfo();
2178 static void r_part_shutdown(void)
2180 R_FreeTexturePool(&particletexturepool);
2183 static void r_part_newmap(void)
2186 R_SkinFrame_MarkUsed(decalskinframe);
2187 CL_Particles_LoadEffectInfo();
2190 #define BATCHSIZE 256
2191 unsigned short particle_elements[BATCHSIZE*6];
2192 float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16];
2194 void R_Particles_Init (void)
2197 for (i = 0;i < BATCHSIZE;i++)
2199 particle_elements[i*6+0] = i*4+0;
2200 particle_elements[i*6+1] = i*4+1;
2201 particle_elements[i*6+2] = i*4+2;
2202 particle_elements[i*6+3] = i*4+0;
2203 particle_elements[i*6+4] = i*4+2;
2204 particle_elements[i*6+5] = i*4+3;
2207 Cvar_RegisterVariable(&r_drawparticles);
2208 Cvar_RegisterVariable(&r_drawparticles_drawdistance);
2209 Cvar_RegisterVariable(&r_drawdecals);
2210 Cvar_RegisterVariable(&r_drawdecals_drawdistance);
2211 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
2214 void R_DrawDecal_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2216 int surfacelistindex;
2218 float *v3f, *t2f, *c4f;
2219 particletexture_t *tex;
2220 float right[3], up[3], size, ca;
2221 float alphascale = (1.0f / 65536.0f) * cl_particles_alpha.value;
2222 float particle_vertex3f[BATCHSIZE*12], particle_texcoord2f[BATCHSIZE*8], particle_color4f[BATCHSIZE*16];
2224 RSurf_ActiveWorldEntity();
2226 r_refdef.stats.drawndecals += numsurfaces;
2227 R_Mesh_ResetTextureState();
2228 R_Mesh_VertexPointer(particle_vertex3f, 0, 0);
2229 R_Mesh_TexCoordPointer(0, 2, particle_texcoord2f, 0, 0);
2230 R_Mesh_ColorPointer(particle_color4f, 0, 0);
2231 GL_DepthMask(false);
2232 GL_DepthRange(0, 1);
2233 GL_PolygonOffset(0, 0);
2235 GL_CullFace(GL_NONE);
2237 // generate all the vertices at once
2238 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
2240 d = cl.decals + surfacelist[surfacelistindex];
2243 c4f = particle_color4f + 16*surfacelistindex;
2244 ca = d->alpha * alphascale;
2245 if (r_refdef.fogenabled)
2246 ca *= RSurf_FogVertex(d->org);
2247 Vector4Set(c4f, d->color[0] * ca, d->color[1] * ca, d->color[2] * ca, 1);
2248 Vector4Copy(c4f, c4f + 4);
2249 Vector4Copy(c4f, c4f + 8);
2250 Vector4Copy(c4f, c4f + 12);
2252 // calculate vertex positions
2253 size = d->size * cl_particles_size.value;
2254 VectorVectors(d->normal, right, up);
2255 VectorScale(right, size, right);
2256 VectorScale(up, size, up);
2257 v3f = particle_vertex3f + 12*surfacelistindex;
2258 v3f[ 0] = d->org[0] - right[0] - up[0];
2259 v3f[ 1] = d->org[1] - right[1] - up[1];
2260 v3f[ 2] = d->org[2] - right[2] - up[2];
2261 v3f[ 3] = d->org[0] - right[0] + up[0];
2262 v3f[ 4] = d->org[1] - right[1] + up[1];
2263 v3f[ 5] = d->org[2] - right[2] + up[2];
2264 v3f[ 6] = d->org[0] + right[0] + up[0];
2265 v3f[ 7] = d->org[1] + right[1] + up[1];
2266 v3f[ 8] = d->org[2] + right[2] + up[2];
2267 v3f[ 9] = d->org[0] + right[0] - up[0];
2268 v3f[10] = d->org[1] + right[1] - up[1];
2269 v3f[11] = d->org[2] + right[2] - up[2];
2271 // calculate texcoords
2272 tex = &particletexture[d->texnum];
2273 t2f = particle_texcoord2f + 8*surfacelistindex;
2274 t2f[0] = tex->s1;t2f[1] = tex->t2;
2275 t2f[2] = tex->s1;t2f[3] = tex->t1;
2276 t2f[4] = tex->s2;t2f[5] = tex->t1;
2277 t2f[6] = tex->s2;t2f[7] = tex->t2;
2280 // now render the decals all at once
2281 // (this assumes they all use one particle font texture!)
2282 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2283 R_SetupShader_Generic(particletexture[63].texture, NULL, GL_MODULATE, 1);
2284 R_Mesh_Draw(0, numsurfaces * 4, 0, numsurfaces * 2, NULL, particle_elements, 0, 0);
2287 void R_DrawDecals (void)
2290 int drawdecals = r_drawdecals.integer;
2295 int killsequence = cl.decalsequence - max(0, cl_decals_max.integer);
2297 frametime = bound(0, cl.time - cl.decals_updatetime, 1);
2298 cl.decals_updatetime = bound(cl.time - 1, cl.decals_updatetime + frametime, cl.time + 1);
2300 // LordHavoc: early out conditions
2304 decalfade = frametime * 256 / cl_decals_fadetime.value;
2305 drawdist2 = r_drawdecals_drawdistance.value * r_refdef.view.quality;
2306 drawdist2 = drawdist2*drawdist2;
2308 for (i = 0, decal = cl.decals;i < cl.num_decals;i++, decal++)
2310 if (!decal->typeindex)
2313 if (killsequence - decal->decalsequence > 0)
2316 if (cl.time > decal->time2 + cl_decals_time.value)
2318 decal->alpha -= decalfade;
2319 if (decal->alpha <= 0)
2325 if (cl.entities[decal->owner].render.model == decal->ownermodel)
2327 Matrix4x4_Transform(&cl.entities[decal->owner].render.matrix, decal->relativeorigin, decal->org);
2328 Matrix4x4_Transform3x3(&cl.entities[decal->owner].render.matrix, decal->relativenormal, decal->normal);
2334 if(cl_decals_visculling.integer && decal->clusterindex > -1000 && !CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, decal->clusterindex))
2340 if (DotProduct(r_refdef.view.origin, decal->normal) > DotProduct(decal->org, decal->normal) && VectorDistance2(decal->org, r_refdef.view.origin) < drawdist2 * (decal->size * decal->size))
2341 R_MeshQueue_AddTransparent(decal->org, R_DrawDecal_TransparentCallback, NULL, i, NULL);
2344 decal->typeindex = 0;
2345 if (cl.free_decal > i)
2349 // reduce cl.num_decals if possible
2350 while (cl.num_decals > 0 && cl.decals[cl.num_decals - 1].typeindex == 0)
2353 if (cl.num_decals == cl.max_decals && cl.max_decals < MAX_DECALS)
2355 decal_t *olddecals = cl.decals;
2356 cl.max_decals = min(cl.max_decals * 2, MAX_DECALS);
2357 cl.decals = (decal_t *) Mem_Alloc(cls.levelmempool, cl.max_decals * sizeof(decal_t));
2358 memcpy(cl.decals, olddecals, cl.num_decals * sizeof(decal_t));
2359 Mem_Free(olddecals);
2362 r_refdef.stats.totaldecals = cl.num_decals;
2365 void R_DrawParticle_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2367 int surfacelistindex;
2368 int batchstart, batchcount;
2369 const particle_t *p;
2371 rtexture_t *texture;
2372 float *v3f, *t2f, *c4f;
2373 particletexture_t *tex;
2374 float up2[3], v[3], right[3], up[3], fog, ifog, size, len, lenfactor;
2375 float ambient[3], diffuse[3], diffusenormal[3];
2376 vec4_t colormultiplier;
2378 RSurf_ActiveWorldEntity();
2380 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));
2382 r_refdef.stats.particles += numsurfaces;
2383 R_Mesh_ResetTextureState();
2384 R_Mesh_VertexPointer(particle_vertex3f, 0, 0);
2385 R_Mesh_TexCoordPointer(0, 2, particle_texcoord2f, 0, 0);
2386 R_Mesh_ColorPointer(particle_color4f, 0, 0);
2387 GL_DepthMask(false);
2388 GL_DepthRange(0, 1);
2389 GL_PolygonOffset(0, 0);
2391 GL_CullFace(GL_NONE);
2393 // first generate all the vertices at once
2394 for (surfacelistindex = 0, v3f = particle_vertex3f, t2f = particle_texcoord2f, c4f = particle_color4f;surfacelistindex < numsurfaces;surfacelistindex++, v3f += 3*4, t2f += 2*4, c4f += 4*4)
2396 p = cl.particles + surfacelist[surfacelistindex];
2398 blendmode = p->blendmode;
2402 case PBLEND_INVALID:
2404 c4f[0] = p->color[0] * (1.0f / 256.0f);
2405 c4f[1] = p->color[1] * (1.0f / 256.0f);
2406 c4f[2] = p->color[2] * (1.0f / 256.0f);
2407 c4f[3] = p->alpha * colormultiplier[3];
2408 // additive and modulate can just fade out in fog (this is correct)
2409 if (r_refdef.fogenabled)
2410 c4f[3] *= RSurf_FogVertex(p->org);
2411 // collapse alpha into color for these blends (so that the particlefont does not need alpha on most textures)
2418 c4f[0] = p->color[0] * colormultiplier[0];
2419 c4f[1] = p->color[1] * colormultiplier[1];
2420 c4f[2] = p->color[2] * colormultiplier[2];
2421 c4f[3] = p->alpha * colormultiplier[3];
2422 // additive and modulate can just fade out in fog (this is correct)
2423 if (r_refdef.fogenabled)
2424 c4f[3] *= RSurf_FogVertex(p->org);
2425 // collapse alpha into color for these blends (so that the particlefont does not need alpha on most textures)
2432 c4f[0] = p->color[0] * colormultiplier[0];
2433 c4f[1] = p->color[1] * colormultiplier[1];
2434 c4f[2] = p->color[2] * colormultiplier[2];
2435 c4f[3] = p->alpha * colormultiplier[3];
2436 // note: lighting is not cheap!
2437 if (particletype[p->typeindex].lighting)
2439 R_CompleteLightPoint(ambient, diffuse, diffusenormal, p->org, true);
2440 c4f[0] *= (ambient[0] + 0.5 * diffuse[0]);
2441 c4f[1] *= (ambient[1] + 0.5 * diffuse[1]);
2442 c4f[2] *= (ambient[2] + 0.5 * diffuse[2]);
2444 // mix in the fog color
2445 if (r_refdef.fogenabled)
2447 fog = RSurf_FogVertex(p->org);
2449 c4f[0] = c4f[0] * fog + r_refdef.fogcolor[0] * ifog;
2450 c4f[1] = c4f[1] * fog + r_refdef.fogcolor[1] * ifog;
2451 c4f[2] = c4f[2] * fog + r_refdef.fogcolor[2] * ifog;
2455 // copy the color into the other three vertices
2456 Vector4Copy(c4f, c4f + 4);
2457 Vector4Copy(c4f, c4f + 8);
2458 Vector4Copy(c4f, c4f + 12);
2460 size = p->size * cl_particles_size.value;
2461 tex = &particletexture[p->texnum];
2462 switch(p->orientation)
2464 case PARTICLE_INVALID:
2465 case PARTICLE_BILLBOARD:
2466 VectorScale(r_refdef.view.left, -size * p->stretch, right);
2467 VectorScale(r_refdef.view.up, size, up);
2468 v3f[ 0] = p->org[0] - right[0] - up[0];
2469 v3f[ 1] = p->org[1] - right[1] - up[1];
2470 v3f[ 2] = p->org[2] - right[2] - up[2];
2471 v3f[ 3] = p->org[0] - right[0] + up[0];
2472 v3f[ 4] = p->org[1] - right[1] + up[1];
2473 v3f[ 5] = p->org[2] - right[2] + up[2];
2474 v3f[ 6] = p->org[0] + right[0] + up[0];
2475 v3f[ 7] = p->org[1] + right[1] + up[1];
2476 v3f[ 8] = p->org[2] + right[2] + up[2];
2477 v3f[ 9] = p->org[0] + right[0] - up[0];
2478 v3f[10] = p->org[1] + right[1] - up[1];
2479 v3f[11] = p->org[2] + right[2] - up[2];
2480 t2f[0] = tex->s1;t2f[1] = tex->t2;
2481 t2f[2] = tex->s1;t2f[3] = tex->t1;
2482 t2f[4] = tex->s2;t2f[5] = tex->t1;
2483 t2f[6] = tex->s2;t2f[7] = tex->t2;
2485 case PARTICLE_ORIENTED_DOUBLESIDED:
2486 VectorVectors(p->vel, right, up);
2487 VectorScale(right, size * p->stretch, right);
2488 VectorScale(up, size, up);
2489 v3f[ 0] = p->org[0] - right[0] - up[0];
2490 v3f[ 1] = p->org[1] - right[1] - up[1];
2491 v3f[ 2] = p->org[2] - right[2] - up[2];
2492 v3f[ 3] = p->org[0] - right[0] + up[0];
2493 v3f[ 4] = p->org[1] - right[1] + up[1];
2494 v3f[ 5] = p->org[2] - right[2] + up[2];
2495 v3f[ 6] = p->org[0] + right[0] + up[0];
2496 v3f[ 7] = p->org[1] + right[1] + up[1];
2497 v3f[ 8] = p->org[2] + right[2] + up[2];
2498 v3f[ 9] = p->org[0] + right[0] - up[0];
2499 v3f[10] = p->org[1] + right[1] - up[1];
2500 v3f[11] = p->org[2] + right[2] - up[2];
2501 t2f[0] = tex->s1;t2f[1] = tex->t2;
2502 t2f[2] = tex->s1;t2f[3] = tex->t1;
2503 t2f[4] = tex->s2;t2f[5] = tex->t1;
2504 t2f[6] = tex->s2;t2f[7] = tex->t2;
2506 case PARTICLE_SPARK:
2507 len = VectorLength(p->vel);
2508 VectorNormalize2(p->vel, up);
2509 lenfactor = p->stretch * 0.04 * len;
2510 if(lenfactor < size * 0.5)
2511 lenfactor = size * 0.5;
2512 VectorMA(p->org, -lenfactor, up, v);
2513 VectorMA(p->org, lenfactor, up, up2);
2514 R_CalcBeam_Vertex3f(v3f, v, up2, size);
2515 t2f[0] = tex->s1;t2f[1] = tex->t2;
2516 t2f[2] = tex->s1;t2f[3] = tex->t1;
2517 t2f[4] = tex->s2;t2f[5] = tex->t1;
2518 t2f[6] = tex->s2;t2f[7] = tex->t2;
2520 case PARTICLE_VBEAM:
2521 R_CalcBeam_Vertex3f(v3f, p->org, p->vel, size);
2522 VectorSubtract(p->vel, p->org, up);
2523 VectorNormalize(up);
2524 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) * p->stretch;
2525 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f) * p->stretch;
2526 t2f[0] = tex->s2;t2f[1] = v[0];
2527 t2f[2] = tex->s1;t2f[3] = v[0];
2528 t2f[4] = tex->s1;t2f[5] = v[1];
2529 t2f[6] = tex->s2;t2f[7] = v[1];
2531 case PARTICLE_HBEAM:
2532 R_CalcBeam_Vertex3f(v3f, p->org, p->vel, size);
2533 VectorSubtract(p->vel, p->org, up);
2534 VectorNormalize(up);
2535 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f) * p->stretch;
2536 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f) * p->stretch;
2537 t2f[0] = v[0];t2f[1] = tex->t1;
2538 t2f[2] = v[0];t2f[3] = tex->t2;
2539 t2f[4] = v[1];t2f[5] = tex->t2;
2540 t2f[6] = v[1];t2f[7] = tex->t1;
2545 // now render batches of particles based on blendmode and texture
2546 blendmode = PBLEND_INVALID;
2550 for (surfacelistindex = 0;surfacelistindex < numsurfaces;)
2552 p = cl.particles + surfacelist[surfacelistindex];
2554 if (blendmode != p->blendmode)
2556 blendmode = p->blendmode;
2560 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2562 case PBLEND_INVALID:
2564 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2567 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
2571 if (texture != particletexture[p->texnum].texture)
2573 texture = particletexture[p->texnum].texture;
2574 R_SetupShader_Generic(texture, NULL, GL_MODULATE, 1);
2577 // iterate until we find a change in settings
2578 batchstart = surfacelistindex++;
2579 for (;surfacelistindex < numsurfaces;surfacelistindex++)
2581 p = cl.particles + surfacelist[surfacelistindex];
2582 if (blendmode != p->blendmode || texture != particletexture[p->texnum].texture)
2586 batchcount = surfacelistindex - batchstart;
2587 R_Mesh_Draw(batchstart * 4, batchcount * 4, batchstart * 2, batchcount * 2, NULL, particle_elements, 0, 0);
2591 void R_DrawParticles (void)
2594 int drawparticles = r_drawparticles.integer;
2595 float minparticledist;
2597 float gravity, dvel, decalfade, frametime, f, dist, oldorg[3];
2603 frametime = bound(0, cl.time - cl.particles_updatetime, 1);
2604 cl.particles_updatetime = bound(cl.time - 1, cl.particles_updatetime + frametime, cl.time + 1);
2606 // LordHavoc: early out conditions
2607 if (!cl.num_particles)
2610 minparticledist = DotProduct(r_refdef.view.origin, r_refdef.view.forward) + 4.0f;
2611 gravity = frametime * cl.movevars_gravity;
2612 dvel = 1+4*frametime;
2613 decalfade = frametime * 255 / cl_decals_fadetime.value;
2614 update = frametime > 0;
2615 drawdist2 = r_drawparticles_drawdistance.value * r_refdef.view.quality;
2616 drawdist2 = drawdist2*drawdist2;
2618 for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++)
2622 if (cl.free_particle > i)
2623 cl.free_particle = i;
2629 if (p->delayedspawn > cl.time)
2631 p->delayedspawn = 0;
2635 p->size += p->sizeincrease * frametime;
2636 p->alpha -= p->alphafade * frametime;
2638 if (p->alpha <= 0 || p->die <= cl.time)
2641 if (p->orientation != PARTICLE_VBEAM && p->orientation != PARTICLE_HBEAM && frametime > 0)
2643 if (p->liquidfriction && (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK))
2645 if (p->typeindex == pt_blood)
2646 p->size += frametime * 8;
2648 p->vel[2] -= p->gravity * gravity;
2649 f = 1.0f - min(p->liquidfriction * frametime, 1);
2650 VectorScale(p->vel, f, p->vel);
2654 p->vel[2] -= p->gravity * gravity;
2657 f = 1.0f - min(p->airfriction * frametime, 1);
2658 VectorScale(p->vel, f, p->vel);
2662 VectorCopy(p->org, oldorg);
2663 VectorMA(p->org, frametime, p->vel, p->org);
2664 if (p->bounce && cl.time >= p->delayedcollisions)
2666 trace = CL_TraceLine(oldorg, p->org, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | ((p->typeindex == pt_rain || p->typeindex == pt_snow) ? SUPERCONTENTS_LIQUIDSMASK : 0), true, false, &hitent, false);
2667 // if the trace started in or hit something of SUPERCONTENTS_NODROP
2668 // or if the trace hit something flagged as NOIMPACT
2669 // then remove the particle
2670 if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT || ((trace.startsupercontents | trace.hitsupercontents) & SUPERCONTENTS_NODROP) || (trace.startsupercontents & SUPERCONTENTS_SOLID))
2672 VectorCopy(trace.endpos, p->org);
2673 // react if the particle hit something
2674 if (trace.fraction < 1)
2676 VectorCopy(trace.endpos, p->org);
2678 if (p->staintexnum >= 0)
2680 // blood - splash on solid
2681 if (!(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
2684 (p->staincolor >> 16) & 0xFF, (p->staincolor >> 8) & 0xFF, p->staincolor & 0xFF, (int)(p->alpha * p->size * (1.0f / 80.0f)),
2685 (p->staincolor >> 16) & 0xFF, (p->staincolor >> 8) & 0xFF, p->staincolor & 0xFF, (int)(p->alpha * p->size * (1.0f / 80.0f)));
2686 if (cl_decals.integer)
2688 // create a decal for the blood splat
2689 CL_SpawnDecalParticleForSurface(hitent, p->org, trace.plane.normal, 0xFFFFFF ^ p->staincolor, 0xFFFFFF ^ p->staincolor, p->staintexnum, p->size * 2, p->alpha); // staincolor needs to be inverted for decals!
2694 if (p->typeindex == pt_blood)
2696 // blood - splash on solid
2697 if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS)
2699 if(p->staintexnum == -1) // staintex < -1 means no stains at all
2701 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)));
2702 if (cl_decals.integer)
2704 // create a decal for the blood splat
2705 CL_SpawnDecalParticleForSurface(hitent, p->org, trace.plane.normal, p->color[0] * 65536 + p->color[1] * 256 + p->color[2], p->color[0] * 65536 + p->color[1] * 256 + p->color[2], tex_blooddecal[rand()&7], p->size * 2, p->alpha);
2710 else if (p->bounce < 0)
2712 // bounce -1 means remove on impact
2717 // anything else - bounce off solid
2718 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
2719 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
2720 if (DotProduct(p->vel, p->vel) < 0.03)
2721 VectorClear(p->vel);
2727 if (p->typeindex != pt_static)
2729 switch (p->typeindex)
2731 case pt_entityparticle:
2732 // particle that removes itself after one rendered frame
2739 a = CL_PointSuperContents(p->org);
2740 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
2744 a = CL_PointSuperContents(p->org);
2745 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
2749 a = CL_PointSuperContents(p->org);
2750 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
2754 if (cl.time > p->time2)
2757 p->time2 = cl.time + (rand() & 3) * 0.1;
2758 p->vel[0] = p->vel[0] * 0.9f + lhrandom(-32, 32);
2759 p->vel[1] = p->vel[0] * 0.9f + lhrandom(-32, 32);
2761 a = CL_PointSuperContents(p->org);
2762 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
2770 else if (p->delayedspawn)
2774 // don't render particles too close to the view (they chew fillrate)
2775 // also don't render particles behind the view (useless)
2776 // further checks to cull to the frustum would be too slow here
2777 switch(p->typeindex)
2780 // beams have no culling
2781 R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL);
2784 if(cl_particles_visculling.integer)
2785 if (!r_refdef.viewcache.world_novis)
2786 if(r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.PointInLeaf)
2788 mleaf_t *leaf = r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, p->org);
2790 if(!CHECKPVSBIT(r_refdef.viewcache.world_pvsbits, leaf->clusterindex))
2793 // anything else just has to be in front of the viewer and visible at this distance
2794 if (DotProduct(p->org, r_refdef.view.forward) >= minparticledist && VectorDistance2(p->org, r_refdef.view.origin) < drawdist2 * (p->size * p->size))
2795 R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL);
2802 if (cl.free_particle > i)
2803 cl.free_particle = i;
2806 // reduce cl.num_particles if possible
2807 while (cl.num_particles > 0 && cl.particles[cl.num_particles - 1].typeindex == 0)
2810 if (cl.num_particles == cl.max_particles && cl.max_particles < MAX_PARTICLES)
2812 particle_t *oldparticles = cl.particles;
2813 cl.max_particles = min(cl.max_particles * 2, MAX_PARTICLES);
2814 cl.particles = (particle_t *) Mem_Alloc(cls.levelmempool, cl.max_particles * sizeof(particle_t));
2815 memcpy(cl.particles, oldparticles, cl.num_particles * sizeof(particle_t));
2816 Mem_Free(oldparticles);