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);
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()
277 qfree(freeparticles);
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 extern cvar_t sv_gravity;
1018 void R_MoveParticles (void)
1021 int i, activeparticles, maxparticle, j, a;
1023 float gravity, dvel, frametime;
1025 // LordHavoc: early out condition
1029 frametime = cl.time - cl.oldtime;
1031 return; // if absolutely still, don't update particles
1032 gravity = frametime * sv_gravity.value;
1033 dvel = 1+4*frametime;
1035 activeparticles = 0;
1038 for (i = 0, p = particles;i < numparticles;i++, p++)
1040 if (p->die < cl.time)
1042 freeparticles[j++] = p;
1046 VectorCopy(p->org, p->oldorg);
1047 p->org[0] += p->vel[0]*frametime;
1048 p->org[1] += p->vel[1]*frametime;
1049 p->org[2] += p->vel[2]*frametime;
1054 if (TraceLine(p->oldorg, p->org, v, normal) < 1)
1056 VectorCopy(v, p->org);
1059 byte *color24 = (byte *) &d_8to24table[(int)p->color];
1060 R_Decal(v, p->tex, p->scale, color24[0], color24[1], color24[2], p->alpha);
1062 freeparticles[j++] = p;
1065 VectorClear(p->vel);
1067 // have to negate the direction (why?)
1068 VectorNegate(normal, p->direction);
1069 VectorVectors(p->direction, p->decalright, p->decalup);
1070 VectorSubtract(p->org, p->direction, p->org); // push off the surface a bit so it doesn't flicker
1072 p->time2 = cl.time + 30;
1077 dist = DotProduct(p->vel, normal) * -p->bounce;
1078 VectorMAQuick(p->vel, dist, normal, p->vel);
1079 if (DotProduct(p->vel, p->vel) < 0.03)
1080 VectorClear(p->vel);
1090 // LordHavoc: drop-through because of shared code
1096 p->alpha -= frametime * 256;
1102 p->vel[2] -= gravity;
1105 p->vel[2] -= gravity * 0.05;
1108 p->vel[2] -= gravity * 0.05;
1109 p->alpha -= frametime * 192;
1114 if (cl.time > p->time2)
1116 p->time2 = cl.time + (rand() & 3) * 0.1;
1117 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1118 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1119 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1121 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1122 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1125 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1126 break; // still in solid
1127 p->die = cl.time + 1000;
1128 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1132 case CONTENTS_SLIME:
1133 p->tex = smokeparticletexture[rand()&7];
1139 case CONTENTS_WATER:
1140 p->tex = smokeparticletexture[rand()&7];
1141 p->type = pt_splash;
1146 default: // CONTENTS_SOLID and any others
1147 TraceLine(p->oldorg, p->org, v, normal);
1148 VectorCopy(v, p->org);
1149 p->tex = smokeparticletexture[rand()&7];
1151 VectorClear(p->vel);
1157 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1162 p->vel[2] -= gravity * 0.5;
1165 p->alpha -= frametime * 512;
1166 p->vel[2] -= gravity;
1171 p->alpha -= frametime * 512;
1176 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1177 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1179 p->tex = smokeparticletexture[rand()&7];
1180 p->type = pt_splashpuff;
1182 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1185 p->vel[2] += gravity * 0.25;
1186 p->vel[0] *= (1 - (frametime * 0.0625));
1187 p->vel[1] *= (1 - (frametime * 0.0625));
1188 p->vel[2] *= (1 - (frametime * 0.0625));
1189 if (cl.time > p->time2)
1191 p->time2 = cl.time + lhrandom(0, 0.5);
1192 p->vel[0] += lhrandom(-32,32);
1193 p->vel[1] += lhrandom(-32,32);
1194 p->vel[2] += lhrandom(-32,32);
1196 p->alpha -= frametime * 256;
1200 case pt_bulletsmoke:
1201 p->scale += frametime * 16;
1202 p->alpha -= frametime * 1024;
1203 p->vel[2] += gravity * 0.05;
1208 p->scale += frametime * 32;
1209 p->alpha -= frametime * 512;
1210 p->vel[2] += gravity * 0.05;
1215 p->scale += frametime * 48;
1216 p->alpha -= frametime * 512;
1217 p->vel[2] += gravity * 0.05;
1222 // p->scale += frametime * 24;
1223 p->alpha -= frametime * 1024;
1228 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1229 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1232 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1233 break; // still in solid
1234 p->die = cl.time + 1000;
1235 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1239 case CONTENTS_SLIME:
1240 p->tex = smokeparticletexture[rand()&7];
1245 case CONTENTS_WATER:
1246 p->tex = smokeparticletexture[rand()&7];
1247 p->type = pt_splashpuff;
1250 default: // CONTENTS_SOLID and any others
1251 TraceLine(p->oldorg, p->org, v, normal);
1252 VectorCopy(v, p->org);
1253 p->tex = smokeparticletexture[rand()&7];
1254 p->type = pt_splashpuff;
1261 p->alpha -= frametime * 512;
1262 p->vel[2] += gravity;
1263 // p->scale -= frametime * 16;
1268 case pt_flamingdebris:
1269 if (cl.time >= p->time2)
1271 p->time2 = cl.time + 0.01;
1272 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));
1274 p->alpha -= frametime * 512;
1275 p->vel[2] -= gravity * 0.5f;
1276 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1281 case pt_smokingdebris:
1282 if (cl.time >= p->time2)
1284 p->time2 = cl.time + 0.01;
1285 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));
1287 p->alpha -= frametime * 512;
1288 p->vel[2] -= gravity * 0.5f;
1289 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1295 p->alpha -= frametime * 512;
1296 p->vel[2] -= gravity * 0.5f;
1303 if (cl.time > p->time2)
1305 p->alpha -= frametime * 256;
1319 printf("unknown particle type %i\n", p->type);
1324 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1325 if (p->die < cl.time)
1326 freeparticles[j++] = p;
1333 // fill in gaps to compact the array
1335 while (maxparticle >= activeparticles)
1337 *freeparticles[i++] = particles[maxparticle--];
1338 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1341 numparticles = activeparticles;
1344 void R_DrawParticles (void)
1347 int i, dynamiclight, staticlight, r, g, b;
1348 byte br, bg, bb, ba;
1349 float scale, scale2, minparticledist;
1351 vec3_t uprightangles, up2, right2, tempcolor, corner;
1354 // LordHavoc: early out condition
1355 if ((!numparticles) || (!r_drawparticles.value))
1358 staticlight = dynamiclight = r_particles_lighting.value;
1359 if (!r_dynamic.value)
1361 c_particles += numparticles;
1363 uprightangles[0] = 0;
1364 uprightangles[1] = r_refdef.viewangles[1];
1365 uprightangles[2] = 0;
1366 AngleVectors (uprightangles, NULL, right2, up2);
1368 minparticledist = DotProduct(r_origin, vpn) + 16.0f;
1370 for (i = 0, p = particles;i < numparticles;i++, p++)
1372 // LordHavoc: unnecessary (array was already compacted)
1373 // if (p->die < cl.time)
1376 // LordHavoc: only render if not too close
1377 if (DotProduct(p->org, vpn) < minparticledist)
1380 // LordHavoc: check if it's in a visible leaf
1381 leaf = Mod_PointInLeaf(p->org, cl.worldmodel);
1382 if (leaf->visframe != r_framecount)
1386 if (p->type == pt_decal)
1388 VectorSubtract(p->org, r_origin, v);
1389 if (DotProduct(p->direction, v) < 0)
1394 color24 = (byte *) &d_8to24table[(int)p->color];
1398 if (staticlight && (p->dynlight || staticlight >= 2)) // LordHavoc: only light blood and smoke
1400 R_CompleteLightPoint(tempcolor, p->org, dynamiclight);
1401 r = (r * (int) tempcolor[0]) >> 7;
1402 g = (g * (int) tempcolor[1]) >> 7;
1403 b = (b * (int) tempcolor[2]) >> 7;
1405 br = (byte) min(r, 255);
1406 bg = (byte) min(g, 255);
1407 bb = (byte) min(b, 255);
1408 ba = (byte) p->alpha;
1409 transpolybegin(R_GetTexture(p->tex), 0, R_GetTexture(p->tex), p->rendermode);
1410 scale = p->scale * -0.5;scale2 = p->scale;
1412 if (p->type == pt_decal)
1414 corner[0] = p->org[0] + p->decalup[0]*scale + p->decalright[0]*scale;
1415 corner[1] = p->org[1] + p->decalup[1]*scale + p->decalright[1]*scale;
1416 corner[2] = p->org[2] + p->decalup[2]*scale + p->decalright[2]*scale;
1417 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1418 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);
1419 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);
1420 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);
1422 else*/ if (p->tex == rainparticletexture) // rain streak
1424 corner[0] = p->org[0] + up2[0]*scale + right2[0]*scale;
1425 corner[1] = p->org[1] + up2[1]*scale + right2[1]*scale;
1426 corner[2] = p->org[2] + up2[2]*scale + right2[2]*scale;
1427 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1428 transpolyvertub(corner[0] + up2[0]*scale2 , corner[1] + up2[1]*scale2 , corner[2] + up2[2]*scale2 , 0,0,br,bg,bb,ba);
1429 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);
1430 transpolyvertub(corner[0] + right2[0]*scale2, corner[1] + right2[1]*scale2, corner[2] + right2[2]*scale2, 1,1,br,bg,bb,ba);
1434 corner[0] = p->org[0] + vup[0]*scale + vright[0]*scale;
1435 corner[1] = p->org[1] + vup[1]*scale + vright[1]*scale;
1436 corner[2] = p->org[2] + vup[2]*scale + vright[2]*scale;
1437 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1438 transpolyvertub(corner[0] + vup[0]*scale2 , corner[1] + vup[1]*scale2 , corner[2] + vup[2]*scale2 , 0,0,br,bg,bb,ba);
1439 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);
1440 transpolyvertub(corner[0] + vright[0]*scale2, corner[1] + vright[1]*scale2, corner[2] + vright[2]*scale2, 1,1,br,bg,bb,ba);