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);
480 Con_Printf ("couldn't open %s\n", name);
484 Con_Printf ("Reading %s...\n", name);
488 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
493 if (numparticles >= r_numparticles)
495 Con_Printf ("Not enough free particles\n");
498 particle(pt_static, (-c)&15, particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 99999, 0, org[0], org[1], org[2], 0, 0, 0);
502 Con_Printf ("%i points read\n", c);
507 R_ParseParticleEffect
509 Parse an effect out of the server message
512 void R_ParseParticleEffect (void)
515 int i, count, msgcount, color;
517 for (i=0 ; i<3 ; i++)
518 org[i] = MSG_ReadCoord ();
519 for (i=0 ; i<3 ; i++)
520 dir[i] = MSG_ReadChar () * (1.0/16);
521 msgcount = MSG_ReadByte ();
522 color = MSG_ReadByte ();
529 R_RunParticleEffect (org, dir, color, count);
538 void R_ParticleExplosion (vec3_t org, int smoke)
541 if (!r_particles.value) return; // LordHavoc: particles are optional
543 // particle(pt_smoke, (rand()&7) + 8, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 30, 255, 2, org[0], org[1], org[2], 0, 0, 0);
545 i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
546 if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
548 for (i = 0;i < 128;i++)
549 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));
558 float f, forg[3], fvel[3], fvel2[3];
559 // for (i = 0;i < 256;i++)
560 // 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));
561 // for (i = 0;i < 256;i++)
562 // 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));
563 for (i = 0;i < 32;i++)
565 fvel[0] = lhrandom(-150, 150);
566 fvel[1] = lhrandom(-150, 150);
567 fvel[2] = lhrandom(-150, 150) + 80;
568 // 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]);
569 for (j = 0;j < 64;j++)
571 forg[0] = lhrandom(-20, 20) + org[0];
572 forg[1] = lhrandom(-20, 20) + org[1];
573 forg[2] = lhrandom(-20, 20) + org[2];
574 fvel2[0] = fvel[0] + lhrandom(-30, 30);
575 fvel2[1] = fvel[1] + lhrandom(-30, 30);
576 fvel2[2] = fvel[2] + lhrandom(-30, 30);
577 f = lhrandom(0.2, 1);
581 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]);
584 // for (i = 0;i < 16;i++)
585 // 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);
586 // for (i = 0;i < 50;i++)
587 // 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));
588 // for (i = 0;i < 30;i++)
589 // 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));
600 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
603 if (!r_particles.value) return; // LordHavoc: particles are optional
605 for (i = 0;i < 512;i++)
606 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));
615 void R_BlobExplosion (vec3_t org)
618 if (!r_particles.value) return; // LordHavoc: particles are optional
620 for (i = 0;i < 256;i++)
621 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));
622 for (i = 0;i < 256;i++)
623 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));
632 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
634 if (!r_particles.value) return; // LordHavoc: particles are optional
638 R_ParticleExplosion(org, false);
642 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));
645 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
651 void R_SparkShower (vec3_t org, vec3_t dir, int count)
653 if (!r_particles.value) return; // LordHavoc: particles are optional
655 R_Decal(org, bulletholetexture[rand()&7], 16, 0, 0, 0, 255);
658 if (r_particles_smoke.value)
659 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));
661 if (r_particles_sparks.value)
665 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));
669 void R_BloodPuff (vec3_t org, vec3_t vel, int count)
671 // bloodcount is used to accumulate counts too small to cause a blood particle
672 static int bloodcount = 0;
673 if (!r_particles.value) return; // LordHavoc: particles are optional
674 if (!r_particles_blood.value) return;
679 while(bloodcount >= 10)
681 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));
686 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
691 if (!r_particles.value) return; // LordHavoc: particles are optional
692 if (!r_particles_bloodshowers.value) return;
693 if (!r_particles_blood.value) return;
695 VectorSubtract(maxs, mins, diff);
696 center[0] = (mins[0] + maxs[0]) * 0.5;
697 center[1] = (mins[1] + maxs[1]) * 0.5;
698 center[2] = (mins[2] + maxs[2]) * 0.5;
699 // FIXME: change velspeed back to 2.0x after fixing mod
700 velscale[0] = velspeed * 2.0 / diff[0];
701 velscale[1] = velspeed * 2.0 / diff[1];
702 velscale[2] = velspeed * 2.0 / diff[2];
707 org[0] = lhrandom(mins[0], maxs[0]);
708 org[1] = lhrandom(mins[1], maxs[1]);
709 org[2] = lhrandom(mins[2], maxs[2]);
710 vel[0] = (org[0] - center[0]) * velscale[0];
711 vel[1] = (org[1] - center[1]) * velscale[1];
712 vel[2] = (org[2] - center[2]) * velscale[2];
713 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]);
717 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
720 if (!r_particles.value) return; // LordHavoc: particles are optional
721 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
722 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
723 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
726 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));
729 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
733 if (!r_particles.value) return; // LordHavoc: particles are optional
734 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
735 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
736 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
737 if (dir[2] < 0) // falling
739 t = (maxs[2] - mins[2]) / -dir[2];
744 t = (maxs[2] - mins[2]) / dir[2];
747 if (t < 0 || t > 2) // sanity check
755 vel[0] = dir[0] + lhrandom(-16, 16);
756 vel[1] = dir[1] + lhrandom(-16, 16);
757 vel[2] = dir[2] + lhrandom(-32, 32);
758 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]);
764 vel[0] = dir[0] + lhrandom(-16, 16);
765 vel[1] = dir[1] + lhrandom(-16, 16);
766 vel[2] = dir[2] + lhrandom(-32, 32);
767 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]);
771 Host_Error("R_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
775 void R_FlameCube (vec3_t mins, vec3_t maxs, int count)
778 if (!r_particles.value) return; // LordHavoc: particles are optional
779 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
780 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
781 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
784 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));
787 void R_Flames (vec3_t org, vec3_t vel, int count)
789 if (!r_particles.value) return; // LordHavoc: particles are optional
792 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));
803 void R_LavaSplash (vec3_t origin)
808 if (!r_particles.value) return; // LordHavoc: particles are optional
810 for (i=-128 ; i<128 ; i+=16)
812 for (j=-128 ; j<128 ; j+=16)
814 dir[0] = j + lhrandom(0, 8);
815 dir[1] = i + lhrandom(0, 8);
817 org[0] = origin[0] + dir[0];
818 org[1] = origin[1] + dir[1];
819 org[2] = origin[2] + lhrandom(0, 64);
820 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
821 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);
822 // 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));
833 void R_TeleportSplash (vec3_t org)
836 if (!r_particles.value) return; // LordHavoc: particles are optional
838 for (i=-16 ; i<16 ; i+=8)
839 for (j=-16 ; j<16 ; j+=8)
840 for (k=-24 ; k<32 ; k+=8)
841 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));
844 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
846 vec3_t vec, dir, vel;
847 float len, dec = 0, speed;
848 int contents, bubbles, polytype;
850 if (!r_particles.value) return; // LordHavoc: particles are optional
852 VectorSubtract(end, start, dir);
853 VectorNormalize(dir);
855 if (type == 0 && host_frametime != 0) // rocket glow
856 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);
858 t = ent->render.trail_time;
860 return; // no particles to spawn this frame (sparse trail)
865 VectorSubtract (end, start, vec);
866 len = VectorNormalizeLength (vec);
869 // advance the trail time
870 ent->render.trail_time = cl.time;
873 speed = len / (cl.time - cl.oldtime);
874 VectorScale(vec, speed, vel);
876 // advance into this frame to reach the first puff location
877 dec = t - cl.oldtime;
879 VectorMA(start, dec, vec, start);
881 contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
882 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
884 // advance the trail time
885 ent->render.trail_time = cl.time;
889 bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
891 polytype = TPOLYTYPE_ALPHA;
892 if (ent->render.effects & EF_ADDITIVE)
893 polytype = TPOLYTYPE_ADD;
899 case 0: // rocket trail
900 if (!r_particles_smoke.value)
902 else if (bubbles && r_particles_bubbles.value)
905 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));
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_smoke, 254, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
912 particle(pt_smoke, 12, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
913 // 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);
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);
920 case 1: // grenade trail
921 // FIXME: make it gradually stop smoking
922 if (!r_particles_smoke.value)
924 else if (bubbles && r_particles_bubbles.value)
927 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));
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_smoke, 254, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
934 particle(pt_smoke, 8, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
940 if (!r_particles_blood.value)
945 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));
949 case 4: // slight blood
950 if (!r_particles_blood.value)
955 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));
959 case 3: // green tracer
961 particle(pt_fade, 56, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
964 case 5: // flame tracer
966 particle(pt_fade, 234, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
969 case 6: // voor trail
970 dec = 0.05f; // sparse trail
971 particle(pt_fade, 152 + (rand()&3), smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
974 case 7: // Nehahra smoke tracer
975 if (!r_particles_smoke.value)
980 particle(pt_smoke, 12, smokeparticletexture[rand()&7], polytype, true, 10, 64, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
985 // advance to next time and position
988 VectorMA (start, dec, vec, start);
990 ent->render.trail_time = t;
993 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
997 if (!r_particles.value) return; // LordHavoc: particles are optional
998 if (!r_particles_smoke.value) return;
1000 VectorSubtract (end, start, vec);
1001 len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
1002 VectorScale(vec, 3, vec);
1005 particle(pt_smoke, color, particletexture, TPOLYTYPE_ALPHA, false, 8, 192, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
1006 VectorAdd (start, vec, start);
1016 void R_MoveParticles (void)
1019 int i, activeparticles, maxparticle, j, a;
1021 float gravity, dvel, frametime;
1023 // LordHavoc: early out condition
1027 frametime = cl.time - cl.oldtime;
1029 return; // if absolutely still, don't update particles
1030 gravity = frametime * sv_gravity.value;
1031 dvel = 1+4*frametime;
1033 activeparticles = 0;
1036 for (i = 0, p = particles;i < numparticles;i++, p++)
1038 if (p->die < cl.time)
1040 freeparticles[j++] = p;
1044 VectorCopy(p->org, p->oldorg);
1045 p->org[0] += p->vel[0]*frametime;
1046 p->org[1] += p->vel[1]*frametime;
1047 p->org[2] += p->vel[2]*frametime;
1052 if (TraceLine(p->oldorg, p->org, v, normal) < 1)
1054 VectorCopy(v, p->org);
1057 byte *color24 = (byte *) &d_8to24table[(int)p->color];
1058 R_Decal(v, p->tex, p->scale, color24[0], color24[1], color24[2], p->alpha);
1060 freeparticles[j++] = p;
1063 VectorClear(p->vel);
1065 // have to negate the direction (why?)
1066 VectorNegate(normal, p->direction);
1067 VectorVectors(p->direction, p->decalright, p->decalup);
1068 VectorSubtract(p->org, p->direction, p->org); // push off the surface a bit so it doesn't flicker
1070 p->time2 = cl.time + 30;
1075 dist = DotProduct(p->vel, normal) * -p->bounce;
1076 VectorMAQuick(p->vel, dist, normal, p->vel);
1077 if (DotProduct(p->vel, p->vel) < 0.03)
1078 VectorClear(p->vel);
1088 // LordHavoc: drop-through because of shared code
1094 p->alpha -= frametime * 256;
1100 p->vel[2] -= gravity;
1103 p->vel[2] -= gravity * 0.05;
1106 p->vel[2] -= gravity * 0.05;
1107 p->alpha -= frametime * 192;
1112 if (cl.time > p->time2)
1114 p->time2 = cl.time + (rand() & 3) * 0.1;
1115 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1116 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1117 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1119 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1120 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1123 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1124 break; // still in solid
1125 p->die = cl.time + 1000;
1126 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1130 case CONTENTS_SLIME:
1131 p->tex = smokeparticletexture[rand()&7];
1137 case CONTENTS_WATER:
1138 p->tex = smokeparticletexture[rand()&7];
1139 p->type = pt_splash;
1144 default: // CONTENTS_SOLID and any others
1145 TraceLine(p->oldorg, p->org, v, normal);
1146 VectorCopy(v, p->org);
1147 p->tex = smokeparticletexture[rand()&7];
1149 VectorClear(p->vel);
1155 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1160 p->vel[2] -= gravity * 0.5;
1163 p->alpha -= frametime * 512;
1164 p->vel[2] -= gravity;
1169 p->alpha -= frametime * 512;
1174 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1175 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1177 p->tex = smokeparticletexture[rand()&7];
1178 p->type = pt_splashpuff;
1180 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1183 p->vel[2] += gravity * 0.25;
1184 p->vel[0] *= (1 - (frametime * 0.0625));
1185 p->vel[1] *= (1 - (frametime * 0.0625));
1186 p->vel[2] *= (1 - (frametime * 0.0625));
1187 if (cl.time > p->time2)
1189 p->time2 = cl.time + lhrandom(0, 0.5);
1190 p->vel[0] += lhrandom(-32,32);
1191 p->vel[1] += lhrandom(-32,32);
1192 p->vel[2] += lhrandom(-32,32);
1194 p->alpha -= frametime * 256;
1198 case pt_bulletsmoke:
1199 p->scale += frametime * 16;
1200 p->alpha -= frametime * 1024;
1201 p->vel[2] += gravity * 0.05;
1206 p->scale += frametime * 32;
1207 p->alpha -= frametime * 512;
1208 p->vel[2] += gravity * 0.05;
1213 p->scale += frametime * 48;
1214 p->alpha -= frametime * 512;
1215 p->vel[2] += gravity * 0.05;
1220 // p->scale += frametime * 24;
1221 p->alpha -= frametime * 1024;
1226 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1227 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1230 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1231 break; // still in solid
1232 p->die = cl.time + 1000;
1233 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1237 case CONTENTS_SLIME:
1238 p->tex = smokeparticletexture[rand()&7];
1243 case CONTENTS_WATER:
1244 p->tex = smokeparticletexture[rand()&7];
1245 p->type = pt_splashpuff;
1248 default: // CONTENTS_SOLID and any others
1249 TraceLine(p->oldorg, p->org, v, normal);
1250 VectorCopy(v, p->org);
1251 p->tex = smokeparticletexture[rand()&7];
1252 p->type = pt_splashpuff;
1259 p->alpha -= frametime * 512;
1260 p->vel[2] += gravity;
1261 // p->scale -= frametime * 16;
1266 case pt_flamingdebris:
1267 if (cl.time >= p->time2)
1269 p->time2 = cl.time + 0.01;
1270 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));
1272 p->alpha -= frametime * 512;
1273 p->vel[2] -= gravity * 0.5f;
1274 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1279 case pt_smokingdebris:
1280 if (cl.time >= p->time2)
1282 p->time2 = cl.time + 0.01;
1283 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));
1285 p->alpha -= frametime * 512;
1286 p->vel[2] -= gravity * 0.5f;
1287 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1293 p->alpha -= frametime * 512;
1294 p->vel[2] -= gravity * 0.5f;
1301 if (cl.time > p->time2)
1303 p->alpha -= frametime * 256;
1317 printf("unknown particle type %i\n", p->type);
1322 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1323 if (p->die < cl.time)
1324 freeparticles[j++] = p;
1331 // fill in gaps to compact the array
1333 while (maxparticle >= activeparticles)
1335 *freeparticles[i++] = particles[maxparticle--];
1336 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1339 numparticles = activeparticles;
1342 void R_DrawParticles (void)
1345 int i, dynamiclight, staticlight, r, g, b;
1346 byte br, bg, bb, ba;
1347 float scale, scale2, minparticledist;
1349 vec3_t uprightangles, up2, right2, tempcolor, corner;
1352 // LordHavoc: early out condition
1353 if ((!numparticles) || (!r_drawparticles.value))
1356 staticlight = dynamiclight = r_particles_lighting.value;
1357 if (!r_dynamic.value)
1359 c_particles += numparticles;
1361 uprightangles[0] = 0;
1362 uprightangles[1] = r_refdef.viewangles[1];
1363 uprightangles[2] = 0;
1364 AngleVectors (uprightangles, NULL, right2, up2);
1366 minparticledist = DotProduct(r_origin, vpn) + 16.0f;
1368 for (i = 0, p = particles;i < numparticles;i++, p++)
1370 // LordHavoc: unnecessary (array was already compacted)
1371 // if (p->die < cl.time)
1374 // LordHavoc: only render if not too close
1375 if (DotProduct(p->org, vpn) < minparticledist)
1378 // LordHavoc: check if it's in a visible leaf
1379 leaf = Mod_PointInLeaf(p->org, cl.worldmodel);
1380 if (leaf->visframe != r_framecount)
1384 if (p->type == pt_decal)
1386 VectorSubtract(p->org, r_origin, v);
1387 if (DotProduct(p->direction, v) < 0)
1392 color24 = (byte *) &d_8to24table[(int)p->color];
1396 if (staticlight && (p->dynlight || staticlight >= 2)) // LordHavoc: only light blood and smoke
1398 R_CompleteLightPoint(tempcolor, p->org, dynamiclight);
1399 r = (r * (int) tempcolor[0]) >> 7;
1400 g = (g * (int) tempcolor[1]) >> 7;
1401 b = (b * (int) tempcolor[2]) >> 7;
1403 br = (byte) min(r, 255);
1404 bg = (byte) min(g, 255);
1405 bb = (byte) min(b, 255);
1406 ba = (byte) p->alpha;
1407 transpolybegin(R_GetTexture(p->tex), 0, R_GetTexture(p->tex), p->rendermode);
1408 scale = p->scale * -0.5;scale2 = p->scale;
1410 if (p->type == pt_decal)
1412 corner[0] = p->org[0] + p->decalup[0]*scale + p->decalright[0]*scale;
1413 corner[1] = p->org[1] + p->decalup[1]*scale + p->decalright[1]*scale;
1414 corner[2] = p->org[2] + p->decalup[2]*scale + p->decalright[2]*scale;
1415 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1416 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);
1417 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);
1418 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);
1420 else*/ if (p->tex == rainparticletexture) // rain streak
1422 corner[0] = p->org[0] + up2[0]*scale + right2[0]*scale;
1423 corner[1] = p->org[1] + up2[1]*scale + right2[1]*scale;
1424 corner[2] = p->org[2] + up2[2]*scale + right2[2]*scale;
1425 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1426 transpolyvertub(corner[0] + up2[0]*scale2 , corner[1] + up2[1]*scale2 , corner[2] + up2[2]*scale2 , 0,0,br,bg,bb,ba);
1427 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);
1428 transpolyvertub(corner[0] + right2[0]*scale2, corner[1] + right2[1]*scale2, corner[2] + right2[2]*scale2, 1,1,br,bg,bb,ba);
1432 corner[0] = p->org[0] + vup[0]*scale + vright[0]*scale;
1433 corner[1] = p->org[1] + vup[1]*scale + vright[1]*scale;
1434 corner[2] = p->org[2] + vup[2]*scale + vright[2]*scale;
1435 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1436 transpolyvertub(corner[0] + vup[0]*scale2 , corner[1] + vup[1]*scale2 , corner[2] + vup[2]*scale2 , 0,0,br,bg,bb,ba);
1437 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);
1438 transpolyvertub(corner[0] + vright[0]*scale2, corner[1] + vright[1]*scale2, corner[2] + vright[2]*scale2, 1,1,br,bg,bb,ba);