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 #define MAX_PARTICLES 16384 // default max # of particles at one time
24 #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line
26 // LordHavoc: added dust, smoke, snow, bloodcloud, and many others
29 pt_static, pt_grav, pt_slowgrav, pt_blob, pt_blob2, pt_bulletsmoke, pt_smoke, pt_snow, pt_rain, pt_spark, pt_bubble, pt_fade, pt_steam, pt_splash, pt_splashpuff, pt_flame/*, pt_decal*/, pt_blood, pt_oneframe, pt_lavasplash
33 typedef struct particle_s
39 byte dynlight; // if set the particle will be dynamically lit (if r_dynamicparticles is on), used for smoke and blood
40 byte rendermode; // a TPOLYTYPE_ value
46 float time2; // used for various things (snow fluttering, for example)
47 float bounce; // how much bounce-back from a surface the particle hits (0 = no physics, 1 = stop and slide, 2 = keep bouncing forever, 1.5 is typical)
49 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
50 // vec3_t direction; // used by decals
51 // vec3_t decalright; // used by decals
52 // vec3_t decalup; // used by decals
56 float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);
58 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
59 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
60 int ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
62 rtexture_t *particletexture;
63 rtexture_t *smokeparticletexture[8];
64 rtexture_t *rainparticletexture;
65 rtexture_t *bubbleparticletexture;
66 rtexture_t *bulletholetexture[8];
67 rtexture_t *rocketglowparticletexture;
69 particle_t *particles;
72 vec3_t r_pright, r_pup, r_ppn;
75 particle_t **freeparticles; // list used only in compacting particles array
77 cvar_t r_particles = {"r_particles", "1", true};
78 cvar_t r_drawparticles = {"r_drawparticles", "1"};
79 cvar_t r_particles_lighting = {"r_particles_lighting", "1", true};
80 cvar_t r_particles_bloodshowers = {"r_particles_bloodshowers", "1", true};
81 cvar_t r_particles_blood = {"r_particles_blood", "1", true};
82 cvar_t r_particles_smoke = {"r_particles_smoke", "1", true};
83 cvar_t r_particles_sparks = {"r_particles_sparks", "1", true};
84 cvar_t r_particles_bubbles = {"r_particles_bubbles", "1", true};
86 byte shadebubble(float dx, float dy, vec3_t light)
90 dz = 1 - (dx*dx+dy*dy);
91 if (dz > 0) // it does hit the sphere
95 normal[0] = dx;normal[1] = dy;normal[2] = dz;
96 VectorNormalize(normal);
97 dot = DotProduct(normal, light);
98 if (dot > 0.5) // interior reflection
100 else if (dot < -0.5) // exterior reflection
101 f += ((dot * -2) - 1);
103 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
104 VectorNormalize(normal);
105 dot = DotProduct(normal, light);
106 if (dot > 0.5) // interior reflection
107 f += ((dot * 2) - 1);
108 else if (dot < -0.5) // exterior reflection
109 f += ((dot * -2) - 1);
111 f += 16; // just to give it a haze so you can see the outline
112 f = bound(0, f, 255);
119 void R_InitParticleTexture (void)
123 byte data[32][32][4], noise1[64][64], noise2[64][64];
126 for (y = 0;y < 32;y++)
129 for (x = 0;x < 32;x++)
131 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
133 d = (256 - (dx*dx+dy*dy));
134 d = bound(0, d, 255);
135 data[y][x][3] = (byte) d;
138 particletexture = R_LoadTexture ("particletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
140 for (i = 0;i < 8;i++)
144 fractalnoise(&noise1[0][0], 64, 4);
145 fractalnoise(&noise2[0][0], 64, 8);
147 for (y = 0;y < 32;y++)
150 for (x = 0;x < 32;x++)
152 d = (noise1[y][x] - 128) * 2 + 128;
153 d = bound(0, d, 255);
154 data[y][x][0] = data[y][x][1] = data[y][x][2] = d;
156 d = (noise2[y][x] - 128) * 4 + 128;
158 d = (d * (256 - (int) (dx*dx+dy*dy))) >> 8;
159 d = bound(0, d, 255);
160 data[y][x][3] = (byte) d;
168 smokeparticletexture[i] = R_LoadTexture (va("smokeparticletexture%d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
171 light[0] = 1;light[1] = 1;light[2] = 1;
172 VectorNormalize(light);
173 for (y = 0;y < 32;y++)
175 for (x = 0;x < 32;x++)
177 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
178 data[y][x][3] = shadebubble((x - 16) * (1.0 / 8.0), y < 24 ? (y - 24) * (1.0 / 24.0) : (y - 24) * (1.0 / 8.0), light);
181 rainparticletexture = R_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
183 light[0] = 1;light[1] = 1;light[2] = 1;
184 VectorNormalize(light);
185 for (y = 0;y < 32;y++)
187 for (x = 0;x < 32;x++)
189 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
190 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
193 bubbleparticletexture = R_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
195 for (i = 0;i < 8;i++)
198 fractalnoise(&noise1[0][0], 64, 8);
199 for (y = 0;y < 32;y++)
200 for (x = 0;x < 32;x++)
201 p[y][x] = (noise1[y][x] / 8.0f) - 64.0f;
202 for (m = 0;m < 32;m++)
206 fx = lhrandom(14, 18);
207 fy = lhrandom(14, 18);
210 dx = lhrandom(-1, 1);
211 dy = lhrandom(-1, 1);
212 f = (dx * dx + dy * dy);
214 while(f < 0.125f || f > 1.0f);
215 f = (m + 1) / 40.0f; //lhrandom(0.0f, 1.0);
218 for (j = 0;f > 0 && j < (32 * 14);j++)
224 p[y - 1][x - 1] += f * 0.125f;
225 p[y - 1][x ] += f * 0.25f;
226 p[y - 1][x + 1] += f * 0.125f;
227 p[y ][x - 1] += f * 0.25f;
229 p[y ][x + 1] += f * 0.25f;
230 p[y + 1][x - 1] += f * 0.125f;
231 p[y + 1][x ] += f * 0.25f;
232 p[y + 1][x + 1] += f * 0.125f;
233 // f -= (0.5f / (32 * 16));
236 for (y = 0;y < 32;y++)
238 for (x = 0;x < 32;x++)
241 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
242 data[y][x][3] = (byte) bound(0, m, 255);
246 bulletholetexture[i] = R_LoadTexture (va("bulletholetexture%d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
249 for (y = 0;y < 32;y++)
252 for (x = 0;x < 32;x++)
255 d = (2048.0f / (dx*dx+dy*dy+1)) - 8.0f;
256 data[y][x][0] = bound(0, d * 1.0f, 255);
257 data[y][x][1] = bound(0, d * 0.8f, 255);
258 data[y][x][2] = bound(0, d * 0.5f, 255);
259 data[y][x][3] = bound(0, d * 1.0f, 255);
262 rocketglowparticletexture = R_LoadTexture ("glowparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
265 void r_part_start(void)
267 particles = (particle_t *) qmalloc(r_numparticles * sizeof(particle_t));
268 freeparticles = (void *) qmalloc(r_numparticles * sizeof(particle_t *));
270 R_InitParticleTexture ();
273 void r_part_shutdown(void)
277 qfree(freeparticles);
280 void r_part_newmap(void)
290 void R_ReadPointFile_f (void);
291 void R_Particles_Init (void)
295 i = COM_CheckParm ("-particles");
299 r_numparticles = (int)(atoi(com_argv[i+1]));
300 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
301 r_numparticles = ABSOLUTE_MIN_PARTICLES;
305 r_numparticles = MAX_PARTICLES;
308 Cmd_AddCommand ("pointfile", R_ReadPointFile_f);
310 Cvar_RegisterVariable (&r_particles);
311 Cvar_RegisterVariable (&r_drawparticles);
312 Cvar_RegisterVariable (&r_particles_lighting);
313 Cvar_RegisterVariable (&r_particles_bloodshowers);
314 Cvar_RegisterVariable (&r_particles_blood);
315 Cvar_RegisterVariable (&r_particles_smoke);
316 Cvar_RegisterVariable (&r_particles_sparks);
317 Cvar_RegisterVariable (&r_particles_bubbles);
319 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
322 //void particle(int ptype, int pcolor, int ptex, int prendermode, int plight, float pscale, float palpha, float ptime, float pbounce, float px, float py, float pz, float pvx, float pvy, float pvz)
323 #define particle(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz)\
326 if (numparticles >= r_numparticles)\
328 part = &particles[numparticles++];\
329 part->type = (ptype);\
330 part->color = (pcolor);\
332 part->dynlight = (plight);\
333 part->rendermode = (prendermode);\
334 part->scale = (pscale);\
335 part->alpha = (palpha);\
336 part->die = cl.time + (ptime);\
337 part->bounce = (pbounce);\
338 part->org[0] = (px);\
339 part->org[1] = (py);\
340 part->org[2] = (pz);\
341 part->vel[0] = (pvx);\
342 part->vel[1] = (pvy);\
343 part->vel[2] = (pvz);\
345 part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
348 #define particle2(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, poscale, pvscale)\
351 if (numparticles >= r_numparticles)\
353 part = &particles[numparticles++];\
354 part->type = (ptype);\
355 part->color = (pcolor);\
357 part->dynlight = (plight);\
358 part->rendermode = (prendermode);\
359 part->scale = (pscale);\
360 part->alpha = (palpha);\
361 part->die = cl.time + (ptime);\
362 part->bounce = (pbounce);\
363 part->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
364 part->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
365 part->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
366 part->vel[0] = lhrandom(-(pvscale), (pvscale));\
367 part->vel[1] = lhrandom(-(pvscale), (pvscale));\
368 part->vel[2] = lhrandom(-(pvscale), (pvscale));\
370 part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
374 #define particle3(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
377 if (numparticles >= r_numparticles)\
379 part = &particles[numparticles++];\
380 part->type = (ptype);\
381 part->color = (pcolor);\
383 part->dynlight = (plight);\
384 part->rendermode = (prendermode);\
385 part->scale = (pscale);\
386 part->alpha = (palpha);\
387 part->die = cl.time + (ptime);\
388 part->bounce = (pbounce);\
389 part->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
390 part->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
391 part->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
392 part->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
393 part->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
394 part->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
396 part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
399 #define particle4(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz, ptime2, pvx2, pvy2, pvz2)\
402 if (numparticles >= r_numparticles)\
404 part = &particles[numparticles++];\
405 part->type = (ptype);\
406 part->color = (pcolor);\
408 part->dynlight = (plight);\
409 part->rendermode = (prendermode);\
410 part->scale = (pscale);\
411 part->alpha = (palpha);\
412 part->die = cl.time + (ptime);\
413 part->bounce = (pbounce);\
414 part->org[0] = (px);\
415 part->org[1] = (py);\
416 part->org[2] = (pz);\
417 part->vel[0] = (pvx);\
418 part->vel[1] = (pvy);\
419 part->vel[2] = (pvz);\
420 part->time2 = (ptime2);\
421 part->vel2[0] = (pvx2);\
422 part->vel2[1] = (pvy2);\
423 part->vel2[2] = (pvz2);\
431 void R_EntityParticles (entity_t *ent)
435 float sp, sy, cp, cy;
439 static vec3_t avelocities[NUMVERTEXNORMALS];
440 if (!r_particles.value) return; // LordHavoc: particles are optional
445 if (!avelocities[0][0])
446 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
447 avelocities[0][i] = (rand()&255) * 0.01;
449 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
451 angle = cl.time * avelocities[i][0];
454 angle = cl.time * avelocities[i][1];
462 particle(pt_oneframe, 0x6f, particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 9999, 0, ent->render.origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->render.origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->render.origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0);
467 void R_ReadPointFile_f (void)
473 char name[MAX_OSPATH];
475 sprintf (name,"maps/%s.pts", sv.name);
477 COM_FOpenFile (name, &f, false, true);
480 Con_Printf ("couldn't open %s\n", name);
484 Con_Printf ("Reading %s...\n", name);
488 char *str = Qgetline (f);
489 r = sscanf (str,"%f %f %f\n", &org[0], &org[1], &org[2]);
494 if (numparticles >= r_numparticles)
496 Con_Printf ("Not enough free particles\n");
499 particle(pt_static, (-c)&15, particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 99999, 0, org[0], org[1], org[2], 0, 0, 0);
503 Con_Printf ("%i points read\n", c);
508 R_ParseParticleEffect
510 Parse an effect out of the server message
513 void R_ParseParticleEffect (void)
516 int i, count, msgcount, color;
518 for (i=0 ; i<3 ; i++)
519 org[i] = MSG_ReadCoord ();
520 for (i=0 ; i<3 ; i++)
521 dir[i] = MSG_ReadChar () * (1.0/16);
522 msgcount = MSG_ReadByte ();
523 color = MSG_ReadByte ();
530 R_RunParticleEffect (org, dir, color, count);
539 void R_ParticleExplosion (vec3_t org, int smoke)
542 if (!r_particles.value) return; // LordHavoc: particles are optional
544 // particle(pt_smoke, (rand()&7) + 8, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 30, 255, 2, org[0], org[1], org[2], 0, 0, 0);
546 i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
547 if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
549 for (i = 0;i < 128;i++)
550 particle(pt_bubble, 254, bubbleparticletexture, TPOLYTYPE_ADD, false, lhrandom(1, 2), 255, 9999, 1.5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-96, 96), lhrandom(-96, 96), lhrandom(-96, 96));
559 float f, forg[3], fvel[3], fvel2[3];
560 // for (i = 0;i < 256;i++)
561 // particle(pt_fallfadespark, ramp3[rand()%6], particletexture, TPOLYTYPE_ALPHA, false, 1.5, lhrandom(128, 255), 5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(0, 384));
562 // for (i = 0;i < 256;i++)
563 // particle(pt_fallfadespark, ramp3[rand()%6], particletexture, TPOLYTYPE_ALPHA, false, 1.5, lhrandom(128, 255), 5, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-150, 150), lhrandom(-150, 150), lhrandom(-150, 150));
564 for (i = 0;i < 32;i++)
566 fvel[0] = lhrandom(-150, 150);
567 fvel[1] = lhrandom(-150, 150);
568 fvel[2] = lhrandom(-150, 150) + 80;
569 // particle(pt_flamefall, 106 + (rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 3, 255, 5, forg[0] + lhrandom(-5, 5), forg[1] + lhrandom(-5, 5), forg[2] + lhrandom(-5, 5), fvel2[0], fvel2[1], fvel2[2]);
570 for (j = 0;j < 64;j++)
572 forg[0] = lhrandom(-20, 20) + org[0];
573 forg[1] = lhrandom(-20, 20) + org[1];
574 forg[2] = lhrandom(-20, 20) + org[2];
575 fvel2[0] = fvel[0] + lhrandom(-30, 30);
576 fvel2[1] = fvel[1] + lhrandom(-30, 30);
577 fvel2[2] = fvel[2] + lhrandom(-30, 30);
578 f = lhrandom(0.2, 1);
582 particle(pt_flamefall, 106 + (rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 5, lhrandom(96, 192), 5, forg[0], forg[1], forg[2], fvel2[0], fvel2[1], fvel2[2]);
585 // for (i = 0;i < 16;i++)
586 // particle(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 20, 192, 99, org[0] + lhrandom(-20, 20), org[1] + lhrandom(-20, 20), org[2] + lhrandom(-20, 20), 0, 0, 0);
587 // for (i = 0;i < 50;i++)
588 // particle(pt_flamingdebris, ramp3[rand()%6], particletexture, TPOLYTYPE_ALPHA, false, 3, 255, 99, org[0] + lhrandom(-10, 10), org[1] + lhrandom(-10, 10), org[2] + lhrandom(-10, 10), lhrandom(-200, 200), lhrandom(-200, 200), lhrandom(-200, 200));
589 // for (i = 0;i < 30;i++)
590 // particle(pt_smokingdebris, 10 + (rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 99, org[0] + lhrandom(-10, 10), org[1] + lhrandom(-10, 10), org[2] + lhrandom(-10, 10), lhrandom(-100, 100), lhrandom(-100, 100), lhrandom(-100, 100));
601 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
604 if (!r_particles.value) return; // LordHavoc: particles are optional
606 for (i = 0;i < 512;i++)
607 particle(pt_fade, colorStart + (i % colorLength), particletexture, TPOLYTYPE_ALPHA, false, 1.5, 255, 0.3, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192));
616 void R_BlobExplosion (vec3_t org)
619 if (!r_particles.value) return; // LordHavoc: particles are optional
621 for (i = 0;i < 256;i++)
622 particle(pt_blob, 66+(rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 4, 255, 9999, 0, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(-128, 128));
623 for (i = 0;i < 256;i++)
624 particle(pt_blob2, 150+(rand()%6), particletexture, TPOLYTYPE_ALPHA, false, 4, 255, 9999, 0, org[0] + lhrandom(-16, 16), org[1] + lhrandom(-16, 16), org[2] + lhrandom(-16, 16), lhrandom(-4, 4), lhrandom(-4, 4), lhrandom(-128, 128));
633 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
635 if (!r_particles.value) return; // LordHavoc: particles are optional
639 R_ParticleExplosion(org, false);
643 particle(pt_fade, color + (rand()&7), particletexture, TPOLYTYPE_ALPHA, false, 1, 128, 9999, 0, org[0] + lhrandom(-8, 8), org[1] + lhrandom(-8, 8), org[2] + lhrandom(-8, 8), lhrandom(-15, 15), lhrandom(-15, 15), lhrandom(-15, 15));
646 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
652 void R_SparkShower (vec3_t org, vec3_t dir, int count)
654 if (!r_particles.value) return; // LordHavoc: particles are optional
656 R_Decal(org, bulletholetexture[rand()&7], 16, 0, 0, 0, 255);
659 if (r_particles_smoke.value)
660 particle(pt_bulletsmoke, 10, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 5, 255, 9999, 0, org[0], org[1], org[2], lhrandom(-8, 8), lhrandom(-8, 8), lhrandom(0, 16));
662 if (r_particles_sparks.value)
666 particle(pt_spark, ramp3[rand()%6], particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(0, 255), 9999, 1.5, org[0], org[1], org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(0, 128));
670 void R_BloodPuff (vec3_t org, vec3_t vel, int count)
672 // bloodcount is used to accumulate counts too small to cause a blood particle
673 static int bloodcount = 0;
674 if (!r_particles.value) return; // LordHavoc: particles are optional
675 if (!r_particles_blood.value) return;
680 while(bloodcount >= 10)
682 particle(pt_blood, 68+(rand()&3), smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 24, 255, 9999, -1, org[0], org[1], org[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64));
687 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
692 if (!r_particles.value) return; // LordHavoc: particles are optional
693 if (!r_particles_bloodshowers.value) return;
694 if (!r_particles_blood.value) return;
696 VectorSubtract(maxs, mins, diff);
697 center[0] = (mins[0] + maxs[0]) * 0.5;
698 center[1] = (mins[1] + maxs[1]) * 0.5;
699 center[2] = (mins[2] + maxs[2]) * 0.5;
700 // FIXME: change velspeed back to 2.0x after fixing mod
701 velscale[0] = velspeed * 2.0 / diff[0];
702 velscale[1] = velspeed * 2.0 / diff[1];
703 velscale[2] = velspeed * 2.0 / diff[2];
708 org[0] = lhrandom(mins[0], maxs[0]);
709 org[1] = lhrandom(mins[1], maxs[1]);
710 org[2] = lhrandom(mins[2], maxs[2]);
711 vel[0] = (org[0] - center[0]) * velscale[0];
712 vel[1] = (org[1] - center[1]) * velscale[1];
713 vel[2] = (org[2] - center[2]) * velscale[2];
714 particle(pt_blood, 68+(rand()&3), smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 24, 255, 9999, -1, org[0], org[1], org[2], vel[0], vel[1], vel[2]);
718 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
721 if (!r_particles.value) return; // LordHavoc: particles are optional
722 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
723 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
724 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
727 particle(gravity ? pt_grav : pt_static, colorbase + (rand()&3), particletexture, TPOLYTYPE_ALPHA, false, 2, 255, lhrandom(1, 2), 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), dir[0] + lhrandom(-randomvel, randomvel), dir[1] + lhrandom(-randomvel, randomvel), dir[2] + lhrandom(-randomvel, randomvel));
730 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
734 if (!r_particles.value) return; // LordHavoc: particles are optional
735 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
736 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
737 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
738 if (dir[2] < 0) // falling
740 t = (maxs[2] - mins[2]) / -dir[2];
745 t = (maxs[2] - mins[2]) / dir[2];
748 if (t < 0 || t > 2) // sanity check
756 vel[0] = dir[0] + lhrandom(-16, 16);
757 vel[1] = dir[1] + lhrandom(-16, 16);
758 vel[2] = dir[2] + lhrandom(-32, 32);
759 particle4(pt_rain, colorbase + (rand()&3), rainparticletexture, TPOLYTYPE_ALPHA, true, 3, 255, t, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), z, vel[0], vel[1], vel[2], 0, vel[0], vel[1], vel[2]);
765 vel[0] = dir[0] + lhrandom(-16, 16);
766 vel[1] = dir[1] + lhrandom(-16, 16);
767 vel[2] = dir[2] + lhrandom(-32, 32);
768 particle4(pt_snow, colorbase + (rand()&3), particletexture, TPOLYTYPE_ALPHA, false, 2, 255, t, 0, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), z, vel[0], vel[1], vel[2], 0, vel[0], vel[1], vel[2]);
772 Host_Error("R_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
776 void R_FlameCube (vec3_t mins, vec3_t maxs, int count)
779 if (!r_particles.value) return; // LordHavoc: particles are optional
780 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
781 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
782 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
785 particle(pt_flame, 224 + (rand()&15), smokeparticletexture[rand()&7], TPOLYTYPE_ADD, false, 8, 255, 9999, 1.1, lhrandom(mins[0], maxs[0]), lhrandom(mins[1], maxs[1]), lhrandom(mins[2], maxs[2]), lhrandom(-32, 32), lhrandom(-32, 32), lhrandom(-32, 64));
788 void R_Flames (vec3_t org, vec3_t vel, int count)
790 if (!r_particles.value) return; // LordHavoc: particles are optional
793 particle(pt_flame, 224 + (rand()&15), smokeparticletexture[rand()&7], TPOLYTYPE_ADD, false, 8, 255, 9999, 1.1, org[0], org[1], org[2], vel[0] + lhrandom(-128, 128), vel[1] + lhrandom(-128, 128), vel[2] + lhrandom(-128, 128));
804 void R_LavaSplash (vec3_t origin)
809 if (!r_particles.value) return; // LordHavoc: particles are optional
811 for (i=-128 ; i<128 ; i+=16)
813 for (j=-128 ; j<128 ; j+=16)
815 dir[0] = j + lhrandom(0, 8);
816 dir[1] = i + lhrandom(0, 8);
818 org[0] = origin[0] + dir[0];
819 org[1] = origin[1] + dir[1];
820 org[2] = origin[2] + lhrandom(0, 64);
821 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
822 particle(pt_lavasplash, 224 + (rand()&7), particletexture, TPOLYTYPE_ALPHA, false, 7, 255, 9999, 0, org[0], org[1], org[2], dir[0] * vel, dir[1] * vel, dir[2] * vel);
823 // particle(pt_lavasplash, 224 + (rand()&7), particletexture, TPOLYTYPE_ALPHA, false, 7, 255, 9999, 0, origin[0] + i, origin[1] + j, origin[2] + lhrandom(0, 63), i * lhrandom(0.125, 0.25), j * lhrandom(0.125, 0.25), lhrandom(64, 128));
834 void R_TeleportSplash (vec3_t org)
837 if (!r_particles.value) return; // LordHavoc: particles are optional
839 for (i=-16 ; i<16 ; i+=8)
840 for (j=-16 ; j<16 ; j+=8)
841 for (k=-24 ; k<32 ; k+=8)
842 particle(pt_fade, 254, particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(64, 128), 9999, 0, org[0] + i + lhrandom(0, 8), org[1] + j + lhrandom(0, 8), org[2] + k + lhrandom(0, 8), i*2 + lhrandom(-12.5, 12.5), j*2 + lhrandom(-12.5, 12.5), k*2 + lhrandom(27.5, 52.5));
845 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
847 vec3_t vec, dir, vel;
848 float len, dec = 0, speed;
849 int contents, bubbles, polytype;
851 if (!r_particles.value) return; // LordHavoc: particles are optional
853 VectorSubtract(end, start, dir);
854 VectorNormalize(dir);
856 if (type == 0 && host_frametime != 0) // rocket glow
857 particle(pt_oneframe, 254, rocketglowparticletexture, TPOLYTYPE_ADD, false, 24, 255, 9999, 0, end[0] - 12 * dir[0], end[1] - 12 * dir[1], end[2] - 12 * dir[2], 0, 0, 0);
859 t = ent->render.trail_time;
861 return; // no particles to spawn this frame (sparse trail)
866 VectorSubtract (end, start, vec);
867 len = VectorNormalizeLength (vec);
870 // advance the trail time
871 ent->render.trail_time = cl.time;
874 speed = len / (cl.time - cl.oldtime);
875 VectorScale(vec, speed, vel);
877 // advance into this frame to reach the first puff location
878 dec = t - cl.oldtime;
880 VectorMA(start, dec, vec, start);
882 contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
883 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
885 // advance the trail time
886 ent->render.trail_time = cl.time;
890 bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
892 polytype = TPOLYTYPE_ALPHA;
893 if (ent->render.effects & EF_ADDITIVE)
894 polytype = TPOLYTYPE_ADD;
900 case 0: // rocket trail
901 if (!r_particles_smoke.value)
903 else if (bubbles && r_particles_bubbles.value)
906 particle(pt_bubble, 254, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16));
907 particle(pt_bubble, 254, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16));
908 particle(pt_smoke, 254, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
913 particle(pt_smoke, 12, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
914 // particle(pt_spark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
915 // particle(pt_spark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
916 // particle(pt_spark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
917 // particle(pt_spark, 0x68 + (rand() & 7), particletexture, TPOLYTYPE_ADD, false, 1, lhrandom(128, 255), 9999, 1.5, start[0], start[1], start[2], lhrandom(-64, 64) - vel[0] * 0.25, lhrandom(-64, 64) - vel[1] * 0.25, lhrandom(-64, 64) - vel[2] * 0.25);
921 case 1: // grenade trail
922 // FIXME: make it gradually stop smoking
923 if (!r_particles_smoke.value)
925 else if (bubbles && r_particles_bubbles.value)
928 particle(pt_bubble, 254, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16));
929 particle(pt_bubble, 254, bubbleparticletexture, polytype, false, lhrandom(1, 2), 255, 9999, 1.5, start[0], start[1], start[2], lhrandom(-16, 16), lhrandom(-16, 16), lhrandom(-16, 16));
930 particle(pt_smoke, 254, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
935 particle(pt_smoke, 8, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
941 if (!r_particles_blood.value)
946 particle(pt_blood, 67+(rand()&3), smokeparticletexture[rand()&7], polytype, true, 24, 255, 9999, -1, start[0], start[1], start[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64));
950 case 4: // slight blood
951 if (!r_particles_blood.value)
956 particle(pt_blood, 67+(rand()&3), smokeparticletexture[rand()&7], polytype, true, 24, 255, 9999, -1, start[0], start[1], start[2], vel[0] + lhrandom(-64, 64), vel[1] + lhrandom(-64, 64), vel[2] + lhrandom(-64, 64));
960 case 3: // green tracer
962 particle(pt_fade, 56, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
965 case 5: // flame tracer
967 particle(pt_fade, 234, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
970 case 6: // voor trail
971 dec = 0.05f; // sparse trail
972 particle(pt_fade, 152 + (rand()&3), smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
975 case 7: // Nehahra smoke tracer
976 if (!r_particles_smoke.value)
981 particle(pt_smoke, 12, smokeparticletexture[rand()&7], polytype, true, 10, 64, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
986 // advance to next time and position
989 VectorMA (start, dec, vec, start);
991 ent->render.trail_time = t;
994 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
998 if (!r_particles.value) return; // LordHavoc: particles are optional
999 if (!r_particles_smoke.value) return;
1001 VectorSubtract (end, start, vec);
1002 len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
1003 VectorScale(vec, 3, vec);
1006 particle(pt_smoke, color, particletexture, TPOLYTYPE_ALPHA, false, 8, 192, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
1007 VectorAdd (start, vec, start);
1017 void R_MoveParticles (void)
1020 int i, activeparticles, maxparticle, j, a;
1022 float gravity, dvel, frametime;
1024 // LordHavoc: early out condition
1028 frametime = cl.time - cl.oldtime;
1030 return; // if absolutely still, don't update particles
1031 gravity = frametime * sv_gravity.value;
1032 dvel = 1+4*frametime;
1034 activeparticles = 0;
1037 for (i = 0, p = particles;i < numparticles;i++, p++)
1039 if (p->die < cl.time)
1041 freeparticles[j++] = p;
1045 VectorCopy(p->org, p->oldorg);
1046 p->org[0] += p->vel[0]*frametime;
1047 p->org[1] += p->vel[1]*frametime;
1048 p->org[2] += p->vel[2]*frametime;
1053 if (TraceLine(p->oldorg, p->org, v, normal) < 1)
1055 VectorCopy(v, p->org);
1058 byte *color24 = (byte *) &d_8to24table[(int)p->color];
1059 R_Decal(v, p->tex, p->scale, color24[0], color24[1], color24[2], p->alpha);
1061 freeparticles[j++] = p;
1064 VectorClear(p->vel);
1066 // have to negate the direction (why?)
1067 VectorNegate(normal, p->direction);
1068 VectorVectors(p->direction, p->decalright, p->decalup);
1069 VectorSubtract(p->org, p->direction, p->org); // push off the surface a bit so it doesn't flicker
1071 p->time2 = cl.time + 30;
1076 dist = DotProduct(p->vel, normal) * -p->bounce;
1077 VectorMAQuick(p->vel, dist, normal, p->vel);
1078 if (DotProduct(p->vel, p->vel) < 0.03)
1079 VectorClear(p->vel);
1089 // LordHavoc: drop-through because of shared code
1095 p->alpha -= frametime * 256;
1101 p->vel[2] -= gravity;
1104 p->vel[2] -= gravity * 0.05;
1107 p->vel[2] -= gravity * 0.05;
1108 p->alpha -= frametime * 192;
1113 if (cl.time > p->time2)
1115 p->time2 = cl.time + (rand() & 3) * 0.1;
1116 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1117 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1118 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1120 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1121 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1124 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1125 break; // still in solid
1126 p->die = cl.time + 1000;
1127 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1131 case CONTENTS_SLIME:
1132 p->tex = smokeparticletexture[rand()&7];
1138 case CONTENTS_WATER:
1139 p->tex = smokeparticletexture[rand()&7];
1140 p->type = pt_splash;
1145 default: // CONTENTS_SOLID and any others
1146 TraceLine(p->oldorg, p->org, v, normal);
1147 VectorCopy(v, p->org);
1148 p->tex = smokeparticletexture[rand()&7];
1150 VectorClear(p->vel);
1156 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1161 p->vel[2] -= gravity * 0.5;
1164 p->alpha -= frametime * 512;
1165 p->vel[2] -= gravity;
1170 p->alpha -= frametime * 512;
1175 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1176 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1178 p->tex = smokeparticletexture[rand()&7];
1179 p->type = pt_splashpuff;
1181 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1184 p->vel[2] += gravity * 0.25;
1185 p->vel[0] *= (1 - (frametime * 0.0625));
1186 p->vel[1] *= (1 - (frametime * 0.0625));
1187 p->vel[2] *= (1 - (frametime * 0.0625));
1188 if (cl.time > p->time2)
1190 p->time2 = cl.time + lhrandom(0, 0.5);
1191 p->vel[0] += lhrandom(-32,32);
1192 p->vel[1] += lhrandom(-32,32);
1193 p->vel[2] += lhrandom(-32,32);
1195 p->alpha -= frametime * 256;
1199 case pt_bulletsmoke:
1200 p->scale += frametime * 16;
1201 p->alpha -= frametime * 1024;
1202 p->vel[2] += gravity * 0.05;
1207 p->scale += frametime * 32;
1208 p->alpha -= frametime * 512;
1209 p->vel[2] += gravity * 0.05;
1214 p->scale += frametime * 48;
1215 p->alpha -= frametime * 512;
1216 p->vel[2] += gravity * 0.05;
1221 // p->scale += frametime * 24;
1222 p->alpha -= frametime * 1024;
1227 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1228 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1231 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1232 break; // still in solid
1233 p->die = cl.time + 1000;
1234 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1238 case CONTENTS_SLIME:
1239 p->tex = smokeparticletexture[rand()&7];
1244 case CONTENTS_WATER:
1245 p->tex = smokeparticletexture[rand()&7];
1246 p->type = pt_splashpuff;
1249 default: // CONTENTS_SOLID and any others
1250 TraceLine(p->oldorg, p->org, v, normal);
1251 VectorCopy(v, p->org);
1252 p->tex = smokeparticletexture[rand()&7];
1253 p->type = pt_splashpuff;
1260 p->alpha -= frametime * 512;
1261 p->vel[2] += gravity;
1262 // p->scale -= frametime * 16;
1267 case pt_flamingdebris:
1268 if (cl.time >= p->time2)
1270 p->time2 = cl.time + 0.01;
1271 particle(pt_flame, p->color, particletexture, TPOLYTYPE_ADD, false, 4, p->alpha, 9999, 0, org[0], org[1], org[2], lhrandom(-50, 50), lhrandom(-50, 50), lhrandom(-50, 50));
1273 p->alpha -= frametime * 512;
1274 p->vel[2] -= gravity * 0.5f;
1275 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1280 case pt_smokingdebris:
1281 if (cl.time >= p->time2)
1283 p->time2 = cl.time + 0.01;
1284 particle2(pt_flame, 15, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, false, 4, p->alpha, 9999, 0, org[0], org[1], org[2], lhrandom(-50, 50), lhrandom(-50, 50), lhrandom(-50, 50));
1286 p->alpha -= frametime * 512;
1287 p->vel[2] -= gravity * 0.5f;
1288 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1294 p->alpha -= frametime * 512;
1295 p->vel[2] -= gravity * 0.5f;
1302 if (cl.time > p->time2)
1304 p->alpha -= frametime * 256;
1318 printf("unknown particle type %i\n", p->type);
1323 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1324 if (p->die < cl.time)
1325 freeparticles[j++] = p;
1332 // fill in gaps to compact the array
1334 while (maxparticle >= activeparticles)
1336 *freeparticles[i++] = particles[maxparticle--];
1337 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1340 numparticles = activeparticles;
1343 void R_DrawParticles (void)
1346 int i, dynamiclight, staticlight, r, g, b;
1347 byte br, bg, bb, ba;
1348 float scale, scale2, minparticledist;
1350 vec3_t uprightangles, up2, right2, tempcolor, corner;
1353 // LordHavoc: early out condition
1354 if ((!numparticles) || (!r_drawparticles.value))
1357 staticlight = dynamiclight = r_particles_lighting.value;
1358 if (!r_dynamic.value)
1360 c_particles += numparticles;
1362 uprightangles[0] = 0;
1363 uprightangles[1] = r_refdef.viewangles[1];
1364 uprightangles[2] = 0;
1365 AngleVectors (uprightangles, NULL, right2, up2);
1367 minparticledist = DotProduct(r_origin, vpn) + 16.0f;
1369 for (i = 0, p = particles;i < numparticles;i++, p++)
1371 // LordHavoc: unnecessary (array was already compacted)
1372 // if (p->die < cl.time)
1375 // LordHavoc: only render if not too close
1376 if (DotProduct(p->org, vpn) < minparticledist)
1379 // LordHavoc: check if it's in a visible leaf
1380 leaf = Mod_PointInLeaf(p->org, cl.worldmodel);
1381 if (leaf->visframe != r_framecount)
1385 if (p->type == pt_decal)
1387 VectorSubtract(p->org, r_origin, v);
1388 if (DotProduct(p->direction, v) < 0)
1393 color24 = (byte *) &d_8to24table[(int)p->color];
1397 if (staticlight && (p->dynlight || staticlight >= 2)) // LordHavoc: only light blood and smoke
1399 R_CompleteLightPoint(tempcolor, p->org, dynamiclight);
1400 r = (r * (int) tempcolor[0]) >> 7;
1401 g = (g * (int) tempcolor[1]) >> 7;
1402 b = (b * (int) tempcolor[2]) >> 7;
1404 br = (byte) min(r, 255);
1405 bg = (byte) min(g, 255);
1406 bb = (byte) min(b, 255);
1407 ba = (byte) p->alpha;
1408 transpolybegin(R_GetTexture(p->tex), 0, R_GetTexture(p->tex), p->rendermode);
1409 scale = p->scale * -0.5;scale2 = p->scale;
1411 if (p->type == pt_decal)
1413 corner[0] = p->org[0] + p->decalup[0]*scale + p->decalright[0]*scale;
1414 corner[1] = p->org[1] + p->decalup[1]*scale + p->decalright[1]*scale;
1415 corner[2] = p->org[2] + p->decalup[2]*scale + p->decalright[2]*scale;
1416 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1417 transpolyvertub(corner[0] + p->decalup[0]*scale2 , corner[1] + p->decalup[1]*scale2 , corner[2] + p->decalup[2]*scale2 , 0,0,br,bg,bb,ba);
1418 transpolyvertub(corner[0] + p->decalup[0]*scale2 + p->decalright[0]*scale2, corner[1] + p->decalup[1]*scale2 + p->decalright[1]*scale2, corner[2] + p->decalup[2]*scale2 + p->decalright[2]*scale2, 1,0,br,bg,bb,ba);
1419 transpolyvertub(corner[0] + p->decalright[0]*scale2, corner[1] + p->decalright[1]*scale2, corner[2] + p->decalright[2]*scale2, 1,1,br,bg,bb,ba);
1421 else*/ if (p->tex == rainparticletexture) // rain streak
1423 corner[0] = p->org[0] + up2[0]*scale + right2[0]*scale;
1424 corner[1] = p->org[1] + up2[1]*scale + right2[1]*scale;
1425 corner[2] = p->org[2] + up2[2]*scale + right2[2]*scale;
1426 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1427 transpolyvertub(corner[0] + up2[0]*scale2 , corner[1] + up2[1]*scale2 , corner[2] + up2[2]*scale2 , 0,0,br,bg,bb,ba);
1428 transpolyvertub(corner[0] + up2[0]*scale2 + right2[0]*scale2, corner[1] + up2[1]*scale2 + right2[1]*scale2, corner[2] + up2[2]*scale2 + right2[2]*scale2, 1,0,br,bg,bb,ba);
1429 transpolyvertub(corner[0] + right2[0]*scale2, corner[1] + right2[1]*scale2, corner[2] + right2[2]*scale2, 1,1,br,bg,bb,ba);
1433 corner[0] = p->org[0] + vup[0]*scale + vright[0]*scale;
1434 corner[1] = p->org[1] + vup[1]*scale + vright[1]*scale;
1435 corner[2] = p->org[2] + vup[2]*scale + vright[2]*scale;
1436 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1437 transpolyvertub(corner[0] + vup[0]*scale2 , corner[1] + vup[1]*scale2 , corner[2] + vup[2]*scale2 , 0,0,br,bg,bb,ba);
1438 transpolyvertub(corner[0] + vup[0]*scale2 + vright[0]*scale2, corner[1] + vup[1]*scale2 + vright[1]*scale2, corner[2] + vup[2]*scale2 + vright[2]*scale2, 1,0,br,bg,bb,ba);
1439 transpolyvertub(corner[0] + vright[0]*scale2, corner[1] + vright[1]*scale2, corner[2] + vright[2]*scale2, 1,1,br,bg,bb,ba);