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];
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);
252 particles = (particle_t *) qmalloc(r_numparticles * sizeof(particle_t));
253 freeparticles = (void *) qmalloc(r_numparticles * sizeof(particle_t *));
255 R_InitParticleTexture ();
258 void r_part_shutdown()
262 qfree(freeparticles);
275 void R_ReadPointFile_f (void);
276 void R_Particles_Init (void)
280 i = COM_CheckParm ("-particles");
284 r_numparticles = (int)(atoi(com_argv[i+1]));
285 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
286 r_numparticles = ABSOLUTE_MIN_PARTICLES;
290 r_numparticles = MAX_PARTICLES;
293 Cmd_AddCommand ("pointfile", R_ReadPointFile_f);
295 Cvar_RegisterVariable (&r_particles);
296 Cvar_RegisterVariable (&r_drawparticles);
297 Cvar_RegisterVariable (&r_particles_lighting);
298 Cvar_RegisterVariable (&r_particles_bloodshowers);
299 Cvar_RegisterVariable (&r_particles_blood);
300 Cvar_RegisterVariable (&r_particles_smoke);
301 Cvar_RegisterVariable (&r_particles_sparks);
302 Cvar_RegisterVariable (&r_particles_bubbles);
304 R_RegisterModule("R_Particles", r_part_start, r_part_shutdown, r_part_newmap);
307 //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)
308 #define particle(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz)\
311 if (numparticles >= r_numparticles)\
313 part = &particles[numparticles++];\
314 part->type = (ptype);\
315 part->color = (pcolor);\
317 part->dynlight = (plight);\
318 part->rendermode = (prendermode);\
319 part->scale = (pscale);\
320 part->alpha = (palpha);\
321 part->die = cl.time + (ptime);\
322 part->bounce = (pbounce);\
323 part->org[0] = (px);\
324 part->org[1] = (py);\
325 part->org[2] = (pz);\
326 part->vel[0] = (pvx);\
327 part->vel[1] = (pvy);\
328 part->vel[2] = (pvz);\
330 part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
333 #define particle2(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, poscale, pvscale)\
336 if (numparticles >= r_numparticles)\
338 part = &particles[numparticles++];\
339 part->type = (ptype);\
340 part->color = (pcolor);\
342 part->dynlight = (plight);\
343 part->rendermode = (prendermode);\
344 part->scale = (pscale);\
345 part->alpha = (palpha);\
346 part->die = cl.time + (ptime);\
347 part->bounce = (pbounce);\
348 part->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
349 part->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
350 part->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
351 part->vel[0] = lhrandom(-(pvscale), (pvscale));\
352 part->vel[1] = lhrandom(-(pvscale), (pvscale));\
353 part->vel[2] = lhrandom(-(pvscale), (pvscale));\
355 part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
359 #define particle3(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
362 if (numparticles >= r_numparticles)\
364 part = &particles[numparticles++];\
365 part->type = (ptype);\
366 part->color = (pcolor);\
368 part->dynlight = (plight);\
369 part->rendermode = (prendermode);\
370 part->scale = (pscale);\
371 part->alpha = (palpha);\
372 part->die = cl.time + (ptime);\
373 part->bounce = (pbounce);\
374 part->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
375 part->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
376 part->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
377 part->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
378 part->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
379 part->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
381 part->vel2[0] = part->vel2[1] = part->vel2[2] = 0;\
384 #define particle4(ptype, pcolor, ptex, prendermode, plight, pscale, palpha, ptime, pbounce, px, py, pz, pvx, pvy, pvz, ptime2, pvx2, pvy2, pvz2)\
387 if (numparticles >= r_numparticles)\
389 part = &particles[numparticles++];\
390 part->type = (ptype);\
391 part->color = (pcolor);\
393 part->dynlight = (plight);\
394 part->rendermode = (prendermode);\
395 part->scale = (pscale);\
396 part->alpha = (palpha);\
397 part->die = cl.time + (ptime);\
398 part->bounce = (pbounce);\
399 part->org[0] = (px);\
400 part->org[1] = (py);\
401 part->org[2] = (pz);\
402 part->vel[0] = (pvx);\
403 part->vel[1] = (pvy);\
404 part->vel[2] = (pvz);\
405 part->time2 = (ptime2);\
406 part->vel2[0] = (pvx2);\
407 part->vel2[1] = (pvy2);\
408 part->vel2[2] = (pvz2);\
416 void R_EntityParticles (entity_t *ent)
420 float sp, sy, cp, cy;
424 static vec3_t avelocities[NUMVERTEXNORMALS];
425 if (!r_particles.value) return; // LordHavoc: particles are optional
430 if (!avelocities[0][0])
431 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
432 avelocities[0][i] = (rand()&255) * 0.01;
434 for (i=0 ; i<NUMVERTEXNORMALS ; i++)
436 angle = cl.time * avelocities[i][0];
439 angle = cl.time * avelocities[i][1];
447 particle(pt_oneframe, 0x6f, particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 9999, 0, ent->origin[0] + m_bytenormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + m_bytenormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + m_bytenormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0);
452 void R_ReadPointFile_f (void)
458 char name[MAX_OSPATH];
460 sprintf (name,"maps/%s.pts", sv.name);
462 COM_FOpenFile (name, &f, false);
465 Con_Printf ("couldn't open %s\n", name);
469 Con_Printf ("Reading %s...\n", name);
473 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
478 if (numparticles >= r_numparticles)
480 Con_Printf ("Not enough free particles\n");
483 particle(pt_static, (-c)&15, particletexture, TPOLYTYPE_ALPHA, false, 2, 255, 99999, 0, org[0], org[1], org[2], 0, 0, 0);
487 Con_Printf ("%i points read\n", c);
492 R_ParseParticleEffect
494 Parse an effect out of the server message
497 void R_ParseParticleEffect (void)
500 int i, count, msgcount, color;
502 for (i=0 ; i<3 ; i++)
503 org[i] = MSG_ReadCoord ();
504 for (i=0 ; i<3 ; i++)
505 dir[i] = MSG_ReadChar () * (1.0/16);
506 msgcount = MSG_ReadByte ();
507 color = MSG_ReadByte ();
514 R_RunParticleEffect (org, dir, color, count);
523 void R_ParticleExplosion (vec3_t org, int smoke)
526 if (!r_particles.value) return; // LordHavoc: particles are optional
528 // particle(pt_smoke, (rand()&7) + 8, smokeparticletexture[rand()&7], TPOLYTYPE_ALPHA, true, 30, 255, 2, org[0], org[1], org[2], 0, 0, 0);
530 i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
531 if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
533 for (i = 0;i < 128;i++)
534 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));
543 float f, forg[3], fvel[3], fvel2[3];
544 // for (i = 0;i < 256;i++)
545 // 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));
546 // for (i = 0;i < 256;i++)
547 // 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));
548 for (i = 0;i < 32;i++)
550 fvel[0] = lhrandom(-150, 150);
551 fvel[1] = lhrandom(-150, 150);
552 fvel[2] = lhrandom(-150, 150) + 80;
553 // 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]);
554 for (j = 0;j < 64;j++)
556 forg[0] = lhrandom(-20, 20) + org[0];
557 forg[1] = lhrandom(-20, 20) + org[1];
558 forg[2] = lhrandom(-20, 20) + org[2];
559 fvel2[0] = fvel[0] + lhrandom(-30, 30);
560 fvel2[1] = fvel[1] + lhrandom(-30, 30);
561 fvel2[2] = fvel[2] + lhrandom(-30, 30);
562 f = lhrandom(0.2, 1);
566 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]);
569 // for (i = 0;i < 16;i++)
570 // 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);
571 // for (i = 0;i < 50;i++)
572 // 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));
573 // for (i = 0;i < 30;i++)
574 // 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));
585 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
588 if (!r_particles.value) return; // LordHavoc: particles are optional
590 for (i = 0;i < 512;i++)
591 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));
600 void R_BlobExplosion (vec3_t org)
603 if (!r_particles.value) return; // LordHavoc: particles are optional
605 for (i = 0;i < 256;i++)
606 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));
607 for (i = 0;i < 256;i++)
608 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));
617 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
619 if (!r_particles.value) return; // LordHavoc: particles are optional
623 R_ParticleExplosion(org, false);
627 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));
630 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
636 void R_SparkShower (vec3_t org, vec3_t dir, int count)
638 if (!r_particles.value) return; // LordHavoc: particles are optional
640 R_Decal(org, bulletholetexture[rand()&7], 16, 0, 0, 0, 255);
643 if (r_particles_smoke.value)
644 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));
646 if (r_particles_sparks.value)
650 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));
654 void R_BloodPuff (vec3_t org, vec3_t vel, int count)
656 // bloodcount is used to accumulate counts too small to cause a blood particle
657 static int bloodcount = 0;
658 if (!r_particles.value) return; // LordHavoc: particles are optional
659 if (!r_particles_blood.value) return;
664 while(bloodcount >= 10)
666 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));
671 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
676 if (!r_particles.value) return; // LordHavoc: particles are optional
677 if (!r_particles_bloodshowers.value) return;
678 if (!r_particles_blood.value) return;
680 VectorSubtract(maxs, mins, diff);
681 center[0] = (mins[0] + maxs[0]) * 0.5;
682 center[1] = (mins[1] + maxs[1]) * 0.5;
683 center[2] = (mins[2] + maxs[2]) * 0.5;
684 // FIXME: change velspeed back to 2.0x after fixing mod
685 velscale[0] = velspeed * 2.0 / diff[0];
686 velscale[1] = velspeed * 2.0 / diff[1];
687 velscale[2] = velspeed * 2.0 / diff[2];
692 org[0] = lhrandom(mins[0], maxs[0]);
693 org[1] = lhrandom(mins[1], maxs[1]);
694 org[2] = lhrandom(mins[2], maxs[2]);
695 vel[0] = (org[0] - center[0]) * velscale[0];
696 vel[1] = (org[1] - center[1]) * velscale[1];
697 vel[2] = (org[2] - center[2]) * velscale[2];
698 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]);
702 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
705 if (!r_particles.value) return; // LordHavoc: particles are optional
706 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
707 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
708 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
711 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));
714 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
718 if (!r_particles.value) return; // LordHavoc: particles are optional
719 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
720 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
721 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
722 if (dir[2] < 0) // falling
724 t = (maxs[2] - mins[2]) / -dir[2];
729 t = (maxs[2] - mins[2]) / dir[2];
732 if (t < 0 || t > 2) // sanity check
740 vel[0] = dir[0] + lhrandom(-16, 16);
741 vel[1] = dir[1] + lhrandom(-16, 16);
742 vel[2] = dir[2] + lhrandom(-32, 32);
743 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]);
749 vel[0] = dir[0] + lhrandom(-16, 16);
750 vel[1] = dir[1] + lhrandom(-16, 16);
751 vel[2] = dir[2] + lhrandom(-32, 32);
752 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]);
756 Host_Error("R_ParticleRain: unknown type %i (0 = rain, 1 = snow)\n", type);
760 void R_FlameCube (vec3_t mins, vec3_t maxs, int count)
763 if (!r_particles.value) return; // LordHavoc: particles are optional
764 if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
765 if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
766 if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
769 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));
772 void R_Flames (vec3_t org, vec3_t vel, int count)
774 if (!r_particles.value) return; // LordHavoc: particles are optional
777 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));
788 void R_LavaSplash (vec3_t origin)
793 if (!r_particles.value) return; // LordHavoc: particles are optional
795 for (i=-128 ; i<128 ; i+=16)
797 for (j=-128 ; j<128 ; j+=16)
799 dir[0] = j + lhrandom(0, 8);
800 dir[1] = i + lhrandom(0, 8);
802 org[0] = origin[0] + dir[0];
803 org[1] = origin[1] + dir[1];
804 org[2] = origin[2] + lhrandom(0, 64);
805 vel = lhrandom(50, 120) / VectorLength(dir); // normalize and scale
806 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);
807 // 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));
818 void R_TeleportSplash (vec3_t org)
821 if (!r_particles.value) return; // LordHavoc: particles are optional
823 for (i=-16 ; i<16 ; i+=8)
824 for (j=-16 ; j<16 ; j+=8)
825 for (k=-24 ; k<32 ; k+=8)
826 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));
829 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
831 vec3_t vec, dir, vel;
832 float len, dec = 0, speed;
833 int contents, bubbles, polytype;
835 if (!r_particles.value) return; // LordHavoc: particles are optional
837 VectorSubtract(end, start, dir);
838 VectorNormalize(dir);
841 if (type == 0) // rocket glow
842 particle(pt_glow, 254, particletexture, TPOLYTYPE_ADD, false, 10, 160, 9999, 0, start[0] - 12 * dir[0], start[1] - 12 * dir[1], start[2] - 12 * dir[2], 0, 0, 0);
847 return; // no particles to spawn this frame (sparse trail)
852 VectorSubtract (end, start, vec);
853 len = VectorNormalizeLength (vec);
856 // advance the trail time
857 ent->trail_time = cl.time;
860 speed = len / (cl.time - cl.oldtime);
861 VectorScale(vec, speed, vel);
863 // advance into this frame to reach the first puff location
864 dec = t - cl.oldtime;
866 VectorMA(start, dec, vec, start);
868 contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
869 if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
871 // advance the trail time
872 ent->trail_time = cl.time;
876 bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
878 polytype = TPOLYTYPE_ALPHA;
879 if (ent->effects & EF_ADDITIVE)
880 polytype = TPOLYTYPE_ADD;
886 case 0: // rocket trail
887 if (!r_particles_smoke.value)
889 else if (bubbles && r_particles_bubbles.value)
892 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));
893 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));
894 particle(pt_smoke, 254, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
899 particle(pt_smoke, 12, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
900 // 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);
901 // 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);
902 // 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);
903 // 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);
907 case 1: // grenade trail
908 // FIXME: make it gradually stop smoking
909 if (!r_particles_smoke.value)
911 else if (bubbles && r_particles_bubbles.value)
914 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));
915 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));
916 particle(pt_smoke, 254, smokeparticletexture[rand()&7], polytype, false, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
921 particle(pt_smoke, 8, smokeparticletexture[rand()&7], polytype, true, 2, 160, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
927 if (!r_particles_blood.value)
932 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));
936 case 4: // slight blood
937 if (!r_particles_blood.value)
942 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));
946 case 3: // green tracer
948 particle(pt_fade, 56, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
951 case 5: // flame tracer
953 particle(pt_fade, 234, smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
956 case 6: // voor trail
957 dec = 0.05f; // sparse trail
958 particle(pt_fade, 152 + (rand()&3), smokeparticletexture[rand()&7], polytype, false, 4, 255, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
961 case 7: // Nehahra smoke tracer
962 if (!r_particles_smoke.value)
967 particle(pt_smoke, 12, smokeparticletexture[rand()&7], polytype, true, 10, 64, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
972 // advance to next time and position
975 VectorMA (start, dec, vec, start);
980 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
984 if (!r_particles.value) return; // LordHavoc: particles are optional
985 if (!r_particles_smoke.value) return;
987 VectorSubtract (end, start, vec);
988 len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
989 VectorScale(vec, 3, vec);
992 particle(pt_smoke, color, particletexture, TPOLYTYPE_ALPHA, false, 8, 192, 9999, 0, start[0], start[1], start[2], 0, 0, 0);
993 VectorAdd (start, vec, start);
1003 extern cvar_t sv_gravity;
1005 void R_MoveParticles (void)
1008 int i, activeparticles, maxparticle, j, a;
1010 float gravity, dvel, frametime;
1012 // LordHavoc: early out condition
1016 frametime = cl.time - cl.oldtime;
1018 return; // if absolutely still, don't update particles
1019 gravity = frametime * sv_gravity.value;
1020 dvel = 1+4*frametime;
1022 activeparticles = 0;
1025 for (i = 0, p = particles;i < numparticles;i++, p++)
1027 if (p->die < cl.time)
1029 freeparticles[j++] = p;
1033 VectorCopy(p->org, p->oldorg);
1034 p->org[0] += p->vel[0]*frametime;
1035 p->org[1] += p->vel[1]*frametime;
1036 p->org[2] += p->vel[2]*frametime;
1041 if (TraceLine(p->oldorg, p->org, v, normal) < 1)
1043 VectorCopy(v, p->org);
1046 byte *color24 = (byte *) &d_8to24table[(int)p->color];
1047 R_Decal(v, p->tex, p->scale, color24[0], color24[1], color24[2], p->alpha);
1049 freeparticles[j++] = p;
1052 VectorClear(p->vel);
1054 // have to negate the direction (why?)
1055 VectorNegate(normal, p->direction);
1056 VectorVectors(p->direction, p->decalright, p->decalup);
1057 VectorSubtract(p->org, p->direction, p->org); // push off the surface a bit so it doesn't flicker
1059 p->time2 = cl.time + 30;
1064 dist = DotProduct(p->vel, normal) * -p->bounce;
1065 VectorMAQuick(p->vel, dist, normal, p->vel);
1066 if (DotProduct(p->vel, p->vel) < 0.03)
1067 VectorClear(p->vel);
1077 // LordHavoc: drop-through because of shared code
1083 p->alpha -= frametime * 256;
1089 p->vel[2] -= gravity;
1092 p->vel[2] -= gravity * 0.05;
1095 p->vel[2] -= gravity * 0.05;
1096 p->alpha -= frametime * 192;
1101 if (cl.time > p->time2)
1103 p->time2 = cl.time + (rand() & 3) * 0.1;
1104 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1105 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1106 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1108 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1109 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1112 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1113 break; // still in solid
1114 p->die = cl.time + 1000;
1115 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1119 case CONTENTS_SLIME:
1120 p->tex = smokeparticletexture[rand()&7];
1126 case CONTENTS_WATER:
1127 p->tex = smokeparticletexture[rand()&7];
1128 p->type = pt_splash;
1133 default: // CONTENTS_SOLID and any others
1134 TraceLine(p->oldorg, p->org, v, normal);
1135 VectorCopy(v, p->org);
1136 p->tex = smokeparticletexture[rand()&7];
1138 VectorClear(p->vel);
1144 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1149 p->vel[2] -= gravity * 0.5;
1152 p->alpha -= frametime * 512;
1153 p->vel[2] -= gravity;
1158 p->alpha -= frametime * 512;
1163 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1164 if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1166 p->tex = smokeparticletexture[rand()&7];
1167 p->type = pt_splashpuff;
1169 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1172 p->vel[2] += gravity * 0.25;
1173 p->vel[0] *= (1 - (frametime * 0.0625));
1174 p->vel[1] *= (1 - (frametime * 0.0625));
1175 p->vel[2] *= (1 - (frametime * 0.0625));
1176 if (cl.time > p->time2)
1178 p->time2 = cl.time + lhrandom(0, 0.5);
1179 p->vel[0] += lhrandom(-32,32);
1180 p->vel[1] += lhrandom(-32,32);
1181 p->vel[2] += lhrandom(-32,32);
1183 p->alpha -= frametime * 256;
1187 case pt_bulletsmoke:
1188 p->scale += frametime * 16;
1189 p->alpha -= frametime * 1024;
1190 p->vel[2] += gravity * 0.05;
1195 p->scale += frametime * 32;
1196 p->alpha -= frametime * 512;
1197 p->vel[2] += gravity * 0.05;
1202 p->scale += frametime * 48;
1203 p->alpha -= frametime * 512;
1204 p->vel[2] += gravity * 0.05;
1209 // p->scale += frametime * 24;
1210 p->alpha -= frametime * 1024;
1215 a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1216 if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1219 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1220 break; // still in solid
1221 p->die = cl.time + 1000;
1222 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1226 case CONTENTS_SLIME:
1227 p->tex = smokeparticletexture[rand()&7];
1232 case CONTENTS_WATER:
1233 p->tex = smokeparticletexture[rand()&7];
1234 p->type = pt_splashpuff;
1237 default: // CONTENTS_SOLID and any others
1238 TraceLine(p->oldorg, p->org, v, normal);
1239 VectorCopy(v, p->org);
1240 p->tex = smokeparticletexture[rand()&7];
1241 p->type = pt_splashpuff;
1248 p->alpha -= frametime * 512;
1249 p->vel[2] += gravity;
1250 // p->scale -= frametime * 16;
1255 case pt_flamingdebris:
1256 if (cl.time >= p->time2)
1258 p->time2 = cl.time + 0.01;
1259 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));
1261 p->alpha -= frametime * 512;
1262 p->vel[2] -= gravity * 0.5f;
1263 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1268 case pt_smokingdebris:
1269 if (cl.time >= p->time2)
1271 p->time2 = cl.time + 0.01;
1272 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));
1274 p->alpha -= frametime * 512;
1275 p->vel[2] -= gravity * 0.5f;
1276 if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1282 p->alpha -= frametime * 512;
1283 p->vel[2] -= gravity * 0.5f;
1290 if (cl.time > p->time2)
1292 p->alpha -= frametime * 256;
1306 printf("unknown particle type %i\n", p->type);
1311 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1312 if (p->die < cl.time)
1313 freeparticles[j++] = p;
1320 // fill in gaps to compact the array
1322 while (maxparticle >= activeparticles)
1324 *freeparticles[i++] = particles[maxparticle--];
1325 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1328 numparticles = activeparticles;
1331 void R_DrawParticles (void)
1334 int i, dynamiclight, staticlight, r, g, b;
1335 byte br, bg, bb, ba;
1336 float scale, scale2, minparticledist;
1338 vec3_t uprightangles, up2, right2, tempcolor, corner;
1340 // LordHavoc: early out condition
1341 if ((!numparticles) || (!r_drawparticles.value))
1344 staticlight = dynamiclight = r_particles_lighting.value;
1345 if (!r_dynamic.value)
1347 c_particles += numparticles;
1349 uprightangles[0] = 0;
1350 uprightangles[1] = r_refdef.viewangles[1];
1351 uprightangles[2] = 0;
1352 AngleVectors (uprightangles, NULL, right2, up2);
1354 minparticledist = DotProduct(r_refdef.vieworg, vpn) + 16.0f;
1356 for (i = 0, p = particles;i < numparticles;i++, p++)
1358 // LordHavoc: unnecessary (array was already compacted)
1359 // if (p->die < cl.time)
1362 // LordHavoc: only render if not too close
1363 if (DotProduct(p->org, vpn) < minparticledist)
1367 if (p->type == pt_decal)
1369 VectorSubtract(p->org, r_refdef.vieworg, v);
1370 if (DotProduct(p->direction, v) < 0)
1375 color24 = (byte *) &d_8to24table[(int)p->color];
1379 if (staticlight && (p->dynlight || staticlight >= 2)) // LordHavoc: only light blood and smoke
1381 R_CompleteLightPoint(tempcolor, p->org, dynamiclight);
1382 r = (r * (int) tempcolor[0]) >> 7;
1383 g = (g * (int) tempcolor[1]) >> 7;
1384 b = (b * (int) tempcolor[2]) >> 7;
1386 br = (byte) min(r, 255);
1387 bg = (byte) min(g, 255);
1388 bb = (byte) min(b, 255);
1389 ba = (byte) p->alpha;
1390 transpolybegin(R_GetTexture(p->tex), 0, R_GetTexture(p->tex), p->rendermode);
1391 scale = p->scale * -0.5;scale2 = p->scale;
1393 if (p->type == pt_decal)
1395 corner[0] = p->org[0] + p->decalup[0]*scale + p->decalright[0]*scale;
1396 corner[1] = p->org[1] + p->decalup[1]*scale + p->decalright[1]*scale;
1397 corner[2] = p->org[2] + p->decalup[2]*scale + p->decalright[2]*scale;
1398 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1399 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);
1400 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);
1401 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);
1403 else*/ if (p->tex == rainparticletexture) // rain streak
1405 corner[0] = p->org[0] + up2[0]*scale + right2[0]*scale;
1406 corner[1] = p->org[1] + up2[1]*scale + right2[1]*scale;
1407 corner[2] = p->org[2] + up2[2]*scale + right2[2]*scale;
1408 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1409 transpolyvertub(corner[0] + up2[0]*scale2 , corner[1] + up2[1]*scale2 , corner[2] + up2[2]*scale2 , 0,0,br,bg,bb,ba);
1410 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);
1411 transpolyvertub(corner[0] + right2[0]*scale2, corner[1] + right2[1]*scale2, corner[2] + right2[2]*scale2, 1,1,br,bg,bb,ba);
1415 corner[0] = p->org[0] + vup[0]*scale + vright[0]*scale;
1416 corner[1] = p->org[1] + vup[1]*scale + vright[1]*scale;
1417 corner[2] = p->org[2] + vup[2]*scale + vright[2]*scale;
1418 transpolyvertub(corner[0] , corner[1] , corner[2] , 0,1,br,bg,bb,ba);
1419 transpolyvertub(corner[0] + vup[0]*scale2 , corner[1] + vup[1]*scale2 , corner[2] + vup[2]*scale2 , 0,0,br,bg,bb,ba);
1420 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);
1421 transpolyvertub(corner[0] + vright[0]*scale2, corner[1] + vright[1]*scale2, corner[2] + vright[2]*scale2, 1,1,br,bg,bb,ba);