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 | SUPERCONTENTS_SKY, false);
235 // take the closest trace result that doesn't end up hitting a NOMARKS
236 // surface (sky for example)
237 if (bestfrac > trace.fraction && !(trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS))
239 bestfrac = trace.fraction;
241 VectorCopy(trace.endpos, bestorg);
242 VectorCopy(trace.plane.normal, bestnormal);
246 CL_SpawnDecalParticleForSurface(besthitent, bestorg, bestnormal, color1, color2, texnum, size, alpha);
254 void CL_EntityParticles (entity_t *ent)
257 float pitch, yaw, dist = 64, beamlength = 16, org[3], v[3];
258 static vec3_t avelocities[NUMVERTEXNORMALS];
259 if (!cl_particles.integer) return;
261 Matrix4x4_OriginFromMatrix(&ent->render.matrix, org);
263 if (!avelocities[0][0])
264 for (i = 0;i < NUMVERTEXNORMALS * 3;i++)
265 avelocities[0][i] = lhrandom(0, 2.55);
267 for (i = 0;i < NUMVERTEXNORMALS;i++)
269 yaw = cl.time * avelocities[i][0];
270 pitch = cl.time * avelocities[i][1];
271 v[0] = org[0] + m_bytenormals[i][0] * dist + (cos(pitch)*cos(yaw)) * beamlength;
272 v[1] = org[1] + m_bytenormals[i][1] * dist + (cos(pitch)*sin(yaw)) * beamlength;
273 v[2] = org[2] + m_bytenormals[i][2] * dist + (-sin(pitch)) * beamlength;
274 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);
279 void CL_ReadPointFile_f (void)
283 char *pointfile = NULL, *pointfilepos, *t, tchar;
284 char name[MAX_OSPATH];
289 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
290 strlcat (name, ".pts", sizeof (name));
291 pointfile = (char *)FS_LoadFile(name, tempmempool, true, NULL);
294 Con_Printf("Could not open %s\n", name);
298 Con_Printf("Reading %s...\n", name);
299 VectorClear(leakorg);
302 pointfilepos = pointfile;
303 while (*pointfilepos)
305 while (*pointfilepos == '\n' || *pointfilepos == '\r')
310 while (*t && *t != '\n' && *t != '\r')
314 r = sscanf (pointfilepos,"%f %f %f", &org[0], &org[1], &org[2]);
320 VectorCopy(org, leakorg);
323 if (cl.num_particles < cl.max_particles - 3)
326 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);
330 VectorCopy(leakorg, org);
331 Con_Printf("%i points read (%i particles spawned)\nLeak at %f %f %f\n", c, s, org[0], org[1], org[2]);
333 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);
334 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);
335 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);
340 CL_ParseParticleEffect
342 Parse an effect out of the server message
345 void CL_ParseParticleEffect (void)
348 int i, count, msgcount, color;
350 MSG_ReadVector(org, cls.protocol);
351 for (i=0 ; i<3 ; i++)
352 dir[i] = MSG_ReadChar ();
353 msgcount = MSG_ReadByte ();
354 color = MSG_ReadByte ();
361 if (cl_particles_blood_bloodhack.integer && !cl_particles_quake.integer)
366 CL_BloodPuff(org, dir, count / 2);
372 CL_BloodPuff(org, dir, count / 2);
376 CL_RunParticleEffect (org, dir, color, count);
385 void CL_ParticleExplosion (vec3_t org)
391 if (cl_stainmaps.integer)
392 R_Stain(org, 96, 80, 80, 80, 64, 176, 176, 176, 64);
393 CL_SpawnDecalParticleForPoint(org, 40, 48, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
395 if (cl_particles_quake.integer)
397 for (i = 0;i < 1024;i++)
403 color = particlepalette[ramp1[r]];
404 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);
408 color = particlepalette[ramp2[r]];
409 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);
415 i = CL_PointSuperContents(org);
416 if (i & (SUPERCONTENTS_SLIME | SUPERCONTENTS_WATER))
418 if (cl_particles.integer && cl_particles_bubbles.integer && cl_particles_explosions_bubbles.integer)
419 for (i = 0;i < 128 * cl_particles_quality.value;i++)
420 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);
424 // LordHavoc: smoke effect similar to UT2003, chews fillrate too badly up close
426 if (cl_particles.integer && cl_particles_smoke.integer && cl_particles_explosions_smoke.integer)
428 for (i = 0;i < 32;i++)
432 for (k = 0;k < 16;k++)
434 v[0] = org[0] + lhrandom(-48, 48);
435 v[1] = org[1] + lhrandom(-48, 48);
436 v[2] = org[2] + lhrandom(-48, 48);
437 trace = CL_TraceBox(org, vec3_origin, vec3_origin, v, true, NULL, SUPERCONTENTS_SOLID, false);
438 if (trace.fraction >= 0.1)
441 VectorSubtract(trace.endpos, org, v2);
442 VectorScale(v2, 2.0f, v2);
443 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);
447 if (cl_particles.integer && cl_particles_sparks.integer && cl_particles_explosions_sparks.integer)
448 for (i = 0;i < 128 * cl_particles_quality.value;i++)
449 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);
453 if (cl_particles_explosions_shell.integer)
459 CL_ParticleExplosion2
463 void CL_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
466 if (!cl_particles.integer) return;
468 for (i = 0;i < 512 * cl_particles_quality.value;i++)
470 k = particlepalette[colorStart + (i % colorLength)];
471 if (cl_particles_quake.integer)
472 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);
474 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);
484 void CL_BlobExplosion (vec3_t org)
487 if (!cl_particles.integer) return;
489 if (!cl_particles_quake.integer)
491 CL_ParticleExplosion(org);
495 for (i = 0;i < 1024 * cl_particles_quality.value;i++)
499 k = particlepalette[66 + rand()%6];
500 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);
504 k = particlepalette[150 + rand()%6];
505 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);
516 void CL_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
522 CL_ParticleExplosion(org);
525 if (!cl_particles.integer) return;
526 if (cl_particles_quake.integer)
528 count *= cl_particles_quality.value;
531 k = particlepalette[color + (rand()&7)];
532 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);
537 count *= cl_particles_quality.value;
540 k = particlepalette[color + (rand()&7)];
541 if (gamemode == GAME_GOODVSBAD2)
542 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);
544 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);
549 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
555 void CL_SparkShower (vec3_t org, vec3_t dir, int count, vec_t gravityscale, vec_t radius)
559 if (!cl_particles.integer) return;
561 if (cl_particles_sparks.integer)
564 count *= cl_particles_quality.value;
567 k = particlepalette[0x68 + (rand() & 7)];
568 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);
573 void CL_Smoke (vec3_t org, vec3_t dir, int count, vec_t radius)
579 if (!cl_particles.integer) return;
582 if (cl_particles_smoke.integer)
584 k = count * 0.25 * cl_particles_quality.value;
587 org2[0] = org[0] + 0.125f * lhrandom(-count, count);
588 org2[1] = org[1] + 0.125f * lhrandom(-count, count);
589 org2[2] = org[2] + 0.125f * lhrandom(-count, count);
590 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
591 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);
596 void CL_BulletMark (vec3_t org)
598 if (cl_stainmaps.integer)
599 R_Stain(org, 32, 96, 96, 96, 24, 128, 128, 128, 24);
600 CL_SpawnDecalParticleForPoint(org, 6, 3, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
603 void CL_PlasmaBurn (vec3_t org)
605 if (cl_stainmaps.integer)
606 R_Stain(org, 48, 96, 96, 96, 32, 128, 128, 128, 32);
607 CL_SpawnDecalParticleForPoint(org, 6, 6, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
610 static float bloodcount = 0;
611 void CL_BloodPuff (vec3_t org, vec3_t vel, int count)
616 // bloodcount is used to accumulate counts too small to cause a blood particle
617 if (!cl_particles.integer) return;
618 if (cl_particles_quake.integer)
620 CL_RunParticleEffect(org, vel, 73, count * 2);
623 if (!cl_particles_blood.integer) return;
629 bloodcount += count * cl_particles_quality.value;
630 while(bloodcount > 0)
632 org2[0] = org[0] + 0.125f * lhrandom(-bloodcount, bloodcount);
633 org2[1] = org[1] + 0.125f * lhrandom(-bloodcount, bloodcount);
634 org2[2] = org[2] + 0.125f * lhrandom(-bloodcount, bloodcount);
635 trace = CL_TraceBox(org, vec3_origin, vec3_origin, org2, true, NULL, SUPERCONTENTS_SOLID, false);
636 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);
641 void CL_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
643 vec3_t org, vel, diff, center, velscale;
644 if (!cl_particles.integer) return;
645 if (!cl_particles_bloodshowers.integer) return;
646 if (!cl_particles_blood.integer) return;
648 VectorSubtract(maxs, mins, diff);
649 center[0] = (mins[0] + maxs[0]) * 0.5;
650 center[1] = (mins[1] + maxs[1]) * 0.5;
651 center[2] = (mins[2] + maxs[2]) * 0.5;
652 velscale[0] = velspeed * 2.0 / diff[0];
653 velscale[1] = velspeed * 2.0 / diff[1];
654 velscale[2] = velspeed * 2.0 / diff[2];
656 bloodcount += count * 5.0f * cl_particles_quality.value;
657 while (bloodcount > 0)
659 org[0] = lhrandom(mins[0], maxs[0]);
660 org[1] = lhrandom(mins[1], maxs[1]);
661 org[2] = lhrandom(mins[2], maxs[2]);
662 vel[0] = (org[0] - center[0]) * velscale[0];
663 vel[1] = (org[1] - center[1]) * velscale[1];
664 vel[2] = (org[2] - center[2]) * velscale[2];
666 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);
670 void CL_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
674 if (!cl_particles.integer) return;
675 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
676 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
677 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
679 count *= cl_particles_quality.value;
682 k = particlepalette[colorbase + (rand()&3)];
683 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);
687 void CL_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
690 float t, z, minz, maxz;
692 if (!cl_particles.integer) return;
693 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
694 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
695 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
696 if (dir[2] < 0) // falling
701 minz = z - fabs(dir[2]) * 0.1;
702 maxz = z + fabs(dir[2]) * 0.1;
703 minz = bound(mins[2], minz, maxs[2]);
704 maxz = bound(mins[2], maxz, maxs[2]);
706 count *= cl_particles_quality.value;
711 count *= 4; // ick, this should be in the mod or maps?
715 k = particlepalette[colorbase + (rand()&3)];
716 if (gamemode == GAME_GOODVSBAD2)
717 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);
719 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);
725 k = particlepalette[colorbase + (rand()&3)];
726 if (gamemode == GAME_GOODVSBAD2)
727 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);
729 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);
731 VectorCopy(p->vel, p->relativedirection);
735 Con_Printf ("CL_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
739 void CL_Stardust (vec3_t mins, vec3_t maxs, int count)
744 if (!cl_particles.integer) return;
746 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
747 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
748 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
750 center[0] = (mins[0] + maxs[0]) * 0.5f;
751 center[1] = (mins[1] + maxs[1]) * 0.5f;
752 center[2] = (mins[2] + maxs[2]) * 0.5f;
754 count *= cl_particles_quality.value;
757 k = particlepalette[224 + (rand()&15)];
758 o[0] = lhrandom(mins[0], maxs[0]);
759 o[1] = lhrandom(mins[1], maxs[1]);
760 o[2] = lhrandom(mins[2], maxs[2]);
761 VectorSubtract(o, center, v);
763 VectorScale(v, 100, v);
764 v[2] += sv_gravity.value * 0.15f;
765 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);
769 void CL_FlameCube (vec3_t mins, vec3_t maxs, int count)
773 if (!cl_particles.integer) return;
774 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
775 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
776 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
778 count *= cl_particles_quality.value;
781 k = particlepalette[224 + (rand()&15)];
782 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);
784 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);
788 void CL_Flames (vec3_t org, vec3_t vel, int count)
791 if (!cl_particles.integer) return;
793 count *= cl_particles_quality.value;
796 k = particlepalette[224 + (rand()&15)];
797 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);
809 void CL_LavaSplash (vec3_t origin)
811 float i, j, inc, vel;
814 if (!cl_particles.integer) return;
816 if (cl_particles_quake.integer)
818 inc = 8 / cl_particles_quality.value;
819 for (i = -128;i < 128;i += inc)
821 for (j = -128;j < 128;j += inc)
823 dir[0] = j + lhrandom(0, inc);
824 dir[1] = i + lhrandom(0, inc);
826 org[0] = origin[0] + dir[0];
827 org[1] = origin[1] + dir[1];
828 org[2] = origin[2] + lhrandom(0, 64);
829 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
830 k = l = particlepalette[224 + (rand()&7)];
831 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);
837 inc = 32 / cl_particles_quality.value;
838 for (i = -128;i < 128;i += inc)
840 for (j = -128;j < 128;j += inc)
842 dir[0] = j + lhrandom(0, inc);
843 dir[1] = i + lhrandom(0, inc);
845 org[0] = origin[0] + dir[0];
846 org[1] = origin[1] + dir[1];
847 org[2] = origin[2] + lhrandom(0, 64);
848 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
849 if (gamemode == GAME_GOODVSBAD2)
851 k = particlepalette[0 + (rand()&255)];
852 l = particlepalette[0 + (rand()&255)];
853 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);
857 k = l = particlepalette[224 + (rand()&7)];
858 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);
871 void CL_TeleportSplash (vec3_t org)
874 if (!cl_particles.integer) return;
876 if (cl_particles_quake.integer)
878 inc = 4 / cl_particles_quality.value;
879 for (i = -16;i < 16;i += inc)
881 for (j = -16;j < 16;j += inc)
883 for (k = -24;k < 32;k += inc)
887 VectorSet(dir, i*8, j*8, k*8);
888 VectorNormalize(dir);
889 vel = lhrandom(50, 113);
890 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);
897 inc = 8 / cl_particles_quality.value;
898 for (i = -16;i < 16;i += inc)
899 for (j = -16;j < 16;j += inc)
900 for (k = -24;k < 32;k += inc)
901 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);
905 void CL_RocketTrail (vec3_t start, vec3_t end, int type, int color, entity_t *ent)
907 vec3_t vec, dir, vel, pos;
908 float len, dec, speed, qd;
909 int smoke, blood, bubbles, r;
911 if (end[0] == start[0] && end[1] == start[1] && end[2] == start[2])
914 VectorSubtract(end, start, dir);
915 VectorNormalize(dir);
917 VectorSubtract (end, start, vec);
918 len = VectorNormalizeLength (vec);
919 dec = -ent->persistent.trail_time;
920 ent->persistent.trail_time += len;
921 if (ent->persistent.trail_time < 0.01f)
924 // if we skip out, leave it reset
925 ent->persistent.trail_time = 0.0f;
927 speed = ent->state_current.time - ent->state_previous.time;
929 speed = 1.0f / speed;
930 VectorSubtract(ent->state_current.origin, ent->state_previous.origin, vel);
931 color = particlepalette[color];
932 VectorScale(vel, speed, vel);
934 // advance into this frame to reach the first puff location
935 VectorMA(start, dec, vec, pos);
938 smoke = cl_particles.integer && cl_particles_smoke.integer;
939 blood = cl_particles.integer && cl_particles_blood.integer;
940 bubbles = cl_particles.integer && cl_particles_bubbles.integer && (CL_PointSuperContents(pos) & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME));
941 qd = 1.0f / cl_particles_quality.value;
947 case 0: // rocket trail
948 if (cl_particles_quake.integer)
952 color = particlepalette[ramp3[r]];
953 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);
960 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);
961 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);
964 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);
968 case 1: // grenade trail
969 if (cl_particles_quake.integer)
973 color = particlepalette[ramp3[r]];
974 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);
980 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);
986 case 4: // slight blood
987 if (cl_particles_quake.integer)
992 color = particlepalette[67 + (rand()&3)];
993 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);
998 color = particlepalette[67 + (rand()&3)];
999 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);
1006 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);
1010 case 3: // green tracer
1011 if (cl_particles_quake.integer)
1014 color = particlepalette[52 + (rand()&7)];
1015 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);
1016 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);
1023 if (gamemode == GAME_GOODVSBAD2)
1026 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);
1031 color = particlepalette[20 + (rand()&7)];
1032 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);
1038 case 5: // flame tracer
1039 if (cl_particles_quake.integer)
1042 color = particlepalette[230 + (rand()&7)];
1043 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);
1044 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);
1051 color = particlepalette[226 + (rand()&7)];
1052 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);
1057 case 6: // voor trail
1058 if (cl_particles_quake.integer)
1061 color = particlepalette[152 + (rand()&3)];
1062 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);
1069 if (gamemode == GAME_GOODVSBAD2)
1072 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);
1074 else if (gamemode == GAME_PRYDON)
1077 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);
1082 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);
1087 case 7: // Nehahra smoke tracer
1090 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);
1092 case 8: // Nexuiz plasma trail
1095 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);
1097 case 9: // glow trail
1100 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);
1103 Sys_Error("CL_RocketTrail: unknown trail type %i", type);
1106 // advance to next time and position
1109 VectorMA (pos, dec, vec, pos);
1111 ent->persistent.trail_time = len;
1114 void CL_BeamParticle (const vec3_t start, const vec3_t end, vec_t radius, float red, float green, float blue, float alpha, float lifetime)
1116 int tempcolor2, cr, cg, cb;
1120 tempcolor2 = (bound(0, cr, 255) << 16) | (bound(0, cg, 255) << 8) | bound(0, cb, 255);
1121 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);
1124 void CL_Tei_Smoke(const vec3_t org, const vec3_t dir, int count)
1127 if (!cl_particles.integer) return;
1130 if (cl_particles_smoke.integer)
1131 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1132 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);
1135 void CL_Tei_PlasmaHit(const vec3_t org, const vec3_t dir, int count)
1138 if (!cl_particles.integer) return;
1140 if (cl_stainmaps.integer)
1141 R_Stain(org, 40, 96, 96, 96, 40, 128, 128, 128, 40);
1142 CL_SpawnDecalParticleForPoint(org, 6, 8, 255, tex_bulletdecal[rand()&7], 0xFFFFFF, 0xFFFFFF);
1145 if (cl_particles_smoke.integer)
1146 for (f = 0;f < count;f += 4.0f / cl_particles_quality.value)
1147 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);
1150 if (cl_particles_sparks.integer)
1151 for (f = 0;f < count;f += 1.0f / cl_particles_quality.value)
1152 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);
1160 void CL_MoveParticles (void)
1163 int i, maxparticle, j, a, content;
1164 float gravity, dvel, bloodwaterfade, frametime, f, dist, org[3], oldorg[3];
1168 // LordHavoc: early out condition
1169 if (!cl.num_particles)
1171 cl.free_particle = 0;
1175 frametime = cl.time - cl.oldtime;
1176 gravity = frametime * sv_gravity.value;
1177 dvel = 1+4*frametime;
1178 bloodwaterfade = max(cl_particles_blood_alpha.value, 0.01f) * frametime * 128.0f;
1182 for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++)
1189 p->alpha -= p->alphafade * frametime;
1197 if (p->type->orientation != PARTICLE_BEAM)
1199 VectorCopy(p->org, oldorg);
1200 VectorMA(p->org, frametime, p->vel, p->org);
1201 VectorCopy(p->org, org);
1204 trace = CL_TraceBox(oldorg, vec3_origin, vec3_origin, p->org, true, &hitent, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | (p->type == particletype + pt_rain ? SUPERCONTENTS_LIQUIDSMASK : 0), false);
1205 // if the trace started in or hit something of SUPERCONTENTS_NODROP
1206 // or if the trace hit something flagged as NOIMPACT
1207 // then remove the particle
1208 if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT || ((trace.startsupercontents | trace.hitsupercontents) & SUPERCONTENTS_NODROP))
1213 // react if the particle hit something
1214 if (trace.fraction < 1)
1216 VectorCopy(trace.endpos, p->org);
1217 if (p->type == particletype + pt_rain)
1219 // raindrop - splash on solid/water/slime/lava
1221 // convert from a raindrop particle to a rainsplash decal
1222 VectorCopy(trace.plane.normal, p->vel);
1223 VectorAdd(p->org, p->vel, p->org);
1224 p->type = particletype + pt_raindecal;
1225 p->texnum = tex_rainsplash[0];
1227 p->alphafade = p->alpha / 0.4;
1234 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);
1236 else if (p->type == particletype + pt_blood)
1238 // blood - splash on solid
1239 if (trace.hitq3surfaceflags & Q3SURFACEFLAG_NOMARKS)
1244 if (!cl_decals.integer)
1249 // convert from a blood particle to a blood decal
1250 VectorCopy(trace.plane.normal, p->vel);
1251 VectorAdd(p->org, p->vel, p->org);
1252 if (cl_stainmaps.integer)
1253 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));
1255 p->type = particletype + pt_decal;
1256 p->texnum = tex_blooddecal[rand()&7];
1258 p->ownermodel = cl.entities[hitent].render.model;
1259 Matrix4x4_Transform(&cl.entities[hitent].render.inversematrix, p->org, p->relativeorigin);
1260 Matrix4x4_Transform3x3(&cl.entities[hitent].render.inversematrix, p->vel, p->relativedirection);
1268 else if (p->bounce < 0)
1270 // bounce -1 means remove on impact
1276 // anything else - bounce off solid
1277 dist = DotProduct(p->vel, trace.plane.normal) * -p->bounce;
1278 VectorMA(p->vel, dist, trace.plane.normal, p->vel);
1279 if (DotProduct(p->vel, p->vel) < 0.03)
1280 VectorClear(p->vel);
1284 p->vel[2] -= p->gravity * gravity;
1288 f = p->friction * frametime;
1289 if (CL_PointSuperContents(p->org) & SUPERCONTENTS_LIQUIDSMASK)
1292 VectorScale(p->vel, f, p->vel);
1296 if (p->type != particletype + pt_static)
1298 switch (p->type - particletype)
1300 case pt_entityparticle:
1301 // particle that removes itself after one rendered frame
1308 a = CL_PointSuperContents(p->org);
1309 if (a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME))
1311 p->size += frametime * 8;
1312 //p->alpha -= bloodwaterfade;
1315 p->vel[2] -= gravity;
1316 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP))
1320 a = CL_PointSuperContents(p->org);
1321 if (!(a & (SUPERCONTENTS_WATER | SUPERCONTENTS_SLIME)))
1328 a = CL_PointSuperContents(p->org);
1329 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
1333 if (cl.time > p->time2)
1336 p->time2 = cl.time + (rand() & 3) * 0.1;
1337 p->vel[0] = p->relativedirection[0] + lhrandom(-32, 32);
1338 p->vel[1] = p->relativedirection[1] + lhrandom(-32, 32);
1339 //p->vel[2] = p->relativedirection[2] + lhrandom(-32, 32);
1341 a = CL_PointSuperContents(p->org);
1342 if (a & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_LIQUIDSMASK))
1346 //p->size += frametime * 15;
1349 // FIXME: this has fairly wacky handling of alpha
1350 p->alphafade = cl.time > (p->time2 + cl_decals_time.value) ? (255 / cl_decals_fadetime.value) : 0;
1351 if (cl.entities[p->owner].render.model == p->ownermodel)
1353 Matrix4x4_Transform(&cl.entities[p->owner].render.matrix, p->relativeorigin, p->org);
1354 Matrix4x4_Transform3x3(&cl.entities[p->owner].render.matrix, p->relativedirection, p->vel);
1360 a = max(0, (cl.time - p->time2) * 40);
1362 p->texnum = tex_rainsplash[a];
1371 cl.num_particles = maxparticle + 1;
1372 cl.free_particle = 0;
1375 #define MAX_PARTICLETEXTURES 64
1376 // particletexture_t is a rectangle in the particlefonttexture
1377 typedef struct particletexture_s
1379 rtexture_t *texture;
1380 float s1, t1, s2, t2;
1384 static rtexturepool_t *particletexturepool;
1385 static rtexture_t *particlefonttexture;
1386 static particletexture_t particletexture[MAX_PARTICLETEXTURES];
1388 static cvar_t r_drawparticles = {0, "r_drawparticles", "1", "enables drawing of particles"};
1390 #define PARTICLETEXTURESIZE 64
1391 #define PARTICLEFONTSIZE (PARTICLETEXTURESIZE*8)
1393 static unsigned char shadebubble(float dx, float dy, vec3_t light)
1397 dz = 1 - (dx*dx+dy*dy);
1398 if (dz > 0) // it does hit the sphere
1402 normal[0] = dx;normal[1] = dy;normal[2] = dz;
1403 VectorNormalize(normal);
1404 dot = DotProduct(normal, light);
1405 if (dot > 0.5) // interior reflection
1406 f += ((dot * 2) - 1);
1407 else if (dot < -0.5) // exterior reflection
1408 f += ((dot * -2) - 1);
1410 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
1411 VectorNormalize(normal);
1412 dot = DotProduct(normal, light);
1413 if (dot > 0.5) // interior reflection
1414 f += ((dot * 2) - 1);
1415 else if (dot < -0.5) // exterior reflection
1416 f += ((dot * -2) - 1);
1418 f += 16; // just to give it a haze so you can see the outline
1419 f = bound(0, f, 255);
1420 return (unsigned char) f;
1426 static void setuptex(int texnum, unsigned char *data, unsigned char *particletexturedata)
1428 int basex, basey, y;
1429 basex = ((texnum >> 0) & 7) * PARTICLETEXTURESIZE;
1430 basey = ((texnum >> 3) & 7) * PARTICLETEXTURESIZE;
1431 particletexture[texnum].s1 = (basex + 1) / (float)PARTICLEFONTSIZE;
1432 particletexture[texnum].t1 = (basey + 1) / (float)PARTICLEFONTSIZE;
1433 particletexture[texnum].s2 = (basex + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1434 particletexture[texnum].t2 = (basey + PARTICLETEXTURESIZE - 1) / (float)PARTICLEFONTSIZE;
1435 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1436 memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
1439 void particletextureblotch(unsigned char *data, float radius, float red, float green, float blue, float alpha)
1442 float cx, cy, dx, dy, f, iradius;
1444 cx = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1445 cy = (lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius) + lhrandom(radius + 1, PARTICLETEXTURESIZE - 2 - radius)) * 0.5f;
1446 iradius = 1.0f / radius;
1447 alpha *= (1.0f / 255.0f);
1448 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1450 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1454 f = (1.0f - sqrt(dx * dx + dy * dy) * iradius) * alpha;
1457 d = data + (y * PARTICLETEXTURESIZE + x) * 4;
1458 d[0] += f * (red - d[0]);
1459 d[1] += f * (green - d[1]);
1460 d[2] += f * (blue - d[2]);
1466 void particletextureclamp(unsigned char *data, int minr, int ming, int minb, int maxr, int maxg, int maxb)
1469 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1471 data[0] = bound(minr, data[0], maxr);
1472 data[1] = bound(ming, data[1], maxg);
1473 data[2] = bound(minb, data[2], maxb);
1477 void particletextureinvert(unsigned char *data)
1480 for (i = 0;i < PARTICLETEXTURESIZE*PARTICLETEXTURESIZE;i++, data += 4)
1482 data[0] = 255 - data[0];
1483 data[1] = 255 - data[1];
1484 data[2] = 255 - data[2];
1488 // Those loops are in a separate function to work around an optimization bug in Mac OS X's GCC
1489 static void R_InitBloodTextures (unsigned char *particletexturedata)
1492 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4];
1495 for (i = 0;i < 8;i++)
1497 memset(&data[0][0][0], 255, sizeof(data));
1498 for (k = 0;k < 24;k++)
1499 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 96, 0, 0, 160);
1500 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1501 particletextureinvert(&data[0][0][0]);
1502 setuptex(tex_bloodparticle[i], &data[0][0][0], particletexturedata);
1506 for (i = 0;i < 8;i++)
1508 memset(&data[0][0][0], 255, sizeof(data));
1510 for (j = 1;j < 10;j++)
1511 for (k = min(j, m - 1);k < m;k++)
1512 particletextureblotch(&data[0][0][0], (float)j*PARTICLETEXTURESIZE/64.0f, 96, 0, 0, 192 - j * 8);
1513 //particletextureclamp(&data[0][0][0], 32, 32, 32, 255, 255, 255);
1514 particletextureinvert(&data[0][0][0]);
1515 setuptex(tex_blooddecal[i], &data[0][0][0], particletexturedata);
1520 static void R_InitParticleTexture (void)
1522 int x, y, d, i, k, m;
1523 float dx, dy, radius, f, f2;
1524 unsigned char data[PARTICLETEXTURESIZE][PARTICLETEXTURESIZE][4], noise3[64][64], data2[64][16][4];
1526 unsigned char *particletexturedata;
1528 // a note: decals need to modulate (multiply) the background color to
1529 // properly darken it (stain), and they need to be able to alpha fade,
1530 // this is a very difficult challenge because it means fading to white
1531 // (no change to background) rather than black (darkening everything
1532 // behind the whole decal polygon), and to accomplish this the texture is
1533 // inverted (dark red blood on white background becomes brilliant cyan
1534 // and white on black background) so we can alpha fade it to black, then
1535 // we invert it again during the blendfunc to make it work...
1537 particletexturedata = (unsigned char *)Mem_Alloc(tempmempool, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1538 memset(particletexturedata, 255, PARTICLEFONTSIZE*PARTICLEFONTSIZE*4);
1541 for (i = 0;i < 8;i++)
1543 memset(&data[0][0][0], 255, sizeof(data));
1546 unsigned char noise1[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2], noise2[PARTICLETEXTURESIZE*2][PARTICLETEXTURESIZE*2];
1548 fractalnoise(&noise1[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/8);
1549 fractalnoise(&noise2[0][0], PARTICLETEXTURESIZE*2, PARTICLETEXTURESIZE/4);
1551 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1553 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1554 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1556 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1557 d = (noise2[y][x] - 128) * 3 + 192;
1559 d = d * (1-(dx*dx+dy*dy));
1560 d = (d * noise1[y][x]) >> 7;
1561 d = bound(0, d, 255);
1562 data[y][x][3] = (unsigned char) d;
1569 setuptex(tex_smoke[i], &data[0][0][0], particletexturedata);
1573 for (i = 0;i < 16;i++)
1575 memset(&data[0][0][0], 255, sizeof(data));
1576 radius = i * 3.0f / 4.0f / 16.0f;
1577 f2 = 255.0f * ((15.0f - i) / 15.0f);
1578 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1580 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1581 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1583 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1584 f = f2 * (1.0 - 4.0f * fabs(radius - sqrt(dx*dx+dy*dy)));
1585 data[y][x][3] = (int) (bound(0.0f, f, 255.0f));
1588 setuptex(tex_rainsplash[i], &data[0][0][0], particletexturedata);
1592 memset(&data[0][0][0], 255, sizeof(data));
1593 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1595 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1596 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1598 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1599 d = 256 * (1 - (dx*dx+dy*dy));
1600 d = bound(0, d, 255);
1601 data[y][x][3] = (unsigned char) d;
1604 setuptex(tex_particle, &data[0][0][0], particletexturedata);
1607 memset(&data[0][0][0], 255, sizeof(data));
1608 light[0] = 1;light[1] = 1;light[2] = 1;
1609 VectorNormalize(light);
1610 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1612 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1613 // stretch upper half of bubble by +50% and shrink lower half by -50%
1614 // (this gives an elongated teardrop shape)
1616 dy = (dy - 0.5f) * 2.0f;
1618 dy = (dy - 0.5f) / 1.5f;
1619 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1621 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1622 // shrink bubble width to half
1624 data[y][x][3] = shadebubble(dx, dy, light);
1627 setuptex(tex_raindrop, &data[0][0][0], particletexturedata);
1630 memset(&data[0][0][0], 255, sizeof(data));
1631 light[0] = 1;light[1] = 1;light[2] = 1;
1632 VectorNormalize(light);
1633 for (y = 0;y < PARTICLETEXTURESIZE;y++)
1635 dy = (y - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1636 for (x = 0;x < PARTICLETEXTURESIZE;x++)
1638 dx = (x - 0.5f*PARTICLETEXTURESIZE) / (PARTICLETEXTURESIZE*0.5f-1);
1639 data[y][x][3] = shadebubble(dx, dy, light);
1642 setuptex(tex_bubble, &data[0][0][0], particletexturedata);
1644 // Blood particles and blood decals
1645 R_InitBloodTextures (particletexturedata);
1648 for (i = 0;i < 8;i++)
1650 memset(&data[0][0][0], 255, sizeof(data));
1651 for (k = 0;k < 12;k++)
1652 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/16, 0, 0, 0, 128);
1653 for (k = 0;k < 3;k++)
1654 particletextureblotch(&data[0][0][0], PARTICLETEXTURESIZE/2, 0, 0, 0, 160);
1655 //particletextureclamp(&data[0][0][0], 64, 64, 64, 255, 255, 255);
1656 particletextureinvert(&data[0][0][0]);
1657 setuptex(tex_bulletdecal[i], &data[0][0][0], particletexturedata);
1661 Image_WriteTGARGBA ("particles/particlefont.tga", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata);
1664 particlefonttexture = loadtextureimage(particletexturepool, "particles/particlefont.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1665 if (!particlefonttexture)
1666 particlefonttexture = R_LoadTexture2D(particletexturepool, "particlefont", PARTICLEFONTSIZE, PARTICLEFONTSIZE, particletexturedata, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
1667 for (i = 0;i < MAX_PARTICLETEXTURES;i++)
1668 particletexture[i].texture = particlefonttexture;
1671 fractalnoise(&noise3[0][0], 64, 4);
1673 for (y = 0;y < 64;y++)
1675 dy = (y - 0.5f*64) / (64*0.5f-1);
1676 for (x = 0;x < 16;x++)
1678 dx = (x - 0.5f*16) / (16*0.5f-2);
1679 d = (1 - sqrt(fabs(dx))) * noise3[y][x];
1680 data2[y][x][0] = data2[y][x][1] = data2[y][x][2] = (unsigned char) bound(0, d, 255);
1681 data2[y][x][3] = 255;
1686 Image_WriteTGARGBA ("particles/nexbeam.tga", 64, 64, &data2[0][0][0]);
1689 particletexture[tex_beam].texture = loadtextureimage(particletexturepool, "particles/nexbeam.tga", 0, 0, false, TEXF_ALPHA | TEXF_PRECACHE);
1690 if (!particletexture[tex_beam].texture)
1691 particletexture[tex_beam].texture = R_LoadTexture2D(particletexturepool, "nexbeam", 16, 64, &data2[0][0][0], TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1692 particletexture[tex_beam].s1 = 0;
1693 particletexture[tex_beam].t1 = 0;
1694 particletexture[tex_beam].s2 = 1;
1695 particletexture[tex_beam].t2 = 1;
1696 Mem_Free(particletexturedata);
1699 static void r_part_start(void)
1701 particletexturepool = R_AllocTexturePool();
1702 R_InitParticleTexture ();
1705 static void r_part_shutdown(void)
1707 R_FreeTexturePool(&particletexturepool);
1710 static void r_part_newmap(void)
1714 void R_Particles_Init (void)
1716 Cvar_RegisterVariable(&r_drawparticles);
1717 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
1720 float particle_vertex3f[12], particle_texcoord2f[8];
1722 void R_DrawParticle_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
1724 const particle_t *p = cl.particles + surfacenumber;
1727 float org[3], up2[3], v[3], right[3], up[3], fog, ifog, cr, cg, cb, ca, size;
1728 particletexture_t *tex;
1730 VectorCopy(p->org, org);
1732 blendmode = p->type->blendmode;
1733 tex = &particletexture[p->texnum];
1734 cr = p->color[0] * (1.0f / 255.0f);
1735 cg = p->color[1] * (1.0f / 255.0f);
1736 cb = p->color[2] * (1.0f / 255.0f);
1737 ca = p->alpha * (1.0f / 255.0f);
1738 if (blendmode == PBLEND_MOD)
1748 ca /= cl_particles_quality.value;
1749 if (p->type->lighting)
1751 float ambient[3], diffuse[3], diffusenormal[3];
1752 R_CompleteLightPoint(ambient, diffuse, diffusenormal, org, true);
1753 cr *= (ambient[0] + 0.5 * diffuse[0]);
1754 cg *= (ambient[1] + 0.5 * diffuse[1]);
1755 cb *= (ambient[2] + 0.5 * diffuse[2]);
1759 fog = VERTEXFOGTABLE(VectorDistance(org, r_vieworigin));
1764 if (blendmode == PBLEND_ALPHA)
1766 cr += fogcolor[0] * fog;
1767 cg += fogcolor[1] * fog;
1768 cb += fogcolor[2] * fog;
1772 R_Mesh_Matrix(&identitymatrix);
1774 memset(&m, 0, sizeof(m));
1775 m.tex[0] = R_GetTexture(tex->texture);
1776 m.pointer_texcoord[0] = particle_texcoord2f;
1777 m.pointer_vertex = particle_vertex3f;
1780 GL_Color(cr, cg, cb, ca);
1782 if (blendmode == PBLEND_ALPHA)
1783 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1784 else if (blendmode == PBLEND_ADD)
1785 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1786 else //if (blendmode == PBLEND_MOD)
1787 GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
1788 GL_DepthMask(false);
1790 size = p->size * cl_particles_size.value;
1791 if (p->type->orientation == PARTICLE_BILLBOARD || p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1793 if (p->type->orientation == PARTICLE_ORIENTED_DOUBLESIDED)
1796 if (DotProduct(p->vel, r_vieworigin) > DotProduct(p->vel, org))
1798 VectorNegate(p->vel, v);
1799 VectorVectors(v, right, up);
1802 VectorVectors(p->vel, right, up);
1803 VectorScale(right, size, right);
1804 VectorScale(up, size, up);
1808 VectorScale(r_viewleft, -size, right);
1809 VectorScale(r_viewup, size, up);
1811 particle_vertex3f[ 0] = org[0] - right[0] - up[0];
1812 particle_vertex3f[ 1] = org[1] - right[1] - up[1];
1813 particle_vertex3f[ 2] = org[2] - right[2] - up[2];
1814 particle_vertex3f[ 3] = org[0] - right[0] + up[0];
1815 particle_vertex3f[ 4] = org[1] - right[1] + up[1];
1816 particle_vertex3f[ 5] = org[2] - right[2] + up[2];
1817 particle_vertex3f[ 6] = org[0] + right[0] + up[0];
1818 particle_vertex3f[ 7] = org[1] + right[1] + up[1];
1819 particle_vertex3f[ 8] = org[2] + right[2] + up[2];
1820 particle_vertex3f[ 9] = org[0] + right[0] - up[0];
1821 particle_vertex3f[10] = org[1] + right[1] - up[1];
1822 particle_vertex3f[11] = org[2] + right[2] - up[2];
1823 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1824 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1825 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1826 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1828 else if (p->type->orientation == PARTICLE_SPARK)
1830 VectorMA(p->org, -0.02, p->vel, v);
1831 VectorMA(p->org, 0.02, p->vel, up2);
1832 R_CalcBeam_Vertex3f(particle_vertex3f, v, up2, size);
1833 particle_texcoord2f[0] = tex->s1;particle_texcoord2f[1] = tex->t2;
1834 particle_texcoord2f[2] = tex->s1;particle_texcoord2f[3] = tex->t1;
1835 particle_texcoord2f[4] = tex->s2;particle_texcoord2f[5] = tex->t1;
1836 particle_texcoord2f[6] = tex->s2;particle_texcoord2f[7] = tex->t2;
1838 else if (p->type->orientation == PARTICLE_BEAM)
1840 R_CalcBeam_Vertex3f(particle_vertex3f, p->org, p->vel, size);
1841 VectorSubtract(p->vel, p->org, up);
1842 VectorNormalize(up);
1843 v[0] = DotProduct(p->org, up) * (1.0f / 64.0f);
1844 v[1] = DotProduct(p->vel, up) * (1.0f / 64.0f);
1845 particle_texcoord2f[0] = 1;particle_texcoord2f[1] = v[0];
1846 particle_texcoord2f[2] = 0;particle_texcoord2f[3] = v[0];
1847 particle_texcoord2f[4] = 0;particle_texcoord2f[5] = v[1];
1848 particle_texcoord2f[6] = 1;particle_texcoord2f[7] = v[1];
1852 Con_Printf("R_DrawParticles: unknown particle orientation %i\n", p->type->orientation);
1856 R_Mesh_Draw(0, 4, 2, polygonelements);
1859 void R_DrawParticles (void)
1862 float minparticledist;
1865 // LordHavoc: early out conditions
1866 if ((!cl.num_particles) || (!r_drawparticles.integer))
1869 minparticledist = DotProduct(r_vieworigin, r_viewforward) + 4.0f;
1871 // LordHavoc: only render if not too close
1872 for (i = 0, p = cl.particles;i < cl.num_particles;i++, p++)
1876 renderstats.particles++;
1877 if (DotProduct(p->org, r_viewforward) >= minparticledist || p->type->orientation == PARTICLE_BEAM)
1879 if (p->type == particletype + pt_decal)
1880 R_DrawParticle_TransparentCallback(0, i, 0);
1882 R_MeshQueue_AddTransparent(p->org, R_DrawParticle_TransparentCallback, NULL, i, NULL);