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"
26 // must match ptype_t values
27 particletype_t particletype[pt_total] =
29 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_alphastatic
30 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_static
31 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_spark
32 {PBLEND_ADD, PARTICLE_BEAM, false}, //pt_beam
33 {PBLEND_ADD, PARTICLE_SPARK, false}, //pt_rain
34 {PBLEND_ADD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_raindecal
35 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_snow
36 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_bubble
37 {PBLEND_MOD, PARTICLE_BILLBOARD, false}, //pt_blood
38 {PBLEND_ADD, PARTICLE_BILLBOARD, false}, //pt_smoke
39 {PBLEND_MOD, PARTICLE_ORIENTED_DOUBLESIDED, false}, //pt_decal
40 {PBLEND_ALPHA, PARTICLE_BILLBOARD, false}, //pt_entityparticle
43 static int particlepalette[256] =
45 0x000000,0x0f0f0f,0x1f1f1f,0x2f2f2f,0x3f3f3f,0x4b4b4b,0x5b5b5b,0x6b6b6b, // 0-7
46 0x7b7b7b,0x8b8b8b,0x9b9b9b,0xababab,0xbbbbbb,0xcbcbcb,0xdbdbdb,0xebebeb, // 8-15
47 0x0f0b07,0x170f0b,0x1f170b,0x271b0f,0x2f2313,0x372b17,0x3f2f17,0x4b371b, // 16-23
48 0x533b1b,0x5b431f,0x634b1f,0x6b531f,0x73571f,0x7b5f23,0x836723,0x8f6f23, // 24-31
49 0x0b0b0f,0x13131b,0x1b1b27,0x272733,0x2f2f3f,0x37374b,0x3f3f57,0x474767, // 32-39
50 0x4f4f73,0x5b5b7f,0x63638b,0x6b6b97,0x7373a3,0x7b7baf,0x8383bb,0x8b8bcb, // 40-47
51 0x000000,0x070700,0x0b0b00,0x131300,0x1b1b00,0x232300,0x2b2b07,0x2f2f07, // 48-55
52 0x373707,0x3f3f07,0x474707,0x4b4b0b,0x53530b,0x5b5b0b,0x63630b,0x6b6b0f, // 56-63
53 0x070000,0x0f0000,0x170000,0x1f0000,0x270000,0x2f0000,0x370000,0x3f0000, // 64-71
54 0x470000,0x4f0000,0x570000,0x5f0000,0x670000,0x6f0000,0x770000,0x7f0000, // 72-79
55 0x131300,0x1b1b00,0x232300,0x2f2b00,0x372f00,0x433700,0x4b3b07,0x574307, // 80-87
56 0x5f4707,0x6b4b0b,0x77530f,0x835713,0x8b5b13,0x975f1b,0xa3631f,0xaf6723, // 88-95
57 0x231307,0x2f170b,0x3b1f0f,0x4b2313,0x572b17,0x632f1f,0x733723,0x7f3b2b, // 96-103
58 0x8f4333,0x9f4f33,0xaf632f,0xbf772f,0xcf8f2b,0xdfab27,0xefcb1f,0xfff31b, // 104-111
59 0x0b0700,0x1b1300,0x2b230f,0x372b13,0x47331b,0x533723,0x633f2b,0x6f4733, // 112-119
60 0x7f533f,0x8b5f47,0x9b6b53,0xa77b5f,0xb7876b,0xc3937b,0xd3a38b,0xe3b397, // 120-127
61 0xab8ba3,0x9f7f97,0x937387,0x8b677b,0x7f5b6f,0x775363,0x6b4b57,0x5f3f4b, // 128-135
62 0x573743,0x4b2f37,0x43272f,0x371f23,0x2b171b,0x231313,0x170b0b,0x0f0707, // 136-143
63 0xbb739f,0xaf6b8f,0xa35f83,0x975777,0x8b4f6b,0x7f4b5f,0x734353,0x6b3b4b, // 144-151
64 0x5f333f,0x532b37,0x47232b,0x3b1f23,0x2f171b,0x231313,0x170b0b,0x0f0707, // 152-159
65 0xdbc3bb,0xcbb3a7,0xbfa39b,0xaf978b,0xa3877b,0x977b6f,0x876f5f,0x7b6353, // 160-167
66 0x6b5747,0x5f4b3b,0x533f33,0x433327,0x372b1f,0x271f17,0x1b130f,0x0f0b07, // 168-175
67 0x6f837b,0x677b6f,0x5f7367,0x576b5f,0x4f6357,0x475b4f,0x3f5347,0x374b3f, // 176-183
68 0x2f4337,0x2b3b2f,0x233327,0x1f2b1f,0x172317,0x0f1b13,0x0b130b,0x070b07, // 184-191
69 0xfff31b,0xefdf17,0xdbcb13,0xcbb70f,0xbba70f,0xab970b,0x9b8307,0x8b7307, // 192-199
70 0x7b6307,0x6b5300,0x5b4700,0x4b3700,0x3b2b00,0x2b1f00,0x1b0f00,0x0b0700, // 200-207
71 0x0000ff,0x0b0bef,0x1313df,0x1b1bcf,0x2323bf,0x2b2baf,0x2f2f9f,0x2f2f8f, // 208-215
72 0x2f2f7f,0x2f2f6f,0x2f2f5f,0x2b2b4f,0x23233f,0x1b1b2f,0x13131f,0x0b0b0f, // 216-223
73 0x2b0000,0x3b0000,0x4b0700,0x5f0700,0x6f0f00,0x7f1707,0x931f07,0xa3270b, // 224-231
74 0xb7330f,0xc34b1b,0xcf632b,0xdb7f3b,0xe3974f,0xe7ab5f,0xefbf77,0xf7d38b, // 232-239
75 0xa77b3b,0xb79b37,0xc7c337,0xe7e357,0x7fbfff,0xabe7ff,0xd7ffff,0x670000, // 240-247
76 0x8b0000,0xb30000,0xd70000,0xff0000,0xfff393,0xfff7c7,0xffffff,0x9f5b53 // 248-255
79 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
80 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
81 int ramp3[8] = {0x6d, 0x6b, 6, 5, 4, 3};
83 //static int explosparkramp[8] = {0x4b0700, 0x6f0f00, 0x931f07, 0xb7330f, 0xcf632b, 0xe3974f, 0xffe7b5, 0xffffff};
85 // texture numbers in particle font
86 static const int tex_smoke[8] = {0, 1, 2, 3, 4, 5, 6, 7};
87 static const int tex_bulletdecal[8] = {8, 9, 10, 11, 12, 13, 14, 15};
88 static const int tex_blooddecal[8] = {16, 17, 18, 19, 20, 21, 22, 23};
89 static const int tex_bloodparticle[8] = {24, 25, 26, 27, 28, 29, 30, 31};
90 static const int tex_rainsplash[16] = {32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
91 static const int tex_particle = 63;
92 static const int tex_bubble = 62;
93 static const int tex_raindrop = 61;
94 static const int tex_beam = 60;
96 cvar_t cl_particles = {CVAR_SAVE, "cl_particles", "1", "enables particle effects"};
97 cvar_t cl_particles_quality = {CVAR_SAVE, "cl_particles_quality", "1", "multiplies number of particles and reduces their alpha"};
98 cvar_t cl_particles_size = {CVAR_SAVE, "cl_particles_size", "1", "multiplies particle size"};
99 cvar_t cl_particles_quake = {CVAR_SAVE, "cl_particles_quake", "0", "makes particle effects look mostly like the ones in Quake"};
100 cvar_t cl_particles_bloodshowers = {CVAR_SAVE, "cl_particles_bloodshowers", "1", "enables blood shower effects"};
101 cvar_t cl_particles_blood = {CVAR_SAVE, "cl_particles_blood", "1", "enables blood effects"};
102 cvar_t cl_particles_blood_alpha = {CVAR_SAVE, "cl_particles_blood_alpha", "0.5", "opacity of blood"};
103 cvar_t cl_particles_blood_bloodhack = {CVAR_SAVE, "cl_particles_blood_bloodhack", "1", "make certain quake particle() calls create blood effects instead"};
104 cvar_t cl_particles_bulletimpacts = {CVAR_SAVE, "cl_particles_bulletimpacts", "1", "enables bulletimpact effects"};
105 cvar_t cl_particles_explosions_bubbles = {CVAR_SAVE, "cl_particles_explosions_bubbles", "1", "enables bubbles from underwater explosions"};
106 cvar_t cl_particles_explosions_smoke = {CVAR_SAVE, "cl_particles_explosions_smokes", "0", "enables smoke from explosions"};
107 cvar_t cl_particles_explosions_sparks = {CVAR_SAVE, "cl_particles_explosions_sparks", "1", "enables sparks from explosions"};
108 cvar_t cl_particles_explosions_shell = {CVAR_SAVE, "cl_particles_explosions_shell", "0", "enables polygonal shell from explosions"};
109 cvar_t cl_particles_smoke = {CVAR_SAVE, "cl_particles_smoke", "1", "enables smoke (used by multiple effects)"};
110 cvar_t cl_particles_smoke_alpha = {CVAR_SAVE, "cl_particles_smoke_alpha", "0.5", "smoke brightness"};
111 cvar_t cl_particles_smoke_alphafade = {CVAR_SAVE, "cl_particles_smoke_alphafade", "0.55", "brightness fade per second"};
112 cvar_t cl_particles_sparks = {CVAR_SAVE, "cl_particles_sparks", "1", "enables sparks (used by multiple effects)"};
113 cvar_t cl_particles_bubbles = {CVAR_SAVE, "cl_particles_bubbles", "1", "enables bubbles (used by multiple effects)"};
114 cvar_t cl_decals = {CVAR_SAVE, "cl_decals", "0", "enables decals (bullet holes, blood, etc)"};
115 cvar_t cl_decals_time = {CVAR_SAVE, "cl_decals_time", "0", "how long before decals start to fade away"};
116 cvar_t cl_decals_fadetime = {CVAR_SAVE, "cl_decals_fadetime", "20", "how long decals take to fade away"};
123 void CL_ReadPointFile_f (void);
124 void CL_Particles_Init (void)
126 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)");
128 Cvar_RegisterVariable (&cl_particles);
129 Cvar_RegisterVariable (&cl_particles_quality);
130 Cvar_RegisterVariable (&cl_particles_size);
131 Cvar_RegisterVariable (&cl_particles_quake);
132 Cvar_RegisterVariable (&cl_particles_bloodshowers);
133 Cvar_RegisterVariable (&cl_particles_blood);
134 Cvar_RegisterVariable (&cl_particles_blood_alpha);
135 Cvar_RegisterVariable (&cl_particles_blood_bloodhack);
136 Cvar_RegisterVariable (&cl_particles_explosions_bubbles);
137 Cvar_RegisterVariable (&cl_particles_explosions_smoke);
138 Cvar_RegisterVariable (&cl_particles_explosions_sparks);
139 Cvar_RegisterVariable (&cl_particles_explosions_shell);
140 Cvar_RegisterVariable (&cl_particles_bulletimpacts);
141 Cvar_RegisterVariable (&cl_particles_smoke);
142 Cvar_RegisterVariable (&cl_particles_smoke_alpha);
143 Cvar_RegisterVariable (&cl_particles_smoke_alphafade);
144 Cvar_RegisterVariable (&cl_particles_sparks);
145 Cvar_RegisterVariable (&cl_particles_bubbles);
146 Cvar_RegisterVariable (&cl_decals);
147 Cvar_RegisterVariable (&cl_decals_time);
148 Cvar_RegisterVariable (&cl_decals_fadetime);
151 void CL_Particles_Shutdown (void)
155 // list of all 26 parameters:
156 // ptype - any of the pt_ enum values (pt_static, pt_blood, etc), see ptype_t near the top of this file
157 // pcolor1,pcolor2 - minimum and maximum ranges of color, randomly interpolated to decide particle color
158 // ptex - any of the tex_ values such as tex_smoke[rand()&7] or tex_particle
159 // psize - size of particle (or thickness for PARTICLE_SPARK and PARTICLE_BEAM)
160 // palpha - opacity of particle as 0-255 (can be more than 255)
161 // palphafade - rate of fade per second (so 256 would mean a 256 alpha particle would fade to nothing in 1 second)
162 // ptime - how long the particle can live (note it is also removed if alpha drops to nothing)
163 // pgravity - how much effect gravity has on the particle (0-1)
164 // 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
165 // px,py,pz - starting origin of particle
166 // pvx,pvy,pvz - starting velocity of particle
167 // pfriction - how much the particle slows down per second (0-1 typically, can slowdown faster than 1)
168 particle_t *particle(particletype_t *ptype, int pcolor1, int pcolor2, int ptex, float psize, float palpha, float palphafade, float pgravity, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz, float pfriction, float originjitter, float velocityjitter)
173 for (;cl.free_particle < cl.max_particles && cl.particles[cl.free_particle].type;cl.free_particle++);
174 if (cl.free_particle >= cl.max_particles)
176 part = &cl.particles[cl.free_particle++];
177 if (cl.num_particles < cl.free_particle)
178 cl.num_particles = cl.free_particle;
179 memset(part, 0, sizeof(*part));
181 l2 = (int)lhrandom(0.5, 256.5);
183 part->color[0] = ((((pcolor1 >> 16) & 0xFF) * l1 + ((pcolor2 >> 16) & 0xFF) * l2) >> 8) & 0xFF;
184 part->color[1] = ((((pcolor1 >> 8) & 0xFF) * l1 + ((pcolor2 >> 8) & 0xFF) * l2) >> 8) & 0xFF;
185 part->color[2] = ((((pcolor1 >> 0) & 0xFF) * l1 + ((pcolor2 >> 0) & 0xFF) * l2) >> 8) & 0xFF;
186 part->color[3] = 0xFF;
189 part->alpha = palpha;
190 part->alphafade = palphafade;
191 part->gravity = pgravity;
192 part->bounce = pbounce;
194 part->org[0] = px + originjitter * v[0];
195 part->org[1] = py + originjitter * v[1];
196 part->org[2] = pz + originjitter * v[2];
197 part->vel[0] = pvx + velocityjitter * v[0];
198 part->vel[1] = pvy + velocityjitter * v[1];
199 part->vel[2] = pvz + velocityjitter * v[2];
201 part->friction = pfriction;
205 void CL_SpawnDecalParticleForSurface(int hitent, const vec3_t org, const vec3_t normal, int color1, int color2, int texnum, float size, float alpha)
208 if (!cl_decals.integer)
210 p = particle(particletype + pt_decal, color1, color2, texnum, size, alpha, 0, 0, 0, org[0] + normal[0], org[1] + normal[1], org[2] + normal[2], normal[0], normal[1], normal[2], 0, 0, 0);
215 p->ownermodel = cl.entities[p->owner].render.model;
216 Matrix4x4_Transform(&cl.entities[p->owner].render.inversematrix, org, p->relativeorigin);
217 Matrix4x4_Transform3x3(&cl.entities[p->owner].render.inversematrix, normal, p->relativedirection);
218 VectorAdd(p->relativeorigin, p->relativedirection, p->relativeorigin);
222 void CL_SpawnDecalParticleForPoint(const vec3_t org, float maxdist, float size, float alpha, int texnum, int color1, int color2)
225 float bestfrac, bestorg[3], bestnormal[3];
227 int besthitent = 0, hitent;
230 for (i = 0;i < 32;i++)
233 VectorMA(org, maxdist, org2, org2);
234 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, &hitent, SUPERCONTENTS_SOLID, false);
235 if (bestfrac > trace.fraction)
237 bestfrac = trace.fraction;
239 VectorCopy(trace.endpos, bestorg);
240 VectorCopy(trace.plane.normal, bestnormal);
244 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
252 void CL_EntityParticles (entity_t *ent)
255 float pitch, yaw, dist = 64, beamlength = 16, org[3], v[3];
256 static vec3_t avelocities[NUMVERTEXNORMALS];
257 if (!cl_particles.integer) return;
259 Matrix4x4_OriginFromMatrix(&ent->render.matrix, org);
261 if (!avelocities[0][0])
262 for (i = 0;i < NUMVERTEXNORMALS * 3;i++)
263 avelocities[0][i] = lhrandom(0, 2.55);
265 for (i = 0;i < NUMVERTEXNORMALS;i++)
267 yaw = cl.time * avelocities[i][0];
268 pitch = cl.time * avelocities[i][1];
269 v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
270 v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
271 v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
272 particle(particletype + pt_entityparticle, particlepalette[0x6f], particlepalette[0x6f], tex_particle, 1, 255, 0, 0, 0, v[0], v[1], v[2], 0, 0, 0, 0, 0, 0);
277 void CL_ReadPointFile_f (void)
281 char *pointfile = NULL, *pointfilepos, *t, tchar;
282 char name[MAX_OSPATH];
287 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
288 strlcat (name, ".pts", sizeof (name));
289 pointfile = (char *)FS_LoadFile(name, tempmempool, true, NULL);
292 Con_Printf("Could not open %s\n", name);
296 Con_Printf("Reading %s...\n", name);
297 VectorClear(leakorg);
300 pointfilepos = pointfile;
301 while (*pointfilepos)
303 while (*pointfilepos == '\n' || *pointfilepos == '\r')
308 while (*t && *t != '\n' && *t != '\r')
312 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
318 VectorCopy(org, leakorg);
321 if (cl.num_particles < cl.max_particles - 3)
324 particle(particletype + pt_static, particlepalette[(-c)&15], particlepalette[(-c)&15], tex_particle, 2, 255, 0, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0, 0, 0);
328 VectorCopy(leakorg, org);
329 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
331 particle(particletype + pt_beam, 0xFF0000, 0xFF0000, tex_beam, 64, 255, 0, 0, 0, org[0] - 4096, org[1], org[2], org[0] + 4096, org[1], org[2], 0, 0, 0);
332 particle(particletype + pt_beam, 0x00FF00, 0x00FF00, tex_beam, 64, 255, 0, 0, 0, org[0], org[1] - 4096, org[2], org[0], org[1] + 4096, org[2], 0, 0, 0);
333 particle(particletype + pt_beam, 0x0000FF, 0x0000FF, tex_beam, 64, 255, 0, 0, 0, org[0], org[1], org[2] - 4096, org[0], org[1], org[2] + 4096, 0, 0, 0);
338 CL_ParseParticleEffect
340 Parse an effect out of the server message
343 void CL_ParseParticleEffect (void)
346 int i, count, msgcount, color;
348 MSG_ReadVector(org, cls.protocol);
349 for (i=0 ; i<3 ; i++)
350 dir[i] = MSG_ReadChar ();
351 msgcount = MSG_ReadByte ();
352 color = MSG_ReadByte ();
359 if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer)
364 CL_BloodPuff(org, dir, count / 2);
370 CL_BloodPuff(org, dir, count / 2);
374 CL_RunParticleEffect (org, dir, color, count);
383 void CL_ParticleExplosion (vec3_t org)
389 if (cl_stainmaps.integer)
390 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
391 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
393 if (cl_particles_quake.integer)
395 for (i = 0;i < 1024;i++)
401 color = particlepalette[ramp1[r]];
402 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, 32 * (8 - r), 318, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, 16, 256);
406 color = particlepalette[ramp2[r]];
407 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, 32 * (8 - r), 478, 0, 0, org[0], org[1], org[2], 0, 0, 0, 1, 16, 256);
413 i = CL_PointSuperContents(org);
414 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
416 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
417 for (i = 0;i < 128 * cl_particles_quality.value;i++)
418 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, lhrandom(128, 255), 128, -0.125, 1.5, org[0], org[1], org[2], 0, 0, 0, (1.0 / 16.0), 16, 96);
422 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
424 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
426 for (i = 0;i < 32;i++)
430 for (k = 0;k < 16;k++)
432 v[0] = org[0] + lhrandom(-48, 48);
433 v[1] = org[1] + lhrandom(-48, 48);
434 v[2] = org[2] + lhrandom(-48, 48);
435 trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
436 if (trace.fraction >= 0.1)
439 VectorSubtract(trace.endpos, org, v2);
440 VectorScale(v2, 2.0f, v2);
441 particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 12, 32, 64, 0, 0, org[0], org[1], org[2], v2[0], v2[1], v2[2], 0, 0, 0);
445 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
446 for (i = 0;i < 128 * cl_particles_quality.value;i++)
447 particle(particletype + pt_spark, 0x903010, 0xFFD030, tex_particle, 1.0f, lhrandom(0, 255), 512, 1, 0, org[0], org[1], org[2], 0, 0, 80, 0.2, 0, 256);
451 if (cl_particles_explosions_shell.integer)
457 CL_ParticleExplosion2
461 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
464 if (!cl_particles.integer) return;
466 for (i = 0;i < 512 * cl_particles_quality.value;i++)
468 k = particlepalette[colorStart + (i % colorLength)];
469 if (cl_particles_quake.integer)
470 particle(particletype + pt_static, k, k, tex_particle, 1, 255, 850, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, 8, 256);
472 particle(particletype + pt_static, k, k, tex_particle, lhrandom(0.5, 1.5), 255, 512, 0, 0, org[0], org[1], org[2], 0, 0, 0, lhrandom(1.5, 3), 8, 192);
482 void CL_BlobExplosion (vec3_t org)
485 if (!cl_particles.integer) return;
487 if (!cl_particles_quake.integer)
489 CL_ParticleExplosion(org);
493 for (i = 0;i < 1024 * cl_particles_quality.value;i++)
497 k = particlepalette[66 + rand()%6];
498 particle(particletype + pt_static, k, k, tex_particle, 1, lhrandom(182, 255), 182, 0, 0, org[0], org[1], org[2], 0, 0, 0, -4, 16, 256);
502 k = particlepalette[150 + rand()%6];
503 particle(particletype + pt_static, k, k, tex_particle, 1, lhrandom(182, 255), 182, 0, 0, org[0], org[1], org[2], 0, 0, lhrandom(-256, 256), 0, 16, 0);
514 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
520 CL_ParticleExplosion(org);
523 if (!cl_particles.integer) return;
524 if (cl_particles_quake.integer)
526 count *= cl_particles_quality.value;
529 k = particlepalette[color + (rand()&7)];
530 particle(particletype + pt_alphastatic, k, k, tex_particle, 1, lhrandom(51, 255), 512, 0, 0.05, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 8, 0);
535 count *= cl_particles_quality.value;
538 k = particlepalette[color + (rand()&7)];
539 if (gamemode == GAME_GOODVSBAD2)
540 particle(particletype + pt_alphastatic, k, k, tex_particle, 5, 255, 300, 0, 0, org[0], org[1], org[2], 0, 0, 0, 0, 8, 10);
542 particle(particletype + pt_alphastatic, k, k, tex_particle, 1, 255, 512, 0, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 8, 15);
547 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
553 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale, vec_t radius)
557 if (!cl_particles.integer) return;
559 if (cl_particles_sparks.integer)
562 count *= cl_particles_quality.value;
565 k = particlepalette[0x68 + (rand() & 7)];
566 particle(particletype + pt_spark, k, k, tex_particle, 0.4f, lhrandom(64, 255), 512, gravityscale, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2] + sv_gravity.value * 0.1, 0, radius, 64);
571 void CL_Smoke (vec3_t org, vec3_t dir, int count, vec_t radius)
577 if (!cl_particles.integer) return;
580 if (cl_particles_smoke.integer)
582 k = count * 0.25 * cl_particles_quality.value;
585 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
586 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
587 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
588 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
589 particle(particletype + pt_smoke, 0x101010, 0x202020, tex_smoke[rand()&7], 3, 255, 1024, 0, 0, trace.endpos[0], trace.endpos[1], trace.endpos[2], 0, 0, 0, 0, radius, 8);
594 void CL_BulletMark (vec3_t org)
596 if (cl_stainmaps.integer)
597 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
598 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
601 void CL_PlasmaBurn (vec3_t org)
603 if (cl_stainmaps.integer)
604 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
605 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
608 static float bloodcount = 0;
609 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
614 // bloodcount is used to accumulate counts too small to cause a blood particle
615 if (!cl_particles.integer) return;
616 if (cl_particles_quake.integer)
618 CL_RunParticleEffect(org, vel, 73, count * 2);
621 if (!cl_particles_blood.integer) return;
627 bloodcount += count * cl_particles_quality.value;
628 while(bloodcount > 0)
630 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
631 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
632 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
633 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
634 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768, cl_particles_blood_alpha.value * 384, 0, -1, trace.endpos[0], trace.endpos[1], trace.endpos[2], vel[0], vel[1], vel[2], 1, 0, s);
639 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
641 vec3_t org, vel, diff, center, velscale;
642 if (!cl_particles.integer) return;
643 if (!cl_particles_bloodshowers.integer) return;
644 if (!cl_particles_blood.integer) return;
646 VectorSubtract(maxs, mins, diff);
647 center[0] = (mins[0] + maxs[0]) * 0.5;
648 center[1] = (mins[1] + maxs[1]) * 0.5;
649 center[2] = (mins[2] + maxs[2]) * 0.5;
650 velscale[0] = velspeed * 2.0 / diff[0];
651 velscale[1] = velspeed * 2.0 / diff[1];
652 velscale[2] = velspeed * 2.0 / diff[2];
654 bloodcount += count * 5.0f * cl_particles_quality.value;
655 while (bloodcount > 0)
657 org[0] = lhrandom(mins[0], maxs[0]);
658 org[1] = lhrandom(mins[1], maxs[1]);
659 org[2] = lhrandom(mins[2], maxs[2]);
660 vel[0] = (org[0] - center[0]) * velscale[0];
661 vel[1] = (org[1] - center[1]) * velscale[1];
662 vel[2] = (org[2] - center[2]) * velscale[2];
664 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, cl_particles_blood_alpha.value * 768, cl_particles_blood_alpha.value * 384, 0, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 1, 0, 0);
668 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
672 if (!cl_particles.integer) return;
673 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
674 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
675 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
677 count *= cl_particles_quality.value;
680 k = particlepalette[colorbase + (rand()&3)];
681 particle(particletype + pt_alphastatic, k, k, tex_particle, 2, 255, 128, gravity ? 1 : 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0], dir[1], dir[2], 0, 0, randomvel);
685 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
688 float t, z, minz, maxz;
690 if (!cl_particles.integer) return;
691 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
692 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
693 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
694 if (dir[2] < 0) // falling
699 minz = z - fabs(dir[2]) * 0.1;
700 maxz = z + fabs(dir[2]) * 0.1;
701 minz = bound(mins[2], minz, maxs[2]);
702 maxz = bound(mins[2], maxz, maxs[2]);
704 count *= cl_particles_quality.value;
709 count *= 4; // ick, this should be in the mod or maps?
713 k = particlepalette[colorbase + (rand()&3)];
714 if (gamemode == GAME_GOODVSBAD2)
715 particle(particletype + pt_rain, k, k, tex_particle, 20, lhrandom(8, 16), 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);
717 particle(particletype + pt_rain, k, k, tex_particle, 0.5, lhrandom(8, 16), 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);
723 k = particlepalette[colorbase + (rand()&3)];
724 if (gamemode == GAME_GOODVSBAD2)
725 p = particle(particletype + pt_snow, k, k, tex_particle, 20, 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);
727 p = particle(particletype + pt_snow, k, k, tex_particle, 1, 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);
729 VectorCopy(p->vel, p->relativedirection);
733 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
737 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
742 if (!cl_particles.integer) return;
744 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
745 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
746 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
748 center[0] = (mins[0] + maxs[0]) * 0.5f;
749 center[1] = (mins[1] + maxs[1]) * 0.5f;
750 center[2] = (mins[2] + maxs[2]) * 0.5f;
752 count *= cl_particles_quality.value;
755 k = particlepalette[224 + (rand()&15)];
756 o[0] = lhrandom(mins[0], maxs[0]);
757 o[1] = lhrandom(mins[1], maxs[1]);
758 o[2] = lhrandom(mins[2], maxs[2]);
759 VectorSubtract(o, center, v);
761 VectorScale(v, 100, v);
762 v[2] += sv_gravity.value * 0.15f;
763 particle(particletype + pt_static, 0x903010, 0xFFD030, tex_particle, 1.5, lhrandom(64, 128), 128, 1, 0, o[0], o[1], o[2], v[0], v[1], v[2], 0.2, 0, 0);
767 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
771 if (!cl_particles.integer) return;
772 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
773 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
774 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
776 count *= cl_particles_quality.value;
779 k = particlepalette[224 + (rand()&15)];
780 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128), 384, -1, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), 0, 0, 32, 1, 0, 32);
782 particle(particletype + pt_static, 0x303030, 0x606060, tex_smoke[rand()&7], 6, lhrandom(48, 96), 64, 0, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), 0, 0, 24, 0, 0, 8);
786 void CL_Flames (vec3_t org, vec3_t vel, int count)
789 if (!cl_particles.integer) return;
791 count *= cl_particles_quality.value;
794 k = particlepalette[224 + (rand()&15)];
795 particle(particletype + pt_static, k, k, tex_particle, 4, lhrandom(64, 128), 384, -1, 1.1, org[0], org[1], org[2], vel[0], vel[1], vel[2], 1, 0, 128);
807 void CL_LavaSplash (vec3_t origin)
809 float i, j, inc, vel;
812 if (!cl_particles.integer) return;
814 if (cl_particles_quake.integer)
816 inc = 8 / cl_particles_quality.value;
817 for (i = -128;i < 128;i += inc)
819 for (j = -128;j < 128;j += inc)
821 dir[0] = j + lhrandom(0, inc);
822 dir[1] = i + lhrandom(0, inc);
824 org[0] = origin[0] + dir[0];
825 org[1] = origin[1] + dir[1];
826 org[2] = origin[2] + lhrandom(0, 64);
827 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
828 k = l = particlepalette[224 + (rand()&7)];
829 particle(particletype + pt_alphastatic, k, l, tex_particle, 1, inc * lhrandom(24, 32), inc * 12, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0);
835 inc = 32 / cl_particles_quality.value;
836 for (i = -128;i < 128;i += inc)
838 for (j = -128;j < 128;j += inc)
840 dir[0] = j + lhrandom(0, inc);
841 dir[1] = i + lhrandom(0, inc);
843 org[0] = origin[0] + dir[0];
844 org[1] = origin[1] + dir[1];
845 org[2] = origin[2] + lhrandom(0, 64);
846 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
847 if (gamemode == GAME_GOODVSBAD2)
849 k = particlepalette[0 + (rand()&255)];
850 l = particlepalette[0 + (rand()&255)];
851 particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 1, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0);
855 k = l = particlepalette[224 + (rand()&7)];
856 particle(particletype + pt_static, k, l, tex_particle, 12, inc * 8, inc * 8, 0.05, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0);
869 void CL_TeleportSplash (vec3_t org)
872 if (!cl_particles.integer) return;
874 if (cl_particles_quake.integer)
876 inc = 4 / cl_particles_quality.value;
877 for (i = -16;i < 16;i += inc)
879 for (j = -16;j < 16;j += inc)
881 for (k = -24;k < 32;k += inc)
885 VectorSet(dir, i*8, j*8, k*8);
886 VectorNormalize(dir);
887 vel = lhrandom(50, 113);
888 particle(particletype + pt_alphastatic, particlepalette[7], particlepalette[14], tex_particle, 1, inc * lhrandom(37, 63), inc * 187, 0, 0, org[0] + i + lhrandom(0, inc), org[1] + j + lhrandom(0, inc), org[2] + k + lhrandom(0, inc), dir[0] * vel, dir[1] * vel, dir[2] * vel, 0, 0, 0);
895 inc = 8 / cl_particles_quality.value;
896 for (i = -16;i < 16;i += inc)
897 for (j = -16;j < 16;j += inc)
898 for (k = -24;k < 32;k += inc)
899 particle(particletype + pt_static, 0xA0A0A0, 0xFFFFFF, tex_particle, 10, inc * lhrandom(8, 16), inc * 32, 0, 0, org[0] + i + lhrandom(0, inc), org[1] + j + lhrandom(0, inc), org[2] + k + lhrandom(0, inc), 0, 0, lhrandom(-256, 256), 1, 0, 0);
903 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
905 vec3_t vec, dir, vel, pos;
906 float len, dec, speed, qd;
907 int smoke, blood, bubbles, r;
909 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
912 VectorSubtract(end, start, dir);
913 VectorNormalize(dir);
915 VectorSubtract (end, start, vec);
916 len = VectorNormalizeLength (vec);
917 dec = -ent->persistent.trail_time;
918 ent->persistent.trail_time += len;
919 if (ent->persistent.trail_time < 0.01f)
922 // if we skip out, leave it reset
923 ent->persistent.trail_time = 0.0f;
925 speed = ent->state_current.time - ent->state_previous.time;
927 speed = 1.0f / speed;
928 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
929 color = particlepalette[color];
930 VectorScale(vel, speed, vel);
932 // advance into this frame to reach the first puff location
933 VectorMA(start, dec, vec, pos);
936 smoke = cl_particles.integer && cl_particles_smoke.integer;
937 blood = cl_particles.integer && cl_particles_blood.integer;
938 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
939 qd = 1.0f / cl_particles_quality.value;
945 case 0: // rocket trail
946 if (cl_particles_quake.integer)
950 color = particlepalette[ramp3[r]];
951 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, 42*(6-r), 306, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
958 particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, 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);
959 particle(particletype + pt_static, 0x801010, 0xFFA020, tex_smoke[rand()&7], 3, 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, 20);
962 particle(particletype + pt_bubble, 0x404040, 0x808080, tex_bubble, 2, lhrandom(64, 255), 256, -0.25, 1.5, pos[0], pos[1], pos[2], 0, 0, 0, (1.0 / 16.0), 0, 16);
966 case 1: // grenade trail
967 if (cl_particles_quake.integer)
971 color = particlepalette[ramp3[r]];
972 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, 42*(6-r), 306, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
978 particle(particletype + pt_smoke, 0x303030, 0x606060, tex_smoke[rand()&7], 3, cl_particles_smoke_alpha.value*50, cl_particles_smoke_alphafade.value*50, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
984 case 4: // slight blood
985 if (cl_particles_quake.integer)
990 color = particlepalette[67 + (rand()&3)];
991 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, 255, 128, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
996 color = particlepalette[67 + (rand()&3)];
997 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, 255, 128, 0, -0.05, pos[0], pos[1], pos[2], 0, 0, 0, 0, 3, 0);
1004 particle(particletype + pt_blood, 0xFFFFFF, 0xFFFFFF, tex_bloodparticle[rand()&7], 8, qd * cl_particles_blood_alpha.value * 768.0f, qd * cl_particles_blood_alpha.value * 384.0f, 0, -1, pos[0], pos[1], pos[2], vel[0] * 0.5f, vel[1] * 0.5f, vel[2] * 0.5f, 1, 0, 64);
1008 case 3: // green tracer
1009 if (cl_particles_quake.integer)
1012 color = particlepalette[52 + (rand()&7)];
1013 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*vec[1], 30*-vec[0], 0, 0, 0, 0);
1014 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*-vec[1], 30*vec[0], 0, 0, 0, 0);
1021 if (gamemode == GAME_GOODVSBAD2)
1024 particle(particletype + pt_static, 0x00002E, 0x000030, tex_particle, 6, 128, 384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1029 color = particlepalette[20 + (rand()&7)];
1030 particle(particletype + pt_static, color, color, tex_particle, 2, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1036 case 5: // flame tracer
1037 if (cl_particles_quake.integer)
1040 color = particlepalette[230 + (rand()&7)];
1041 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*vec[1], 30*-vec[0], 0, 0, 0, 0);
1042 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, 255, 512, 0, 0, pos[0], pos[1], pos[2], 30*-vec[1], 30*vec[0], 0, 0, 0, 0);
1049 color = particlepalette[226 + (rand()&7)];
1050 particle(particletype + pt_static, color, color, tex_particle, 2, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1055 case 6: // voor trail
1056 if (cl_particles_quake.integer)
1059 color = particlepalette[152 + (rand()&3)];
1060 particle(particletype + pt_alphastatic, color, color, tex_particle, 1, 255, 850, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 8, 0);
1067 if (gamemode == GAME_GOODVSBAD2)
1070 particle(particletype + pt_alphastatic, particlepalette[0 + (rand()&255)], particlepalette[0 + (rand()&255)], tex_particle, 6, 255, 384, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1072 else if (gamemode == GAME_PRYDON)
1075 particle(particletype + pt_static, 0x103040, 0x204050, tex_particle, 6, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1080 particle(particletype + pt_static, 0x502030, 0x502030, tex_particle, 3, 64, 192, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1085 case 7: // Nehahra smoke tracer
1088 particle(particletype + pt_alphastatic, 0x303030, 0x606060, tex_smoke[rand()&7], 7, 64, 320, 0, 0, pos[0], pos[1], pos[2], 0, 0, lhrandom(4, 12), 0, 0, 4);
1090 case 8: // Nexuiz plasma trail
1093 particle(particletype + pt_static, 0x283880, 0x283880, tex_particle, 4, 255, 1024, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 16);
1095 case 9: // glow trail
1098 particle(particletype + pt_alphastatic, color, color, tex_particle, 5, 128, 320, 0, 0, pos[0], pos[1], pos[2], 0, 0, 0, 0, 0, 0);
1101 Sys_Error("CL_RocketTrail: unknown trail type %i", type);
1104 // advance to next time and position
1107 VectorMA (pos, dec, vec, pos);
1109 ent->persistent.trail_time = len;
1112 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1114 int tempcolor2, cr, cg, cb;
1118 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1119 particle(particletype + pt_beam, tempcolor2, tempcolor2, tex_beam, radius, alpha * 255, alpha * 255 / lifetime, 0, 0, start[0], start[1], start[2], end[0], end[1], end[2], 0, 0, 0);
1122 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1125 if (!cl_particles.integer) return;
1128 if (cl_particles_smoke.integer)
1129 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1130 particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255, 512, 0, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, count * 0.125f, count * 0.5f);
1133 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1136 if (!cl_particles.integer) return;
1138 if (cl_stainmaps.integer)
1139 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1140 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1143 if (cl_particles_smoke.integer)
1144 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1145 particle(particletype + pt_smoke, 0x202020, 0x404040, tex_smoke[rand()&7], 5, 255, 512, 0, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, count * 0.125f, count);
1148 if (cl_particles_sparks.integer)
1149 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1150 particle(particletype + pt_spark, 0x2030FF, 0x80C0FF, tex_particle, 2.0f, lhrandom(64, 255), 512, 0, 0, org[0], org[1], org[2], dir[0], dir[1], dir[2], 0, 0, count * 3.0f);
1158 void CL_MoveParticles (void)
1161 int i, maxparticle, j, a, content;
1162 float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
1166 // LordHavoc: early out condition
1167 if (!cl.num_particles)
1169 cl.free_particle = 0;
1173 frametime = cl.time - cl.oldtime;
1174 gravity = frametime * sv_gravity.value;
1175 dvel = 1+4*frametime;
1176 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1180 for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++)
1187 p->alpha -= p->alphafade * frametime;
1195 if (p->type->orientation != PARTICLE_BEAM)
1197 VectorCopy(p->org, oldorg);
1198 VectorMA(p->org, frametime, p->vel, p->org);
1199 VectorCopy(p->org, org);
1202 if (p->type == particletype + pt_rain)
1204 // raindrop - splash on solid/water/slime/lava
1205 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK, false);
1206 if (trace.fraction < 1)
1209 // convert from a raindrop particle to a rainsplash decal
1210 VectorCopy(trace.endpos, p->org);
1211 VectorCopy(trace.plane.normal, p->vel);
1212 VectorAdd(p->org, p->vel, p->org);
1213 p->type = particletype + pt_raindecal;
1214 p->texnum = tex_rainsplash[0];
1216 p->alphafade = p->alpha / 0.4;
1223 particle(particletype + pt_spark, 0x000000, 0x707070, tex_particle, 0.25f, lhrandom(64, 255), 512, 1, 0, p->org[0], p->org[1], p->org[2], p->vel[0]*16, p->vel[1]*16, 32 + p->vel[2]*16, 0, 0, 32);
1226 else if (p->type == particletype + pt_blood)
1228 // blood - splash on solid
1229 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID, false);
1230 if (trace.fraction < 1)
1232 // convert from a blood particle to a blood decal
1233 VectorCopy(trace.endpos, p->org);
1234 VectorCopy(trace.plane.normal, p->vel);
1235 VectorAdd(p->org, p->vel, p->org);
1236 if (cl_stainmaps.integer)
1237 R_Stain(p->org, 32, 32, 16, 16, p->alpha * p->size * (1.0f / 40.0f), 192, 48, 48, p->alpha * p->size * (1.0f / 40.0f));
1238 if (!cl_decals.integer)
1244 p->type = particletype + pt_decal;
1245 p->texnum = tex_blooddecal[rand()&7];
1247 p->ownermodel = cl.entities[hitent].render.model;
1248 Matrix4x4_Transform(&cl.entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1249 Matrix4x4_Transform3x3(&cl.entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1260 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, NULL, SUPERCONTENTS_SOLID, false);
1261 if (trace.fraction < 1)
1263 VectorCopy(trace.endpos, p->org);
1271 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1272 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1273 if (DotProduct(p->vel, p->vel) < 0.03)
1274 VectorClear(p->vel);
1279 p->vel[2] -= p->gravity * gravity;
1283 f = p->friction * frametime;
1284 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1287 VectorScale(p->vel, f, p->vel);
1291 if (p->type != particletype + pt_static)
1293 switch (p->type - particletype)
1295 case pt_entityparticle:
1296 // particle that removes itself after one rendered frame
1303 a = CL_PointSuperContents(p->org);
1304 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1306 p->size += frametime * 8;
1307 //p->alpha -= bloodwaterfade;
1310 p->vel[2] -= gravity;
1311 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1315 a = CL_PointSuperContents(p->org);
1316 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1323 a = CL_PointSuperContents(p->org);
1324 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1328 if (cl.time > p->time2)
1331 p->time2 = cl.time + (rand() & 3) * 0.1;
1332 p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
1333 p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1334 //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
1336 a = CL_PointSuperContents(p->org);
1337 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LIQUIDSMASK))
1341 //p->size += frametime * 15;
1344 // FIXME: this has fairly wacky handling of alpha
1345 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1346 if (cl.entities[p->owner].render.model == p->ownermodel)
1348 Matrix4x4_Transform(&cl.entities[p->owner].render.matrix, p->relativeorigin, p->org);
1349 Matrix4x4_Transform3x3(&cl.entities[p->owner].render.matrix, p->relativedirection, p->vel);
1355 a = max(0, (cl.time - p->time2) * 40);
1357 p->texnum = tex_rainsplash[a];
1366 cl.num_particles = maxparticle + 1;
1367 cl.free_particle = 0;
1370 #define MAX_PARTICLETEXTURES 64
1371 // particletexture_t is a rectangle in the particlefonttexture
1372 typedef struct particletexture_s
1374 rtexture_t *texture;
1375 float s1, t1, s2, t2;
1379 static rtexturepool_t *particletexturepool;
1380 static rtexture_t *particlefonttexture;
1381 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1383 static cvar_t r_drawparticles = {0, "r_drawparticles", "1", "enables drawing of particles"};
1385 #define PARTICLETEXTURESIZE 64
1386 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1388 static unsigned char shadebubble(float dx, float dy, vec3_t light)
1392 dz = 1 - (dx*dx+dy*dy);
1393 if (dz > 0) // it does hit the sphere
1397 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1398 VectorNormalize(normal);
1399 dot = DotProduct(normal, light);
1400 if (dot > 0.5) // interior reflection
1401 f += ((dot * 2) - 1);
1402 else if (dot < -0.5) // exterior reflection
1403 f += ((dot * -2) - 1);
1405 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1406 VectorNormalize(normal);
1407 dot = DotProduct(normal, light);
1408 if (dot > 0.5) // interior reflection
1409 f += ((dot * 2) - 1);
1410 else if (dot < -0.5) // exterior reflection
1411 f += ((dot * -2) - 1);
1413 f += 16; // just to give it a haze so you can see the outline
1414 f = bound(0, f, 255);
1415 return (unsigned char) f;
1421 static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
1423 int basex, basey, y;
1424 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1425 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1426 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1427 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1428 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1429 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1430 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1431 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1434 void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
1437 float cx, cy, dx, dy, f, iradius;
1439 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1440 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1441 iradius = 1.0f / radius;
1442 alpha *= (1.0f / 255.0f);
1443 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1445 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1449 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1452 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1453 d[0] += f * (red - d[0]);
1454 d[1] += f * (green - d[1]);
1455 d[2] += f * (blue - d[2]);
1461 void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1464 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1466 data[0] = bound(minr, data[0], maxr);
1467 data[1] = bound(ming, data[1], maxg);
1468 data[2] = bound(minb, data[2], maxb);
1472 void particletextureinvert(unsigned char *data)
1475 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1477 data[0] = 255 - data[0];
1478 data[1] = 255 - data[1];
1479 data[2] = 255 - data[2];
1483 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1484 static void R_InitBloodTextures (unsigned char *particletexturedata)
1487 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1490 for (i = 0;i < 8;i++)
1492 memset(&data[0][0][0], 255, sizeof(data));
1493 for (k = 0;k < 24;k++)
1494 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1495 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1496 particletextureinvert(&data[0][0][0]);
1497 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1501 for (i = 0;i < 8;i++)
1503 memset(&data[0][0][0], 255, sizeof(data));
1505 for (j = 1;j < 10;j++)
1506 for (k = min(j, m - 1);k < m;k++)
1507 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1508 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1509 particletextureinvert(&data[0][0][0]);
1510 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1515 static void R_InitParticleTexture (void)
1517 int x, y, d, i, k, m;
1518 float dx, dy, radius, f, f2;
1519 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1521 unsigned char *particletexturedata;
1523 // a note: decals need to modulate (multiply) the background color to
1524 // properly darken it (stain), and they need to be able to alpha fade,
1525 // this is a very difficult challenge because it means fading to white
1526 // (no change to background) rather than black (darkening everything
1527 // behind the whole decal polygon), and to accomplish this the texture is
1528 // inverted (dark red blood on white background becomes brilliant cyan
1529 // and white on black background) so we can alpha fade it to black, then
1530 // we invert it again during the blendfunc to make it work...
1532 particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1533 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1536 for (i = 0;i < 8;i++)
1538 memset(&data[0][0][0], 255, sizeof(data));
1541 unsigned char noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1543 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1544 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1546 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1548 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1549 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1551 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1552 d = (noise2[y][x] - 128) * 3 + 192;
1554 d = d * (1-(dx*dx+dy*dy));
1555 d = (d * noise1[y][x]) >> 7;
1556 d = bound(0, d, 255);
1557 data[y][x][3] = (unsigned char) d;
1564 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1568 for (i = 0;i < 16;i++)
1570 memset(&data[0][0][0], 255, sizeof(data));
1571 radius = i * 3.0f / 4.0f / 16.0f;
1572 f2 = 255.0f * ((15.0f - i) / 15.0f);
1573 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1575 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1576 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1578 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1579 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1580 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1583 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1587 memset(&data[0][0][0], 255, sizeof(data));
1588 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1590 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1591 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1593 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1594 d = 256 * (1 - (dx*dx+dy*dy));
1595 d = bound(0, d, 255);
1596 data[y][x][3] = (unsigned char) d;
1599 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1602 memset(&data[0][0][0], 255, sizeof(data));
1603 light[0] = 1;light[1] = 1;light[2] = 1;
1604 VectorNormalize(light);
1605 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1607 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1608 // stretch upper half of bubble by +50% and shrink lower half by -50%
1609 // (this gives an elongated teardrop shape)
1611 dy = (dy - 0.5f) * 2.0f;
1613 dy = (dy - 0.5f) / 1.5f;
1614 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1616 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1617 // shrink bubble width to half
1619 data[y][x][3] = shadebubble(dx, dy, light);
1622 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1625 memset(&data[0][0][0], 255, sizeof(data));
1626 light[0] = 1;light[1] = 1;light[2] = 1;
1627 VectorNormalize(light);
1628 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1630 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1631 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1633 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1634 data[y][x][3] = shadebubble(dx, dy, light);
1637 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1639 // Blood particles and blood decals
1640 R_InitBloodTextures (particletexturedata);
1643 for (i = 0;i < 8;i++)
1645 memset(&data[0][0][0], 255, sizeof(data));
1646 for (k = 0;k < 12;k++)
1647 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1648 for (k = 0;k < 3;k++)
1649 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1650 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1651 particletextureinvert(&data[0][0][0]);
1652 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1656 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1659 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1660 if (!particlefonttexture)
1661 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1662 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1663 particletexture[i].texture = particlefonttexture;
1666 fractalnoise(&noise3[0][0], 64, 4);
1668 for (y = 0;y < 64;y++)
1670 dy = (y - 0.5f*64) / (64*0.5f-1);
1671 for (x = 0;x < 16;x++)
1673 dx = (x - 0.5f*16) / (16*0.5f-2);
1674 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1675 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
1676 data2[y][x][3] = 255;
1681 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1684 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1685 if (!particletexture[tex_beam].texture)
1686 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1687 particletexture[tex_beam].s1 = 0;
1688 particletexture[tex_beam].t1 = 0;
1689 particletexture[tex_beam].s2 = 1;
1690 particletexture[tex_beam].t2 = 1;
1691 Mem_Free(particletexturedata);
1694 static void r_part_start(void)
1696 particletexturepool = R_AllocTexturePool();
1697 R_InitParticleTexture ();
1700 static void r_part_shutdown(void)
1702 R_FreeTexturePool(&particletexturepool);
1705 static void r_part_newmap(void)
1709 void R_Particles_Init (void)
1711 Cvar_RegisterVariable(&r_drawparticles);
1712 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1715 float particle_vertex3f[12], particle_texcoord2f[8];
1717 void R_DrawParticle_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
1719 const particle_t *p = cl.particles + surfacenumber;
1722 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, cr, cg, cb, ca, size;
1723 particletexture_t *tex;
1725 VectorCopy(p->org, org);
1727 blendmode = p->type->blendmode;
1728 tex = &particletexture[p->texnum];
1729 cr = p->color[0] * (1.0f / 255.0f);
1730 cg = p->color[1] * (1.0f / 255.0f);
1731 cb = p->color[2] * (1.0f / 255.0f);
1732 ca = p->alpha * (1.0f / 255.0f);
1733 if (blendmode == PBLEND_MOD)
1743 ca /= cl_particles_quality.value;
1744 if (p->type->lighting)
1746 float ambient[3], diffuse[3], diffusenormal[3];
1747 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
1748 cr *= (ambient[0] + 0.5 * diffuse[0]);
1749 cg *= (ambient[1] + 0.5 * diffuse[1]);
1750 cb *= (ambient[2] + 0.5 * diffuse[2]);
1754 fog = VERTEXFOGTABLE(VectorDistance(org, r_vieworigin));
1759 if (blendmode == PBLEND_ALPHA)
1761 cr += fogcolor[0] * fog;
1762 cg += fogcolor[1] * fog;
1763 cb += fogcolor[2] * fog;
1767 R_Mesh_Matrix(&identitymatrix);
1769 memset(&m, 0, sizeof(m));
1770 m.tex[0] = R_GetTexture(tex->texture);
1771 m.pointer_texcoord[0] = particle_texcoord2f;
1772 m.pointer_vertex = particle_vertex3f;
1775 GL_Color(cr, cg, cb, ca);
1777 if (blendmode == PBLEND_ALPHA)
1778 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1779 else if (blendmode == PBLEND_ADD)
1780 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1781 else //if (blendmode == PBLEND_MOD)
1782 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1783 GL_DepthMask(false);
1785 size = p->size * cl_particles_size.value;
1786 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1788 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1791 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1793 VectorNegate(p->vel, v);
1794 VectorVectors(v, right, up);
1797 VectorVectors(p->vel, right, up);
1798 VectorScale(right, size, right);
1799 VectorScale(up, size, up);
1803 VectorScale(r_viewleft, -size, right);
1804 VectorScale(r_viewup, size, up);
1806 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1807 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1808 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1809 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1810 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1811 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1812 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1813 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1814 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1815 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1816 particle_vertex3f[10] = org[1] + right[1] - up[1];
1817 particle_vertex3f[11] = org[2] + right[2] - up[2];
1818 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1819 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1820 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1821 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1823 else if (p->type->orientation == PARTICLE_SPARK)
1825 VectorMA(p->org, -0.02, p->vel, v);
1826 VectorMA(p->org, 0.02, p->vel, up2);
1827 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
1828 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1829 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1830 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1831 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1833 else if (p->type->orientation == PARTICLE_BEAM)
1835 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
1836 VectorSubtract(p->vel, p->org, up);
1837 VectorNormalize(up);
1838 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
1839 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
1840 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1841 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1842 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1843 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1847 Con_Printf("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
1851 R_Mesh_Draw(0, 4, 2, polygonelements);
1854 void R_DrawParticles (void)
1857 float minparticledist;
1860 // LordHavoc: early out conditions
1861 if ((!cl.num_particles) || (!r_drawparticles.integer))
1864 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1866 // LordHavoc: only render if not too close
1867 for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++)
1871 renderstats.particles++;
1872 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
1874 if (p->type == particletype + pt_decal)
1875 R_DrawParticle_TransparentCallback(0, i, 0);
1877 R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL);