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
42 byte dynlight; // if set the particle will be dynamically lit (if r_dynamicparticles is on), used for smoke and blood
43 byte rendermode; // a TPOLYTYPE_ value
47 float time2; // used for various things (snow fluttering, for example)
48 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 of bouncing particles)
50 vec3_t vel2; // used for snow fluttering (base velocity, wind for instance)
51 // vec3_t direction; // used by decals
52 // vec3_t decalright; // used by decals
53 // vec3_t decalup; // used by decals
57 float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);
59 int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
60 int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
61 int ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
63 rtexture_t *particletexture;
64 rtexture_t *smokeparticletexture[8];
65 rtexture_t *rainparticletexture;
66 rtexture_t *bubbleparticletexture;
67 rtexture_t *bulletholetexture[8];
68 rtexture_t *rocketglowparticletexture;
70 particle_t *particles;
73 vec3_t r_pright, r_pup, r_ppn;
76 particle_t **freeparticles; // list used only in compacting particles array
78 cvar_t r_particles = {"r_particles", "1", true};
79 cvar_t r_drawparticles = {"r_drawparticles", "1"};
80 cvar_t r_particles_lighting = {"r_particles_lighting", "1", true};
81 cvar_t r_particles_bloodshowers = {"r_particles_bloodshowers", "1", true};
82 cvar_t r_particles_blood = {"r_particles_blood", "1", true};
83 cvar_t r_particles_smoke = {"r_particles_smoke", "1", true};
84 cvar_t r_particles_sparks = {"r_particles_sparks", "1", true};
85 cvar_t r_particles_bubbles = {"r_particles_bubbles", "1", true};
87 byte shadebubble(float dx, float dy, vec3_t light)
91 dz = 1 - (dx*dx+dy*dy);
92 if (dz > 0) // it does hit the sphere
96 normal[0] = dx;normal[1] = dy;normal[2] = dz;
97 VectorNormalize(normal);
98 dot = DotProduct(normal, light);
99 if (dot > 0.5) // interior reflection
100 f += ((dot * 2) - 1);
101 else if (dot < -0.5) // exterior reflection
102 f += ((dot * -2) - 1);
104 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
105 VectorNormalize(normal);
106 dot = DotProduct(normal, light);
107 if (dot > 0.5) // interior reflection
108 f += ((dot * 2) - 1);
109 else if (dot < -0.5) // exterior reflection
110 f += ((dot * -2) - 1);
112 f += 16; // just to give it a haze so you can see the outline
113 f = bound(0, f, 255);
120 void R_InitParticleTexture (void)
124 byte data[32][32][4], noise1[64][64], noise2[64][64];
127 for (y = 0;y < 32;y++)
130 for (x = 0;x < 32;x++)
132 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
134 d = (256 - (dx*dx+dy*dy));
135 d = bound(0, d, 255);
136 data[y][x][3] = (byte) d;
139 particletexture = R_LoadTexture ("particletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
141 for (i = 0;i < 8;i++)
145 fractalnoise(&noise1[0][0], 64, 4);
146 fractalnoise(&noise2[0][0], 64, 8);
148 for (y = 0;y < 32;y++)
151 for (x = 0;x < 32;x++)
153 d = (noise1[y][x] - 128) * 2 + 128;
154 d = bound(0, d, 255);
155 data[y][x][0] = data[y][x][1] = data[y][x][2] = d;
157 d = (noise2[y][x] - 128) * 4 + 128;
159 d = (d * (256 - (int) (dx*dx+dy*dy))) >> 8;
160 d = bound(0, d, 255);
161 data[y][x][3] = (byte) d;
169 smokeparticletexture[i] = R_LoadTexture (va("smokeparticletexture%d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
172 light[0] = 1;light[1] = 1;light[2] = 1;
173 VectorNormalize(light);
174 for (y = 0;y < 32;y++)
176 for (x = 0;x < 32;x++)
178 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
179 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);
182 rainparticletexture = R_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
184 light[0] = 1;light[1] = 1;light[2] = 1;
185 VectorNormalize(light);
186 for (y = 0;y < 32;y++)
188 for (x = 0;x < 32;x++)
190 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
191 data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
194 bubbleparticletexture = R_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
196 for (i = 0;i < 8;i++)
199 fractalnoise(&noise1[0][0], 64, 8);
200 for (y = 0;y < 32;y++)
201 for (x = 0;x < 32;x++)
202 p[y][x] = (noise1[y][x] / 8.0f) - 64.0f;
203 for (m = 0;m < 32;m++)
207 fx = lhrandom(14, 18);
208 fy = lhrandom(14, 18);
211 dx = lhrandom(-1, 1);
212 dy = lhrandom(-1, 1);
213 f = (dx * dx + dy * dy);
215 while(f < 0.125f || f > 1.0f);
216 f = (m + 1) / 40.0f; //lhrandom(0.0f, 1.0);
219 for (j = 0;f > 0 && j < (32 * 14);j++)
225 p[y - 1][x - 1] += f * 0.125f;
226 p[y - 1][x ] += f * 0.25f;
227 p[y - 1][x + 1] += f * 0.125f;
228 p[y ][x - 1] += f * 0.25f;
230 p[y ][x + 1] += f * 0.25f;
231 p[y + 1][x - 1] += f * 0.125f;
232 p[y + 1][x ] += f * 0.25f;
233 p[y + 1][x + 1] += f * 0.125f;
234 // f -= (0.5f / (32 * 16));
237 for (y = 0;y < 32;y++)
239 for (x = 0;x < 32;x++)
242 data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
243 data[y][x][3] = (byte) bound(0, m, 255);
247 bulletholetexture[i] = R_LoadTexture (va("bulletholetexture%d", i), 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
250 for (y = 0;y < 32;y++)
253 for (x = 0;x < 32;x++)
256 d = (2048.0f / (dx*dx+dy*dy+1)) - 8.0f;
257 data[y][x][0] = bound(0, d * 1.0f, 255);
258 data[y][x][1] = bound(0, d * 0.8f, 255);
259 data[y][x][2] = bound(0, d * 0.5f, 255);
260 data[y][x][3] = bound(0, d * 1.0f, 255);
263 rocketglowparticletexture = R_LoadTexture ("glowparticletexture", 32, 32, &data[0][0][0], TEXF_MIPMAP | TEXF_ALPHA | TEXF_RGBA | TEXF_PRECACHE);
268 particles = (particle_t *) qmalloc(r_numparticles * sizeof(particle_t));
269 freeparticles = (void *) qmalloc(r_numparticles * sizeof(particle_t *));
271 R_InitParticleTexture ();
274 void r_part_shutdown()
278 qfree(freeparticles);
291 void R_ReadPointFile_f (void);
292 void R_Particles_Init (void)
296 i = COM_CheckParm ("-particles");
300 r_numparticles = (int)(atoi(com_argv[i+1]));
301 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
302 r_numparticles = ABSOLUTE_MIN_PARTICLES;
306 r_numparticles = MAX_PARTICLES;
309 Cmd_AddCommand ("pointfile", R_ReadPointFile_f);
311 Cvar_RegisterVariable (&r_particles);
312 Cvar_RegisterVariable (&r_drawparticles);
313 Cvar_RegisterVariable (&r_particles_lighting);
314 Cvar_RegisterVariable (&r_particles_bloodshowers);
315 Cvar_RegisterVariable (&r_particles_blood);
316 Cvar_RegisterVariable (&r_particles_smoke);
317 Cvar_RegisterVariable (&r_particles_sparks);
318 Cvar_RegisterVariable (&r_particles_bubbles);
320 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
323 //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)
324 #define particle(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz)\
327 if (numparticles >= r_numparticles)\
329 part = &particles[numparticles++];\
330 part->type = (ptype);\
331 part->color = (pcolor);\
333 part->dynlight = (plight);\
334 part->rendermode = (prendermode);\
335 part->scale = (pscale);\
336 part->alpha = (palpha);\
337 part->die = cl.time + (ptime);\
338 part->bounce = (pbounce);\
339 part->org[0] = (px);\
340 part->org[1] = (py);\
341 part->org[2] = (pz);\
342 part->vel[0] = (pvx);\
343 part->vel[1] = (pvy);\
344 part->vel[2] = (pvz);\
346 part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
349 #define particle2(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, poscale, pvscale)\
352 if (numparticles >= r_numparticles)\
354 part = &particles[numparticles++];\
355 part->type = (ptype);\
356 part->color = (pcolor);\
358 part->dynlight = (plight);\
359 part->rendermode = (prendermode);\
360 part->scale = (pscale);\
361 part->alpha = (palpha);\
362 part->die = cl.time + (ptime);\
363 part->bounce = (pbounce);\
364 part->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
365 part->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
366 part->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
367 part->vel[0] = lhrandom(-(pvscale), (pvscale));\
368 part->vel[1] = lhrandom(-(pvscale), (pvscale));\
369 part->vel[2] = lhrandom(-(pvscale), (pvscale));\
371 part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
375 #define particle3(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
378 if (numparticles >= r_numparticles)\
380 part = &particles[numparticles++];\
381 part->type = (ptype);\
382 part->color = (pcolor);\
384 part->dynlight = (plight);\
385 part->rendermode = (prendermode);\
386 part->scale = (pscale);\
387 part->alpha = (palpha);\
388 part->die = cl.time + (ptime);\
389 part->bounce = (pbounce);\
390 part->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
391 part->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
392 part->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
393 part->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
394 part->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
395 part->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
397 part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
400 #define particle4(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz, ptime2, pvx2, pvy2, pvz2)\
403 if (numparticles >= r_numparticles)\
405 part = &particles[numparticles++];\
406 part->type = (ptype);\
407 part->color = (pcolor);\
409 part->dynlight = (plight);\
410 part->rendermode = (prendermode);\
411 part->scale = (pscale);\
412 part->alpha = (palpha);\
413 part->die = cl.time + (ptime);\
414 part->bounce = (pbounce);\
415 part->org[0] = (px);\
416 part->org[1] = (py);\
417 part->org[2] = (pz);\
418 part->vel[0] = (pvx);\
419 part->vel[1] = (pvy);\
420 part->vel[2] = (pvz);\
421 part->time2 = (ptime2);\
422 part->vel2[0] = (pvx2);\
423 part->vel2[1] = (pvy2);\
424 part->vel2[2] = (pvz2);\
432 void R_EntityParticles (entity_t *ent)
436 float sp, sy, cp, cy;
440 static vec3_t avelocities[NUMVERTEXNORMALS];
441 if (!r_particles.value) return; // LordHavoc: particles are optional
446 if (!avelocities[0][0])
447 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
448 avelocities[0][i] = (rand()&255) * 0.01;
450 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
452 angle = cl.time * avelocities[i][0];
455 angle = cl.time * avelocities[i][1];
463 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);
468 void R_ReadPointFile_f (void)
474 char name[MAX_OSPATH];
476 sprintf (name,"maps/%s.pts", sv.name);
478 COM_FOpenFile (name, &f, false);
481 Con_Printf ("couldn't open %s\n", name);
485 Con_Printf ("Reading %s...\n", name);
489 r = fscanf (f,"%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) // 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 extern cvar_t sv_gravity;
1019 void R_MoveParticles (void)
1022 int i, activeparticles, maxparticle, j, a;
1024 float gravity, dvel, frametime;
1026 // LordHavoc: early out condition
1030 frametime = cl.time - cl.oldtime;
1032 return; // if absolutely still, don't update particles
1033 gravity = frametime * sv_gravity.value;
1034 dvel = 1+4*frametime;
1036 activeparticles = 0;
1039 for (i = 0, p = particles;i < numparticles;i++, p++)
1041 if (p->die < cl.time)
1043 freeparticles[j++] = p;
1047 VectorCopy(p->org, p->oldorg);
1048 p->org[0] += p->vel[0]*frametime;
1049 p->org[1] += p->vel[1]*frametime;
1050 p->org[2] += p->vel[2]*frametime;
1055 if (TraceLine(p->oldorg, p->org, v, normal) < 1)
1057 VectorCopy(v, p->org);
1060 byte *color24 = (byte *) &d_8to24table[(int)p->color];
1061 R_Decal(v, p->tex, p->scale, color24[0], color24[1], color24[2], p->alpha);
1063 freeparticles[j++] = p;
1066 VectorClear(p->vel);
1068 // have to negate the direction (why?)
1069 VectorNegate(normal, p->direction);
1070 VectorVectors(p->direction, p->decalright, p->decalup);
1071 VectorSubtract(p->org, p->direction, p->org); // push off the surface a bit so it doesn't flicker
1073 p->time2 = cl.time + 30;
1078 dist = DotProduct(p->vel, normal) * -p->bounce;
1079 VectorMAQuick(p->vel, dist, normal, p->vel);
1080 if (DotProduct(p->vel, p->vel) < 0.03)
1081 VectorClear(p->vel);
1091 // LordHavoc: drop-through because of shared code
1097 p->alpha -= frametime * 256;
1103 p->vel[2] -= gravity;
1106 p->vel[2] -= gravity * 0.05;
1109 p->vel[2] -= gravity * 0.05;
1110 p->alpha -= frametime * 192;
1115 if (cl.time > p->time2)
1117 p->time2 = cl.time + (rand() & 3) * 0.1;
1118 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1119 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1120 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1122 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1123 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1126 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1127 break; // still in solid
1128 p->die = cl.time + 1000;
1129 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1133 case CONTENTS_SLIME:
1134 p->tex = smokeparticletexture[rand()&7];
1140 case CONTENTS_WATER:
1141 p->tex = smokeparticletexture[rand()&7];
1142 p->type = pt_splash;
1147 default: // CONTENTS_SOLID and any others
1148 TraceLine(p->oldorg, p->org, v, normal);
1149 VectorCopy(v, p->org);
1150 p->tex = smokeparticletexture[rand()&7];
1152 VectorClear(p->vel);
1158 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1163 p->vel[2] -= gravity * 0.5;
1166 p->alpha -= frametime * 512;
1167 p->vel[2] -= gravity;
1172 p->alpha -= frametime * 512;
1177 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1178 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1180 p->tex = smokeparticletexture[rand()&7];
1181 p->type = pt_splashpuff;
1183 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1186 p->vel[2] += gravity * 0.25;
1187 p->vel[0] *= (1 - (frametime * 0.0625));
1188 p->vel[1] *= (1 - (frametime * 0.0625));
1189 p->vel[2] *= (1 - (frametime * 0.0625));
1190 if (cl.time > p->time2)
1192 p->time2 = cl.time + lhrandom(0, 0.5);
1193 p->vel[0] += lhrandom(-32,32);
1194 p->vel[1] += lhrandom(-32,32);
1195 p->vel[2] += lhrandom(-32,32);
1197 p->alpha -= frametime * 256;
1201 case pt_bulletsmoke:
1202 p->scale += frametime * 16;
1203 p->alpha -= frametime * 1024;
1204 p->vel[2] += gravity * 0.05;
1209 p->scale += frametime * 32;
1210 p->alpha -= frametime * 512;
1211 p->vel[2] += gravity * 0.05;
1216 p->scale += frametime * 48;
1217 p->alpha -= frametime * 512;
1218 p->vel[2] += gravity * 0.05;
1223 // p->scale += frametime * 24;
1224 p->alpha -= frametime * 1024;
1229 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1230 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1233 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1234 break; // still in solid
1235 p->die = cl.time + 1000;
1236 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1240 case CONTENTS_SLIME:
1241 p->tex = smokeparticletexture[rand()&7];
1246 case CONTENTS_WATER:
1247 p->tex = smokeparticletexture[rand()&7];
1248 p->type = pt_splashpuff;
1251 default: // CONTENTS_SOLID and any others
1252 TraceLine(p->oldorg, p->org, v, normal);
1253 VectorCopy(v, p->org);
1254 p->tex = smokeparticletexture[rand()&7];
1255 p->type = pt_splashpuff;
1262 p->alpha -= frametime * 512;
1263 p->vel[2] += gravity;
1264 // p->scale -= frametime * 16;
1269 case pt_flamingdebris:
1270 if (cl.time >= p->time2)
1272 p->time2 = cl.time + 0.01;
1273 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));
1275 p->alpha -= frametime * 512;
1276 p->vel[2] -= gravity * 0.5f;
1277 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1282 case pt_smokingdebris:
1283 if (cl.time >= p->time2)
1285 p->time2 = cl.time + 0.01;
1286 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));
1288 p->alpha -= frametime * 512;
1289 p->vel[2] -= gravity * 0.5f;
1290 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1296 p->alpha -= frametime * 512;
1297 p->vel[2] -= gravity * 0.5f;
1304 if (cl.time > p->time2)
1306 p->alpha -= frametime * 256;
1320 printf("unknown particle type %i\n", p->type);
1325 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1326 if (p->die < cl.time)
1327 freeparticles[j++] = p;
1334 // fill in gaps to compact the array
1336 while (maxparticle >= activeparticles)
1338 *freeparticles[i++] = particles[maxparticle--];
1339 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1342 numparticles = activeparticles;
1345 void R_DrawParticles (void)
1348 int i, dynamiclight, staticlight, r, g, b;
1349 byte br, bg, bb, ba;
1350 float scale, scale2, minparticledist;
1352 vec3_t uprightangles, up2, right2, tempcolor, corner;
1355 // LordHavoc: early out condition
1356 if ((!numparticles) || (!r_drawparticles.value))
1359 staticlight = dynamiclight = r_particles_lighting.value;
1360 if (!r_dynamic.value)
1362 c_particles += numparticles;
1364 uprightangles[0] = 0;
1365 uprightangles[1] = r_refdef.viewangles[1];
1366 uprightangles[2] = 0;
1367 AngleVectors (uprightangles, NULL, right2, up2);
1369 minparticledist = DotProduct(r_refdef.vieworg, vpn) + 16.0f;
1371 for (i = 0, p = particles;i < numparticles;i++, p++)
1373 // LordHavoc: unnecessary (array was already compacted)
1374 // if (p->die < cl.time)
1377 // LordHavoc: only render if not too close
1378 if (DotProduct(p->org, vpn) < minparticledist)
1381 // LordHavoc: check if it's in a visible leaf
1382 leaf = Mod_PointInLeaf(p->org, cl.worldmodel);
1383 if (leaf->visframe != r_framecount)
1387 if (p->type == pt_decal)
1389 VectorSubtract(p->org, r_refdef.vieworg, v);
1390 if (DotProduct(p->direction, v) < 0)
1395 color24 = (byte *) &d_8to24table[(int)p->color];
1399 if (staticlight && (p->dynlight || staticlight >= 2)) // LordHavoc: only light blood and smoke
1401 R_CompleteLightPoint(tempcolor, p->org, dynamiclight);
1402 r = (r * (int) tempcolor[0]) >> 7;
1403 g = (g * (int) tempcolor[1]) >> 7;
1404 b = (b * (int) tempcolor[2]) >> 7;
1406 br = (byte) min(r, 255);
1407 bg = (byte) min(g, 255);
1408 bb = (byte) min(b, 255);
1409 ba = (byte) p->alpha;
1410 transpolybegin(R_GetTexture(p->tex), 0, R_GetTexture(p->tex), p->rendermode);
1411 scale = p->scale * -0.5;scale2 = p->scale;
1413 if (p->type == pt_decal)
1415 corner[0] = p->org[0] + p->decalup[0]*scale + p->decalright[0]*scale;
1416 corner[1] = p->org[1] + p->decalup[1]*scale + p->decalright[1]*scale;
1417 corner[2] = p->org[2] + p->decalup[2]*scale + p->decalright[2]*scale;
1418 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1419 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);
1420 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);
1421 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);
1423 else*/ if (p->tex == rainparticletexture) // rain streak
1425 corner[0] = p->org[0] + up2[0]*scale + right2[0]*scale;
1426 corner[1] = p->org[1] + up2[1]*scale + right2[1]*scale;
1427 corner[2] = p->org[2] + up2[2]*scale + right2[2]*scale;
1428 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1429 transpolyvertub(corner[0] + up2[0]*scale2 , corner[1] + up2[1]*scale2 , corner[2] + up2[2]*scale2 , 0,0,br,bg,bb,ba);
1430 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);
1431 transpolyvertub(corner[0] + right2[0]*scale2, corner[1] + right2[1]*scale2, corner[2] + right2[2]*scale2, 1,1,br,bg,bb,ba);
1435 corner[0] = p->org[0] + vup[0]*scale + vright[0]*scale;
1436 corner[1] = p->org[1] + vup[1]*scale + vright[1]*scale;
1437 corner[2] = p->org[2] + vup[2]*scale + vright[2]*scale;
1438 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1439 transpolyvertub(corner[0] + vup[0]*scale2 , corner[1] + vup[1]*scale2 , corner[2] + vup[2]*scale2 , 0,0,br,bg,bb,ba);
1440 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);
1441 transpolyvertub(corner[0] + vright[0]*scale2, corner[1] + vright[1]*scale2, corner[2] + vright[2]*scale2, 1,1,br,bg,bb,ba);