]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - r_part.c
improved TraceLine in chase.c to be more generally useful (should move it to another...
[xonotic/darkplaces.git] / r_part.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.  
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20
21 #include "quakedef.h"
22
23 #define MAX_PARTICLES                   4096    // 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
25
26 // LordHavoc: added dust, smoke, snow, bloodcloud, and many others
27 typedef enum
28 {
29         pt_static, pt_grav, pt_blob, pt_blob2, pt_smoke, pt_snow, pt_rain, pt_bloodcloud, pt_fallfadespark, pt_bubble, pt_fade, pt_smokecloud, pt_splash, pt_flame, pt_flamingdebris, pt_smokingdebris, pt_flamefall
30 }
31 ptype_t;
32
33 typedef struct particle_s
34 {
35         vec3_t          org;
36         float           color;
37         vec3_t          vel;
38         float           die;
39         ptype_t         type;
40         float           scale;
41         short           texnum;
42         short           dynlight; // if set the particle will be dynamically lit (if r_dynamicparticles is on), used for smoke and blood
43         float           alpha; // 0-255
44         float           time2; // used for various things (snow fluttering, for example)
45         vec3_t          oldorg;
46         vec3_t          vel2; // used for snow fluttering (base velocity, wind for instance)
47 }
48 particle_t;
49
50 float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal);
51
52 int             ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61};
53 int             ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66};
54 int             ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
55
56 int             particletexture;
57 int             smokeparticletexture[8];
58 int             rainparticletexture;
59 int             bubbleparticletexture;
60 int             explosiontexture;
61 int             explosiontexturefog;
62
63 particle_t      *particles;
64 int                     r_numparticles;
65
66 vec3_t                  r_pright, r_pup, r_ppn;
67
68 int                     numparticles;
69 particle_t      **freeparticles; // list used only in compacting particles array
70
71 // LordHavoc: reduced duplicate code, and allow particle allocation system independence
72 #define ALLOCPARTICLE(part) \
73         if (numparticles >= r_numparticles)\
74                 return;\
75         (part) = &particles[numparticles++];
76
77 cvar_t r_particles = {"r_particles", "1"};
78 cvar_t r_drawparticles = {"r_drawparticles", "1"};
79 cvar_t r_dynamicparticles = {"r_dynamicparticles", "0", TRUE};
80
81 byte shadebubble(float dx, float dy, vec3_t light)
82 {
83         float   dz, f, dot;
84         vec3_t  normal;
85         if ((dx*dx+dy*dy) < 1) // it does hit the sphere
86         {
87                 dz = 1 - (dx*dx+dy*dy);
88                 f = 0;
89                 // back side
90                 normal[0] = dx;normal[1] = dy;normal[2] = dz;
91                 VectorNormalize(normal);
92                 dot = DotProduct(normal, light);
93                 if (dot > 0.5) // interior reflection
94                         f += ((dot *  2) - 1);
95                 else if (dot < -0.5) // exterior reflection
96                         f += ((dot * -2) - 1);
97                 // front side
98                 normal[0] = dx;normal[1] = dy;normal[2] = -dz;
99                 VectorNormalize(normal);
100                 dot = DotProduct(normal, light);
101                 if (dot > 0.5) // interior reflection
102                         f += ((dot *  2) - 1);
103                 else if (dot < -0.5) // exterior reflection
104                         f += ((dot * -2) - 1);
105                 f *= 128;
106                 f += 16; // just to give it a haze so you can see the outline
107                 f = bound(0, f, 255);
108                 return (byte) f;
109         }
110         else
111                 return 0;
112 }
113
114 void R_InitParticleTexture (void)
115 {
116         int             x,y,d,i,m;
117         float   dx, dy;
118         byte    data[32][32][4], noise1[128][128], noise2[128][128];
119         vec3_t  light;
120
121         for (y = 0;y < 32;y++)
122         {
123                 dy = y - 16;
124                 for (x = 0;x < 32;x++)
125                 {
126                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
127                         dx = x - 16;
128                         d = (255 - (dx*dx+dy*dy));
129                         if (d < 0) d = 0;
130                         data[y][x][3] = (byte) d;
131                 }
132         }
133         particletexture = GL_LoadTexture ("particletexture", 32, 32, &data[0][0][0], true, true, 4);
134
135         for (i = 0;i < 8;i++)
136         {
137                 do
138                 {
139                         fractalnoise(&noise1[0][0], 128, 8);
140                         fractalnoise(&noise2[0][0], 128, 16);
141                         m = 0;
142                         for (y = 0;y < 32;y++)
143                         {
144                                 dy = y - 16;
145                                 for (x = 0;x < 32;x++)
146                                 {
147                                         int j;
148                                         j = (noise1[y][x] - 128) * 2 + 128;
149                                         if (j < 0) j = 0;
150                                         if (j > 255) j = 255;
151                                         data[y][x][0] = data[y][x][1] = data[y][x][2] = j;
152                                         dx = x - 16;
153                                         d = (noise2[y][x] - 128) * 4 + 128;
154                                         if (d > 0)
155                                         {
156                                                 d = (d * (255 - (int) (dx*dx+dy*dy))) >> 8;
157                                                 //j = (sqrt(dx*dx+dy*dy) * 2.0f - 16.0f);
158                                                 //if (j > 0)
159                                                 //      d = (d * (255 - j*j)) >> 8;
160                                                 if (d < 0) d = 0;
161                                                 if (d > 255) d = 255;
162                                                 data[y][x][3] = (byte) d;
163                                                 if (m < d)
164                                                         m = d;
165                                         }
166                                 }
167                         }
168                 }
169                 while (m < 192);
170
171                 smokeparticletexture[i] = GL_LoadTexture (va("smokeparticletexture%d", i), 32, 32, &data[0][0][0], true, true, 4);
172         }
173
174         light[0] = 1;light[1] = 1;light[2] = 1;
175         VectorNormalize(light);
176         for (x=0 ; x<32 ; x++)
177         {
178                 for (y=0 ; y<32 ; y++)
179                 {
180                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
181                         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                 }
183         }
184         rainparticletexture = GL_LoadTexture ("rainparticletexture", 32, 32, &data[0][0][0], true, true, 4);
185
186         light[0] = 1;light[1] = 1;light[2] = 1;
187         VectorNormalize(light);
188         for (x=0 ; x<32 ; x++)
189         {
190                 for (y=0 ; y<32 ; y++)
191                 {
192                         data[y][x][0] = data[y][x][1] = data[y][x][2] = 255;
193                         data[y][x][3] = shadebubble((x - 16) * (1.0 / 16.0), (y - 16) * (1.0 / 16.0), light);
194                 }
195         }
196         bubbleparticletexture = GL_LoadTexture ("bubbleparticletexture", 32, 32, &data[0][0][0], true, true, 4);
197 }
198
199 void r_part_start()
200 {
201         particles = (particle_t *) qmalloc(r_numparticles * sizeof(particle_t));
202         freeparticles = (void *) qmalloc(r_numparticles * sizeof(particle_t *));
203         numparticles = 0;
204         R_InitParticleTexture ();
205 }
206
207 void r_part_shutdown()
208 {
209         numparticles = 0;
210         qfree(particles);
211         qfree(freeparticles);
212 }
213
214 /*
215 ===============
216 R_InitParticles
217 ===============
218 */
219 void R_ReadPointFile_f (void);
220 void R_Particles_Init (void)
221 {
222         int             i;
223
224         i = COM_CheckParm ("-particles");
225
226         if (i)
227         {
228                 r_numparticles = (int)(atoi(com_argv[i+1]));
229                 if (r_numparticles < ABSOLUTE_MIN_PARTICLES)
230                         r_numparticles = ABSOLUTE_MIN_PARTICLES;
231         }
232         else
233         {
234                 r_numparticles = MAX_PARTICLES;
235         }
236
237         Cmd_AddCommand ("pointfile", R_ReadPointFile_f);        
238
239         Cvar_RegisterVariable (&r_particles);
240         Cvar_RegisterVariable (&r_drawparticles);
241         Cvar_RegisterVariable (&r_dynamicparticles);
242
243         R_RegisterModule("R_Particles", r_part_start, r_part_shutdown);
244 }
245
246 #define particle(ptype, pcolor, ptex, plight, pscale, palpha, ptime, px, py, pz, pvx, pvy, pvz)\
247 {\
248         particle_t      *part;\
249         ALLOCPARTICLE(part)\
250         part->type = (ptype);\
251         part->color = (pcolor);\
252         part->texnum = (ptex);\
253         part->dynlight = (plight);\
254         part->scale = (pscale);\
255         part->alpha = (palpha);\
256         part->die = cl.time + (ptime);\
257         part->org[0] = (px);\
258         part->org[1] = (py);\
259         part->org[2] = (pz);\
260         part->vel[0] = (pvx);\
261         part->vel[1] = (pvy);\
262         part->vel[2] = (pvz);\
263 }
264 #define particle2(ptype, pcolor, ptex, plight, pscale, palpha, ptime, pbase, poscale, pvscale)\
265 {\
266         particle_t      *part;\
267         ALLOCPARTICLE(part)\
268         part->type = (ptype);\
269         part->color = (pcolor);\
270         part->texnum = (ptex);\
271         part->dynlight = (plight);\
272         part->scale = (pscale);\
273         part->alpha = (palpha);\
274         part->die = cl.time + (ptime);\
275         part->org[0] = lhrandom(-(poscale), (poscale)) + (pbase)[0];\
276         part->org[1] = lhrandom(-(poscale), (poscale)) + (pbase)[1];\
277         part->org[2] = lhrandom(-(poscale), (poscale)) + (pbase)[2];\
278         part->vel[0] = lhrandom(-(pvscale), (pvscale));\
279         part->vel[1] = lhrandom(-(pvscale), (pvscale));\
280         part->vel[2] = lhrandom(-(pvscale), (pvscale));\
281 }
282 #define particle3(ptype, pcolor, ptex, plight, pscale, palpha, ptime, pbase, pscalex, pscaley, pscalez, pvscalex, pvscaley, pvscalez)\
283 {\
284         particle_t      *part;\
285         ALLOCPARTICLE(part)\
286         part->type = (ptype);\
287         part->color = (pcolor);\
288         part->texnum = (ptex);\
289         part->dynlight = (plight);\
290         part->scale = (pscale);\
291         part->alpha = (palpha);\
292         part->die = cl.time + (ptime);\
293         part->org[0] = lhrandom(-(pscalex), (pscalex)) + (pbase)[0];\
294         part->org[1] = lhrandom(-(pscaley), (pscaley)) + (pbase)[1];\
295         part->org[2] = lhrandom(-(pscalez), (pscalez)) + (pbase)[2];\
296         part->vel[0] = lhrandom(-(pvscalex), (pvscalex));\
297         part->vel[1] = lhrandom(-(pvscaley), (pvscaley));\
298         part->vel[2] = lhrandom(-(pvscalez), (pvscalez));\
299 }
300
301 /*
302 ===============
303 R_EntityParticles
304 ===============
305 */
306
307 #define NUMVERTEXNORMALS        162
308 extern  float   r_avertexnormals[NUMVERTEXNORMALS][3];
309 vec3_t  avelocities[NUMVERTEXNORMALS];
310 float   beamlength = 16;
311 vec3_t  avelocity = {23, 7, 3};
312 float   partstep = 0.01;
313 float   timescale = 0.01;
314
315 void R_EntityParticles (entity_t *ent)
316 {
317         int                     count;
318         int                     i;
319         float           angle;
320         float           sp, sy, cp, cy;
321         vec3_t          forward;
322         float           dist;
323         if (!r_particles.value) return; // LordHavoc: particles are optional
324         
325         dist = 64;
326         count = 50;
327
328         if (!avelocities[0][0])
329                 for (i=0 ; i<NUMVERTEXNORMALS*3 ; i++)
330                         avelocities[0][i] = (rand()&255) * 0.01;
331
332         for (i=0 ; i<NUMVERTEXNORMALS ; i++)
333         {
334                 angle = cl.time * avelocities[i][0];
335                 sy = sin(angle);
336                 cy = cos(angle);
337                 angle = cl.time * avelocities[i][1];
338                 sp = sin(angle);
339                 cp = cos(angle);
340         
341                 forward[0] = cp*cy;
342                 forward[1] = cp*sy;
343                 forward[2] = -sp;
344
345                 particle(pt_static, 0x6f, particletexture, false, 2, 255, 0, ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength, ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength, ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength, 0, 0, 0);
346         }
347 }
348
349
350 /*
351 ===============
352 R_ClearParticles
353 ===============
354 */
355 void R_ClearParticles (void)
356 {
357 //      int             i;
358 //      free_particles = &particles[0];
359 //      active_particles = NULL;
360
361 //      for (i=0 ;i<r_numparticles ; i++)
362 //              particles[i].next = &particles[i+1];
363 //      particles[r_numparticles-1].next = NULL;
364
365         numparticles = 0;
366 }
367
368
369 void R_ReadPointFile_f (void)
370 {
371         FILE    *f;
372         vec3_t  org;
373         int             r;
374         int             c;
375         char    name[MAX_OSPATH];
376         
377         sprintf (name,"maps/%s.pts", sv.name);
378
379         COM_FOpenFile (name, &f, false);
380         if (!f)
381         {
382                 Con_Printf ("couldn't open %s\n", name);
383                 return;
384         }
385         
386         Con_Printf ("Reading %s...\n", name);
387         c = 0;
388         for (;;)
389         {
390                 r = fscanf (f,"%f %f %f\n", &org[0], &org[1], &org[2]);
391                 if (r != 3)
392                         break;
393                 c++;
394                 
395                 if (numparticles >= r_numparticles)
396                 {
397                         Con_Printf ("Not enough free particles\n");
398                         break;
399                 }
400                 particle(pt_static, (-c)&15, particletexture, false, 2, 255, 99999, org[0], org[1], org[2], 0, 0, 0);
401         }
402
403         fclose (f);
404         Con_Printf ("%i points read\n", c);
405 }
406
407 /*
408 ===============
409 R_ParseParticleEffect
410
411 Parse an effect out of the server message
412 ===============
413 */
414 void R_ParseParticleEffect (void)
415 {
416         vec3_t          org, dir;
417         int                     i, count, msgcount, color;
418         
419         for (i=0 ; i<3 ; i++)
420                 org[i] = MSG_ReadCoord ();
421         for (i=0 ; i<3 ; i++)
422                 dir[i] = MSG_ReadChar () * (1.0/16);
423         msgcount = MSG_ReadByte ();
424         color = MSG_ReadByte ();
425
426 if (msgcount == 255)
427         count = 1024;
428 else
429         count = msgcount;
430         
431         R_RunParticleEffect (org, dir, color, count);
432 }
433         
434 /*
435 ===============
436 R_ParticleExplosion
437
438 ===============
439 */
440 void R_ParticleExplosion (vec3_t org, int smoke)
441 {
442         int i;
443         if (!r_particles.value) return; // LordHavoc: particles are optional
444
445 //      particle(pt_smoke, (rand()&7) + 8, smokeparticletexture[rand()&7], true, 30, 255, 2, org[0], org[1], org[2], 0, 0, 0);
446
447         i = Mod_PointInLeaf(org, cl.worldmodel)->contents;
448         if (i == CONTENTS_SLIME || i == CONTENTS_WATER)
449         {
450                 for (i=0 ; i<128 ; i++)
451                         particle2(pt_bubble, (rand()&3) + 12, bubbleparticletexture, false, lhrandom(1, 2), 255, 2, org, 16, 96);
452         }
453         else
454                 R_NewExplosion(org);
455         /*
456         else
457         {
458                 int j;
459 //              int color;
460                 float f, forg[3], fvel[3], fvel2[3];
461 //              for (i = 0;i < 256;i++)
462 //                      particle(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1.5, lhrandom(128, 255), 5, lhrandom(-16, 16) + org[0], lhrandom(-16, 16) + org[1], lhrandom(-16, 16) + org[2], lhrandom(-192, 192), lhrandom(-192, 192), lhrandom(-192, 192) + 192);
463 //              for (i = 0;i < 256;i++)
464 //                      particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1.5, lhrandom(128, 255), 5, org, 15, 150);
465                 for (i = 0;i < 32;i++)
466                 {
467                         fvel[0] = lhrandom(-150, 150);
468                         fvel[1] = lhrandom(-150, 150);
469                         fvel[2] = lhrandom(-150, 150) + 80;
470 //                      particle(pt_flamefall, 106 + (rand()%6), particletexture, 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]);
471                         for (j = 0;j < 64;j++)
472                         {
473                                 forg[0] = lhrandom(-20, 20) + org[0];
474                                 forg[1] = lhrandom(-20, 20) + org[1];
475                                 forg[2] = lhrandom(-20, 20) + org[2];
476                                 fvel2[0] = fvel[0] + lhrandom(-30, 30);
477                                 fvel2[1] = fvel[1] + lhrandom(-30, 30);
478                                 fvel2[2] = fvel[2] + lhrandom(-30, 30);
479                                 f = lhrandom(0.2, 1);
480                                 fvel2[0] *= f;
481                                 fvel2[1] *= f;
482                                 fvel2[2] *= f;
483                                 particle(pt_flamefall, 106 + (rand()%6), particletexture, false, 5, lhrandom(96, 192), 5, forg[0], forg[1], forg[2], fvel2[0], fvel2[1], fvel2[2]);
484                         }
485                 }
486 //              for (i = 0;i < 16;i++)
487 //                      particle2(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], true, 20, 192, 99, org, 20, 0);
488 //              for (i = 0;i < 50;i++)
489 //                      particle2(pt_flamingdebris, ramp3[rand()%6], particletexture, false, 3, 255, 99, org, 10, 200);
490 //              for (i = 0;i < 30;i++)
491 //                      particle2(pt_smokingdebris, 10 + (rand()%6), particletexture, false, 2, 255, 99, org, 10, 100);
492         }
493         */
494 }
495
496 /*
497 ===============
498 R_ParticleExplosion2
499
500 ===============
501 */
502 void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength)
503 {
504         int                     i;
505         if (!r_particles.value) return; // LordHavoc: particles are optional
506
507         for (i = 0;i < 512;i++)
508                 particle2(pt_fade, colorStart + (i % colorLength), particletexture, false, 1.5, 255, 0.3, org, 8, 192);
509 }
510
511 /*
512 ===============
513 R_BlobExplosion
514
515 ===============
516 */
517 void R_BlobExplosion (vec3_t org)
518 {
519         int                     i;
520         if (!r_particles.value) return; // LordHavoc: particles are optional
521         
522         for (i=0 ; i<512 ; i++)
523                 particle3(pt_blob, 66+(rand()%6), particletexture, false, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
524         for (i=0 ; i<512 ; i++)
525                 particle3(pt_blob2, 150+(rand()%6), particletexture, false, 2, 255, lhrandom(1, 1.4), org, 16, 16, 16, 4, 4, 128);
526 }
527
528 /*
529 ===============
530 R_RunParticleEffect
531
532 ===============
533 */
534 void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count)
535 {
536         if (!r_particles.value) return; // LordHavoc: particles are optional
537         
538         if (count == 1024)
539         {
540                 R_ParticleExplosion(org, false);
541                 return;
542         }
543         color &= ~7;
544         if (count & 7)
545         {
546                 particle2(pt_fade, color + (rand()&7), particletexture, false, 6, (count & 7) * 16 + (rand()&15), 1, org, 8, 15);
547                 count &= ~7;
548         }
549         count >>= 3;
550         while (count--)
551                 particle2(pt_fade, color + (rand()&7), particletexture, false, 6, 128, 1, org, 8, 15);
552 }
553
554 // LordHavoc: added this for spawning sparks/dust (which have strong gravity)
555 /*
556 ===============
557 R_SparkShower
558 ===============
559 */
560 void R_SparkShower (vec3_t org, vec3_t dir, int count)
561 {
562         if (!r_particles.value) return; // LordHavoc: particles are optional
563
564         // smoke puff
565         particle(pt_smoke, 12+(rand()&3), smokeparticletexture[rand()&7], true, 8, 160, 99, org[0], org[1], org[2], 0, 0, 0);
566         // sparks
567         while(count--)
568 //              particle2(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1, lhrandom(0, 255), 5, org, 4, 96);
569                 particle(pt_fallfadespark, ramp3[rand()%6], particletexture, false, 1, lhrandom(0, 255), 5, lhrandom(-4, 4) + org[0], lhrandom(-4, 4) + org[1], lhrandom(-4, 4) + org[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64) + 64);
570 }
571
572 void R_BloodPuff (vec3_t org)
573 {
574         if (!r_particles.value) return; // LordHavoc: particles are optional
575
576         particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], true, 12, 128, 99, org[0], org[1], org[2], 0, 0, 0);
577         particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], true, 10, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
578         particle(pt_bloodcloud, 251 /*68+(rand()&3)*/, smokeparticletexture[rand()&7], true, 8, 128, 99, org[0] + lhrandom(-4, 4), org[1] + lhrandom(-4, 4), org[2] + lhrandom(-4, 4), 0, 0, 0);
579 }
580
581 void R_BloodShower (vec3_t mins, vec3_t maxs, float velspeed, int count)
582 {
583         int                     j;
584         particle_t      *p;
585         vec3_t          diff;
586         vec3_t          center;
587         vec3_t          velscale;
588         if (!r_particles.value) return; // LordHavoc: particles are optional
589
590         VectorSubtract(maxs, mins, diff);
591         center[0] = (mins[0] + maxs[0]) * 0.5;
592         center[1] = (mins[1] + maxs[1]) * 0.5;
593         center[2] = (mins[2] + maxs[2]) * 0.5;
594         // FIXME: change velspeed back to 2.0x after fixing mod
595         velscale[0] = velspeed * 0.5 / diff[0];
596         velscale[1] = velspeed * 0.5 / diff[1];
597         velscale[2] = velspeed * 0.5 / diff[2];
598         
599         while (count--)
600         {
601                 ALLOCPARTICLE(p)
602
603                 p->texnum = smokeparticletexture[rand()&7];
604                 p->dynlight = true;
605                 p->scale = lhrandom(4, 6);
606                 p->alpha = 96 + (rand()&63);
607                 p->die = cl.time + 2;
608                 p->type = pt_bloodcloud;
609                 p->color = 251; //(rand()&3)+68;
610                 for (j=0 ; j<3 ; j++)
611                 {
612                         p->org[j] = diff[j] * (float) (rand()%1024) * (1.0 / 1024.0) + mins[j];
613                         p->vel[j] = (p->org[j] - center[j]) * velscale[j];
614                 }
615         }
616 }
617
618 void R_ParticleCube (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int gravity, int randomvel)
619 {
620         int                     j;
621         particle_t      *p;
622         vec3_t          diff;
623         float           t;
624         if (!r_particles.value) return; // LordHavoc: particles are optional
625         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
626         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
627         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
628
629         VectorSubtract(maxs, mins, diff);
630         
631         while (count--)
632         {
633                 ALLOCPARTICLE(p)
634
635                 p->texnum = particletexture;
636                 p->dynlight = false;
637                 p->scale = 6;
638                 p->alpha = 255;
639                 p->die = cl.time + 1 + (rand()&15)*0.0625;
640                 if (gravity)
641                         p->type = pt_grav;
642                 else
643                         p->type = pt_static;
644                 p->color = colorbase + (rand()&3);
645                 for (j=0 ; j<3 ; j++)
646                 {
647                         p->org[j] = diff[j] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[j];
648                         if (randomvel)
649                                 p->vel[j] = dir[j] + (rand()%randomvel)-(randomvel*0.5);
650                         else
651                                 p->vel[j] = 0;
652                 }
653         }
654 }
655
656 void R_ParticleRain (vec3_t mins, vec3_t maxs, vec3_t dir, int count, int colorbase, int type)
657 {
658         int                     i;
659         particle_t      *p;
660         vec3_t          diff;
661         vec3_t          org;
662         vec3_t          vel;
663         float           t, z;
664         if (!r_particles.value) return; // LordHavoc: particles are optional
665         if (maxs[0] <= mins[0]) {t = mins[0];mins[0] = maxs[0];maxs[0] = t;}
666         if (maxs[1] <= mins[1]) {t = mins[1];mins[1] = maxs[1];maxs[1] = t;}
667         if (maxs[2] <= mins[2]) {t = mins[2];mins[2] = maxs[2];maxs[2] = t;}
668         if (dir[2] < 0) // falling
669         {
670                 t = (maxs[2] - mins[2]) / -dir[2];
671                 z = maxs[2];
672         }
673         else // rising??
674         {
675                 t = (maxs[2] - mins[2]) / dir[2];
676                 z = mins[2];
677         }
678         if (t < 0 || t > 2) // sanity check
679                 t = 2;
680         t += cl.time;
681
682         VectorSubtract(maxs, mins, diff);
683         
684         for (i=0 ; i<count ; i++)
685         {
686                 ALLOCPARTICLE(p)
687
688                 vel[0] = dir[0] + (rand()&31) - 16;
689                 vel[1] = dir[1] + (rand()&31) - 16;
690                 vel[2] = dir[2] + (rand()&63) - 32;
691                 org[0] = diff[0] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[0];
692                 org[1] = diff[1] * (float) (rand()&1023) * (1.0 / 1024.0) + mins[1];
693                 org[2] = z;
694
695                 p->alpha = 255;
696                 p->die = t;
697                 if (type == 1)
698                 {
699                         p->scale = 2;
700                         p->texnum = particletexture;
701                         p->dynlight = false;
702                         p->type = pt_snow;
703                 }
704                 else // 0
705                 {
706                         p->scale = 3;
707                         p->texnum = rainparticletexture;
708                         p->dynlight = true;
709                         p->type = pt_rain;
710                 }
711                 p->color = colorbase + (rand()&3);
712                 VectorCopy(org, p->org);
713                 VectorCopy(vel, p->vel);
714                 VectorCopy(vel, p->vel2);
715         }
716 }
717
718
719 /*
720 ===============
721 R_LavaSplash
722
723 ===============
724 */
725 void R_LavaSplash (vec3_t org)
726 {
727         int                     i, j;
728         particle_t      *p;
729         float           vel;
730         vec3_t          dir;
731         if (!r_particles.value) return; // LordHavoc: particles are optional
732
733         for (i=-128 ; i<128 ; i+=16)
734                 for (j=-128 ; j<128 ; j+=16)
735                 {
736                         ALLOCPARTICLE(p)
737                 
738                         p->texnum = particletexture;
739                         p->dynlight = false;
740                         p->scale = 10;
741                         p->alpha = 128;
742                         p->die = cl.time + 2 + (rand()&31) * 0.02;
743                         p->color = 224 + (rand()&7);
744                         p->type = pt_grav;
745                         
746                         dir[0] = j + (rand()&7);
747                         dir[1] = i + (rand()&7);
748                         dir[2] = 256;
749
750                         p->org[0] = org[0] + dir[0];
751                         p->org[1] = org[1] + dir[1];
752                         p->org[2] = org[2] + (rand()&63);
753
754                         VectorNormalize (dir);                                          
755                         vel = 50 + (rand()&63);
756                         VectorScale (dir, vel, p->vel);
757                 }
758 }
759
760 /*
761 ===============
762 R_TeleportSplash
763
764 ===============
765 */
766 void R_TeleportSplash (vec3_t org)
767 {
768         int                     i, j, k;
769         particle_t      *p;
770         if (!r_particles.value) return; // LordHavoc: particles are optional
771
772         for (i=-16 ; i<16 ; i+=8)
773                 for (j=-16 ; j<16 ; j+=8)
774                         for (k=-24 ; k<32 ; k+=8)
775                         {
776                                 ALLOCPARTICLE(p)
777                 
778                                 p->texnum = particletexture;
779                                 p->dynlight = false;
780                                 p->scale = 1;
781                                 p->alpha = lhrandom(32,128);
782                                 p->die = cl.time + 5;
783                                 p->color = 254;
784                                 p->type = pt_fade;
785                                 
786                                 p->org[0] = org[0] + i + (rand()&7);
787                                 p->org[1] = org[1] + j + (rand()&7);
788                                 p->org[2] = org[2] + k + (rand()&7);
789         
790                                 p->vel[0] = i*2 + (rand()%25) - 12;
791                                 p->vel[1] = j*2 + (rand()%25) - 12;
792                                 p->vel[2] = k*2 + (rand()%25) - 12 + 40;
793                         }
794 }
795
796 void R_RocketTrail (vec3_t start, vec3_t end, int type, entity_t *ent)
797 {
798         vec3_t          vec;
799         float           len, dec = 0, t, nt, speed;
800         int                     j, contents, bubbles;
801         particle_t      *p;
802         if (!r_particles.value) return; // LordHavoc: particles are optional
803
804         t = cl.oldtime;
805         nt = cl.time;
806         if (ent->trail_leftover < 0)
807                 ent->trail_leftover = 0;
808         t += ent->trail_leftover;
809         ent->trail_leftover -= (cl.time - cl.oldtime);
810         if (t >= cl.time)
811                 return;
812
813         contents = Mod_PointInLeaf(start, cl.worldmodel)->contents;
814         if (contents == CONTENTS_SKY || contents == CONTENTS_LAVA)
815                 return;
816
817         VectorSubtract (end, start, vec);
818         len = VectorNormalizeLength (vec);
819         if (len <= 0.01f)
820                 return;
821         speed = len / (nt - t);
822
823         bubbles = (contents == CONTENTS_WATER || contents == CONTENTS_SLIME);
824
825         while (t < nt)
826         {
827                 ALLOCPARTICLE(p)
828                 
829                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
830                 p->die = cl.time + 2;
831
832                 switch (type)
833                 {
834                         case 0: // rocket trail
835                         case 1: // grenade trail
836                                 if (bubbles)
837                                 {
838                                         dec = type == 0 ? 0.01f : 0.02f;
839                                         p->texnum = bubbleparticletexture;
840                                         p->dynlight = false;
841                                         p->scale = lhrandom(1,2);
842                                         p->alpha = 255;
843                                         p->color = 254;
844                                         p->type = pt_bubble;
845                                         p->die = cl.time + 2;
846                                         for (j=0 ; j<3 ; j++)
847                                         {
848                                                 p->vel[j] = (rand()&31)-16;
849                                                 p->org[j] = start[j] + ((rand()&3)-2);
850                                         }
851                                 }
852                                 else
853                                 {
854                                         dec = type == 0 ? 0.02f : 0.04f;
855                                         p->texnum = smokeparticletexture[rand()&7];
856                                         p->dynlight = true;
857                                         p->scale = lhrandom(4, 8);
858                                         p->alpha = 160; //128 + (rand()&63);
859                                         p->color = 254;
860                                         p->type = pt_smoke;
861                                         p->die = cl.time + 10000;
862                                         VectorCopy(start, p->org);
863                                         /*
864                                         if (type == 0)
865                                         {
866                                                 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
867                                                 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
868                                                 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
869                                                 particle(pt_fallfadespark, 0x68 + (rand() & 7), particletexture, false, 1, lhrandom(64, 128), 5, start[0], start[1], start[2], lhrandom(-64, 64), lhrandom(-64, 64), lhrandom(-64, 64));
870                                         }
871                                         */
872                                 }
873                                 break;
874
875                                 /*
876                         case 1: // smoke smoke
877                                 dec = 0.016f;
878                                 p->texnum = smokeparticletexture;
879                                 p->dynlight = true;
880                                 p->scale = lhrandom(6,9);
881                                 p->alpha = 64;
882                                 if (r_smokecolor.value)
883                                         p->color = r_smokecolor.value;
884                                 else
885                                         p->color = (rand()&3)+12;
886                                 p->type = pt_smoke;
887                                 p->die = cl.time + 1;
888                                 VectorCopy(start, p->org);
889                                 break;
890                                 */
891
892                         case 2: // blood
893                         case 4: // slight blood
894                                 dec = 0.025f;
895                                 p->texnum = smokeparticletexture[rand()&7];
896                                 p->dynlight = true;
897                                 p->scale = lhrandom(4, 6);
898                                 p->alpha = type == 4 ? 192 : 255;
899                                 p->color = 247; //(rand()&3)+68;
900                                 p->type = pt_bloodcloud;
901                                 p->die = cl.time + 9999;
902                                 for (j=0 ; j<3 ; j++)
903                                 {
904                                         p->vel[j] = (rand()&15)-8;
905                                         p->org[j] = start[j] + ((rand()&3)-2);
906                                 }
907                                 break;
908
909                         case 3:
910                         case 5: // tracer
911                                 dec = 0.02f;
912                                 p->texnum = smokeparticletexture[rand()&7];
913                                 p->dynlight = false;
914                                 p->scale = 4;
915                                 p->alpha = 64 + (rand()&31);
916                                 p->color = type == 3 ? 56 : 234;
917                                 p->type = pt_fade;
918                                 p->die = cl.time + 10000;
919                                 VectorCopy(start, p->org);
920                                 break;
921
922                         case 6: // voor trail
923                                 dec = 0.05f; // sparse trail
924                                 p->texnum = smokeparticletexture[rand()&7];
925                                 p->dynlight = false;
926                                 p->scale = lhrandom(3, 5);
927                                 p->alpha = 255;
928                                 p->color = 9*16 + 8 + (rand()&3);
929                                 p->type = pt_fade;
930                                 p->die = cl.time + 2;
931                                 for (j=0 ; j<3 ; j++)
932                                 {
933                                         p->vel[j] = (rand()&15)-8;
934                                         p->org[j] = start[j] + ((rand()&3)-2);
935                                 }
936                                 break;
937
938                         case 7: // Nehahra smoke tracer
939                                 dec = 0.14f;
940                                 p->texnum = smokeparticletexture[rand()&7];
941                                 p->dynlight = true;
942                                 p->scale = lhrandom(8, 12);
943                                 p->alpha = 64;
944                                 p->color = (rand()&3)+12;
945                                 p->type = pt_smoke;
946                                 p->die = cl.time + 10000;
947                                 for (j=0 ; j<3 ; j++)
948                                         p->org[j] = start[j] + ((rand()&3)-2);
949                                 break;
950                 }
951                 
952                 t += dec;
953                 dec *= speed;
954                 VectorMA (start, dec, vec, start);
955         }
956         ent->trail_leftover = t - cl.time;
957 }
958
959 void R_RocketTrail2 (vec3_t start, vec3_t end, int color, entity_t *ent)
960 {
961         vec3_t          vec;
962         int                     len;
963         if (!r_particles.value) return; // LordHavoc: particles are optional
964
965         VectorSubtract (end, start, vec);
966         len = (int) (VectorNormalizeLength (vec) * (1.0f / 3.0f));
967         VectorScale(vec, 3, vec);
968         while (len--)
969         {
970                 particle(pt_smoke, color, particletexture, false, 8, 192, 99, start[0], start[1], start[2], 0, 0, 0);
971                 VectorAdd (start, vec, start);
972         }
973 }
974
975
976 /*
977 ===============
978 R_DrawParticles
979 ===============
980 */
981 extern  cvar_t  sv_gravity;
982
983 void R_MoveParticles (void)
984 {
985         particle_t              *p;
986         int                             i, activeparticles, maxparticle, j, a;
987         vec3_t                  v;
988         float                   gravity, dvel, frametime;
989
990         // LordHavoc: early out condition
991         if (!numparticles)
992                 return;
993
994         frametime = cl.time - cl.oldtime;
995         gravity = frametime * sv_gravity.value;
996         dvel = 1+4*frametime;
997
998         activeparticles = 0;
999         maxparticle = -1;
1000         j = 0;
1001         for (i = 0, p = particles;i < numparticles;i++, p++)
1002         {
1003                 if (p->die < cl.time)
1004                 {
1005                         freeparticles[j++] = p;
1006                         continue;
1007                 }
1008
1009                 VectorCopy(p->org, p->oldorg);
1010                 p->org[0] += p->vel[0]*frametime;
1011                 p->org[1] += p->vel[1]*frametime;
1012                 p->org[2] += p->vel[2]*frametime;
1013                 
1014                 switch (p->type)
1015                 {
1016                 case pt_static:
1017                         break;
1018
1019                         // LordHavoc: drop-through because of shared code
1020                 case pt_blob:
1021                         p->vel[2] *= dvel;
1022                 case pt_blob2:
1023                         p->vel[0] *= dvel;
1024                         p->vel[1] *= dvel;
1025                         break;
1026
1027                 case pt_grav:
1028                         p->vel[2] -= gravity;
1029                         break;
1030                 case pt_snow:
1031                         if (cl.time > p->time2)
1032                         {
1033                                 p->time2 = cl.time + (rand() & 3) * 0.1;
1034                                 p->vel[0] = (rand()&63)-32 + p->vel2[0];
1035                                 p->vel[1] = (rand()&63)-32 + p->vel2[1];
1036                                 p->vel[2] = (rand()&63)-32 + p->vel2[2];
1037                         }
1038                         break;
1039                 case pt_bloodcloud:
1040 //                      if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1041 //                      {
1042 //                              p->die = -1;
1043 //                              break;
1044 //                      }
1045                         p->scale += frametime * 16;
1046                         p->alpha -= frametime * 512;
1047                         break;
1048                 case pt_fallfadespark:
1049                         p->alpha -= frametime * 256;
1050                         p->vel[2] -= gravity;
1051                         break;
1052                 case pt_fade:
1053                         p->alpha -= frametime * 512;
1054                         break;
1055                 case pt_bubble:
1056                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1057                         if (a != CONTENTS_WATER && a != CONTENTS_SLIME)
1058                         {
1059                                 p->texnum = smokeparticletexture[rand()&7];
1060                                 p->type = pt_splash;
1061                                 p->alpha = 96;
1062                                 p->scale = 5;
1063                                 p->vel[0] = p->vel[1] = p->vel[2] = 0;
1064                                 p->die = cl.time + 1000;
1065 //                              p->die = -1;
1066                         }
1067                         p->vel[2] += gravity * 0.25;
1068                         p->vel[0] *= (1 - (frametime * 0.0625));
1069                         p->vel[1] *= (1 - (frametime * 0.0625));
1070                         p->vel[2] *= (1 - (frametime * 0.0625));
1071                         if (cl.time > p->time2)
1072                         {
1073                                 p->time2 = cl.time + lhrandom(0, 0.5);
1074                                 p->vel[0] += lhrandom(-32,32);
1075                                 p->vel[1] += lhrandom(-32,32);
1076                                 p->vel[2] += lhrandom(-32,32);
1077                         }
1078                         p->alpha -= frametime * 64;
1079                         break;
1080 // LordHavoc: for smoke trails
1081                 case pt_smoke:
1082                         p->scale += frametime * 16;
1083                         p->alpha -= frametime * 256;
1084                         break;
1085                 case pt_smokecloud:
1086                         p->scale += frametime * 64;
1087                         p->alpha -= frametime * 256;
1088                         break;
1089                 case pt_splash:
1090                         p->scale += frametime * 24;
1091                         p->alpha -= frametime * 512;
1092                         break;
1093                 case pt_rain:
1094                         a = Mod_PointInLeaf(p->org, cl.worldmodel)->contents;
1095                         if (a != CONTENTS_EMPTY && a != CONTENTS_SKY)
1096                         {
1097                                 vec3_t normal;
1098                                 if (a == CONTENTS_SOLID && Mod_PointInLeaf(p->oldorg, cl.worldmodel)->contents == CONTENTS_SOLID)
1099                                         break; // still in solid
1100                                 p->die = cl.time + 1000;
1101                                 switch (a)
1102                                 {
1103                                 case CONTENTS_LAVA:
1104                                 case CONTENTS_SLIME:
1105                                         p->texnum = smokeparticletexture[rand()&7];
1106                                         p->type = pt_smokecloud;
1107                                         p->alpha = 64;
1108                                         p->vel[2] = 96;
1109                                         break;
1110                                 case CONTENTS_WATER:
1111                                         p->texnum = smokeparticletexture[rand()&7];
1112                                         p->type = pt_splash;
1113                                         p->alpha = 96;
1114                                         p->scale = 5;
1115                                         p->vel[0] = p->vel[1] = p->vel[2] = 0;
1116 //                                      p->texnum = bubbleparticletexture;
1117 //                                      p->type = pt_bubble;
1118 //                                      p->vel[2] *= 0.1;
1119                                         break;
1120                                 default: // CONTENTS_SOLID and any others
1121                                         TraceLine(p->oldorg, p->org, v, normal);
1122                                         VectorCopy(v, p->org);
1123                                         p->texnum = smokeparticletexture[rand()&7];
1124                                         p->type = pt_splash;
1125                                         p->alpha = 96;
1126                                         p->scale = 5;
1127                                         p->vel[0] = p->vel[1] = p->vel[2] = 0;
1128                                         p->die = cl.time + 1000;
1129                                         break;
1130                                 }
1131                         }
1132                         break;
1133                 case pt_flame:
1134                         p->alpha -= frametime * 512;
1135                         break;
1136                 case pt_flamingdebris:
1137                         if (cl.time >= p->time2)
1138                         {
1139                                 p->time2 = cl.time + 0.01;
1140                                 particle2(pt_flame, p->color, particletexture, false, 4, p->alpha, 999, p->org, 0, 50);
1141                         }
1142                         p->alpha -= frametime * 512;
1143                         p->vel[2] -= gravity * 0.5f;
1144                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1145                                 p->die = -1;
1146                         break;
1147                 case pt_smokingdebris:
1148                         if (cl.time >= p->time2)
1149                         {
1150                                 p->time2 = cl.time + 0.01;
1151                                 particle2(pt_flame, 15, smokeparticletexture[rand()&7], false, 4, p->alpha, 999, p->org, 0, 50);
1152                         }
1153                         p->alpha -= frametime * 512;
1154                         p->vel[2] -= gravity * 0.5f;
1155                         if (Mod_PointInLeaf(p->org, cl.worldmodel)->contents != CONTENTS_EMPTY)
1156                                 p->die = -1;
1157                         break;
1158                 case pt_flamefall:
1159                         p->alpha -= frametime * 512;
1160                         p->vel[2] -= gravity * 0.5f;
1161                         break;
1162                 }
1163
1164                 // LordHavoc: most particles did this check anyway, consistency...
1165                 if (p->alpha < 1)
1166                         p->die = -1;
1167
1168                 // LordHavoc: immediate removal of unnecessary particles (must be done to ensure compactor below operates properly in all cases)
1169                 if (p->die < cl.time)
1170                         freeparticles[j++] = p;
1171                 else
1172                 {
1173                         maxparticle = i;
1174                         activeparticles++;
1175                 }
1176         }
1177         // fill in gaps to compact the array
1178         i = 0;
1179         while (maxparticle >= activeparticles)
1180         {
1181                 *freeparticles[i++] = particles[maxparticle--];
1182                 while (maxparticle >= activeparticles && particles[maxparticle].die < cl.time)
1183                         maxparticle--;
1184         }
1185         numparticles = activeparticles;
1186 }
1187
1188 void R_CompleteLightPoint (vec3_t color, vec3_t p);
1189
1190 void R_DrawParticles (void)
1191 {
1192         particle_t              *p;
1193         int                             i, r,g,b,a, dynlight;
1194         float                   scale, scale2, minparticledist;
1195         byte                    *color24;
1196         vec3_t                  up, right, uprightangles, forward2, up2, right2, tempcolor;
1197
1198         // LordHavoc: early out condition
1199         if ((!numparticles) || (!r_drawparticles.value))
1200                 return;
1201
1202         dynlight = r_dynamicparticles.value;
1203         if (!r_dynamic.value)
1204                 dynlight = 0;
1205         c_particles += numparticles;
1206
1207         VectorScale (vup, 1.5, up);
1208         VectorScale (vright, 1.5, right);
1209
1210         uprightangles[0] = 0;
1211         uprightangles[1] = r_refdef.viewangles[1];
1212         uprightangles[2] = 0;
1213         AngleVectors (uprightangles, forward2, right2, up2);
1214
1215         minparticledist = DotProduct(r_refdef.vieworg, vpn) + 16.0f;
1216
1217         for (i = 0, p = particles;i < numparticles;i++, p++)
1218         {
1219                 // LordHavoc: unnecessary (array was already compacted)
1220 //              if (p->die < cl.time)
1221 //                      continue;
1222
1223                 // LordHavoc: only render if not too close
1224                 if (DotProduct(p->org, vpn) < minparticledist)
1225                         continue;
1226
1227                 color24 = (byte *) &d_8to24table[(int)p->color];
1228                 r = color24[0];
1229                 g = color24[1];
1230                 b = color24[2];
1231                 a = p->alpha;
1232                 if (dynlight && (p->dynlight || dynlight >= 2)) // LordHavoc: only light blood and smoke
1233                 {
1234                         R_CompleteLightPoint(tempcolor, p->org);
1235                         r = (r * (int) tempcolor[0]) >> 7;
1236                         g = (g * (int) tempcolor[1]) >> 7;
1237                         b = (b * (int) tempcolor[2]) >> 7;
1238                 }
1239                 transpolybegin(p->texnum, 0, p->texnum, TPOLYTYPE_ALPHA);
1240                 scale = p->scale * -0.5;scale2 = p->scale * 0.5;
1241                 if (p->texnum == rainparticletexture) // rain streak
1242                 {
1243                         transpolyvert(p->org[0] + up2[0]*scale  + right2[0]*scale , p->org[1] + up2[1]*scale  + right2[1]*scale , p->org[2] + up2[2]*scale  + right2[2]*scale , 0,1,r,g,b,a);
1244                         transpolyvert(p->org[0] + up2[0]*scale2 + right2[0]*scale , p->org[1] + up2[1]*scale2 + right2[1]*scale , p->org[2] + up2[2]*scale2 + right2[2]*scale , 0,0,r,g,b,a);
1245                         transpolyvert(p->org[0] + up2[0]*scale2 + right2[0]*scale2, p->org[1] + up2[1]*scale2 + right2[1]*scale2, p->org[2] + up2[2]*scale2 + right2[2]*scale2, 1,0,r,g,b,a);
1246                         transpolyvert(p->org[0] + up2[0]*scale  + right2[0]*scale2, p->org[1] + up2[1]*scale  + right2[1]*scale2, p->org[2] + up2[2]*scale  + right2[2]*scale2, 1,1,r,g,b,a);
1247                 }
1248                 else
1249                 {
1250                         transpolyvert(p->org[0] + up[0]*scale  + right[0]*scale , p->org[1] + up[1]*scale  + right[1]*scale , p->org[2] + up[2]*scale  + right[2]*scale , 0,1,r,g,b,a);
1251                         transpolyvert(p->org[0] + up[0]*scale2 + right[0]*scale , p->org[1] + up[1]*scale2 + right[1]*scale , p->org[2] + up[2]*scale2 + right[2]*scale , 0,0,r,g,b,a);
1252                         transpolyvert(p->org[0] + up[0]*scale2 + right[0]*scale2, p->org[1] + up[1]*scale2 + right[1]*scale2, p->org[2] + up[2]*scale2 + right[2]*scale2, 1,0,r,g,b,a);
1253                         transpolyvert(p->org[0] + up[0]*scale  + right[0]*scale2, p->org[1] + up[1]*scale  + right[1]*scale2, p->org[2] + up[2]*scale  + right[2]*scale2, 1,1,r,g,b,a);
1254                 }
1255                 transpolyend();
1256         }
1257 }