]> icculus.org git repositories - taylor/freespace2.git/blob - src/particle/particle.cpp
ryan's struct patch for gcc 2.95
[taylor/freespace2.git] / src / particle / particle.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Particle/Particle.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Code for particle system
16  *
17  * $Log$
18  * Revision 1.5  2002/06/17 06:33:10  relnev
19  * ryan's struct patch for gcc 2.95
20  *
21  * Revision 1.4  2002/06/09 04:41:25  relnev
22  * added copyright header
23  *
24  * Revision 1.3  2002/06/09 03:16:04  relnev
25  * added _splitpath.
26  *
27  * removed unneeded asm, old sdl 2d setup.
28  *
29  * fixed crash caused by opengl_get_region.
30  *
31  * Revision 1.2  2002/05/07 03:16:48  theoddone33
32  * The Great Newline Fix
33  *
34  * Revision 1.1.1.1  2002/05/03 03:28:10  root
35  * Initial import.
36  *
37  * 
38  * 11    7/21/99 8:10p Dave
39  * First run of supernova effect.
40  * 
41  * 10    7/15/99 9:20a Andsager
42  * FS2_DEMO initial checkin
43  * 
44  * 9     7/07/99 3:11p Dave
45  * Fix for uninitialized particle system data.
46  * 
47  * 8     4/22/99 11:06p Dave
48  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
49  * now is to tweak and fix bugs as they come up. No new beam weapon
50  * features.
51  * 
52  * 7     1/29/99 12:47a Dave
53  * Put in sounds for beam weapon. A bunch of interface screens (tech
54  * database stuff).
55  * 
56  * 6     1/28/99 9:10a Andsager
57  * Particles increased in width, life, number.  Max particles increased
58  * 
59  * 5     1/27/99 9:56a Dave
60  * Temporary checkin of beam weapons for Dan to make cool sounds.
61  * 
62  * 4     1/24/99 11:37p Dave
63  * First full rev of beam weapons. Very customizable. Removed some bogus
64  * Int3()'s in low level net code.
65  * 
66  * 3     1/21/99 2:06p Dave
67  * Final checkin for multiplayer testing.
68  * 
69  * 2     10/07/98 10:53a Dave
70  * Initial checkin.
71  * 
72  * 1     10/07/98 10:50a Dave
73  * 
74  * 28    5/13/98 3:25p John
75  * Added code to make explosion impacts not get removed by other
76  * particles.
77  * 
78  * 27    5/11/98 10:06a John
79  * Added new particle for Adam
80  * 
81  * 26    4/30/98 11:31a Andsager
82  * Added particles to big ship explosions.  Modified particle_emit() to
83  * take optional range to increase range at which pariticles are created.
84  * 
85  * 25    4/17/98 1:42p Allender
86  * fixed NDEBUG build problems
87  * 
88  * 24    4/17/98 6:58a John
89  * Made particles not reduce in low mem conditions.
90  * 
91  * 23    4/15/98 4:21p John
92  * Made particles drop off with distance smoothly.  Made old particles get
93  * deleted by new ones.
94  * 
95  * 22    4/15/98 11:15a Adam
96  * upped MAX_PARTICLES to 1500 (from 800).  Primary weapon hit sparks were
97  * not finding slots.
98  * 
99  * 21    4/12/98 10:24a John
100  * Made particle drop off distance larger for larger detail levels.
101  * 
102  * 20    4/09/98 7:58p John
103  * Cleaned up tmapper code a bit.   Put NDEBUG around some ndebug stuff.
104  * Took out XPARENT flag settings in all the alpha-blended texture stuff.
105  * 
106  * 19    4/02/98 11:40a Lawrance
107  * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE
108  * 
109  * 18    4/01/98 9:21p John
110  * Made NDEBUG, optimized build with no warnings or errors.
111  * 
112  * 17    4/01/98 10:57a Mike
113  * Reduce array sizes to save memory.
114  * 
115  * 16    3/31/98 5:18p John
116  * Removed demo/save/restore.  Made NDEBUG defined compile.  Removed a
117  * bunch of debug stuff out of player file.  Made model code be able to
118  * unload models and malloc out only however many models are needed.
119  *  
120  * 
121  * 15    3/30/98 4:02p John
122  * Made machines with < 32 MB of RAM use every other frame of certain
123  * bitmaps.   Put in code to keep track of how much RAM we've malloc'd.
124  * 
125  * 14    3/26/98 5:26p John
126  * added new paging code. nonfunctional.
127  * 
128  * 13    3/22/98 11:02a John
129  * Made a bunch of the detail levels actually do something
130  * 
131  * 12    2/13/98 5:01p John
132  * Made particles behind you not render
133  * 
134  * 11    2/06/98 7:28p John
135  * Made debris and particles not get created if > 200 m from eye.   Added
136  * max_velocity to asteroid's physics info for aiding in throwing out
137  * collision pairs.
138  * 
139  * 10    2/05/98 9:21p John
140  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
141  * game.
142  * 
143  * 9     1/29/98 11:48a John
144  * Added new counter measure rendering as model code.   Made weapons be
145  * able to have impact explosion.
146  * 
147  * 8     1/26/98 5:10p John
148  * Took particle use out of registry.
149  * 
150  * 7     1/23/98 5:08p John
151  * Took L out of vertex structure used B (blue) instead.   Took all small
152  * fireballs out of fireball types and used particles instead.  Fixed some
153  * debris explosion things.  Restructured fireball code.   Restructured
154  * some lighting code.   Made dynamic lighting on by default. Made groups
155  * of lasers only cast one light.  Made fireballs not cast light.
156  * 
157  * 6     1/13/98 8:09p John
158  * Removed the old collision system that checked all pairs.   Added code
159  * to disable collisions and particles.
160  * 
161  * 5     1/02/98 5:04p John
162  * Several explosion related changes.  Made fireballs not be used as
163  * ani's.  Made ship spark system expell particles.  Took away impact
164  * explosion for weapon hitting ship... this needs to get added to weapon
165  * info and makes shield hit more obvious.  Only make sparks when hit
166  * hull, not shields.
167  * 
168  * 4     12/30/97 6:44p John
169  * Made g3_Draw_bitmap functions account for aspect of bitmap.
170  * 
171  * 3     12/26/97 5:42p Adam
172  * 
173  * 2     12/23/97 3:58p John
174  * Second rev of particles
175  * 
176  * 1     12/23/97 8:26a John
177  *
178  * $NoKeywords: $
179  */
180
181 #include "pstypes.h"
182 #include "freespace.h"
183 #include "linklist.h"
184 #include "3d.h" 
185 #include "bmpman.h"
186 #include "particle.h"
187 #include "osapi.h"
188 #include "object.h"
189 #include "timer.h"
190
191 typedef struct particle {
192         // old style data
193         vector  pos;                                    // position
194         vector  velocity;                       // velocity
195         float           age;                                    // How long it's been alive
196         float           max_life;                       // How much life we had
197         float           radius;                         // radius
198         int             type;                                   // type                                                                         // -1 = None
199         uint            optional_data;          // depends on type
200         int             nframes;                                // If an ani, how many frames?  
201
202         // new style data
203         float           tracer_length;          // if this is set, draw as a rod to simulate a "tracer" effect
204         short           attached_objnum;        // if this is set, pos is relative to the attached object. velocity is ignored
205         int             attached_sig;           // to check for dead/nonexistent objects
206         ubyte           reverse;                                // play any animations in reverse
207 } particle;
208
209 #ifdef FS2_DEMO
210         #define MAX_PARTICLES   500
211 #else
212         #define MAX_PARTICLES   2000    //      Reduced from 2000 to 800 by MK on 4/1/98.  Most I ever saw was 400 and the system recovers
213                                                                                         //      gracefully from running out of slots.
214                                                                                         // AP: Put it to 1500 on 4/15/98.  Primary hit sparks weren't finding open slots.  
215                                                                                         // Made todo item for John to force oldest smoke particles to give up their slots.
216 #endif
217
218 int Num_particles = 0;
219 particle Particles[MAX_PARTICLES];
220 int Next_particle = 0;
221
222 int Anim_bitmap_id_fire = -1;
223 int Anim_num_frames_fire = -1;
224
225 int Anim_bitmap_id_smoke = -1;
226 int Anim_num_frames_smoke = -1;
227
228 int Anim_bitmap_id_smoke2 = -1;
229 int Anim_num_frames_smoke2 = -1;
230
231 static int Particles_enabled = 1;
232
233 // Reset everything between levels
234 void particle_init()
235 {
236         int i;
237
238 //      Particles_enabled = os_config_read_uint( NULL, "UseParticles", 0 );
239
240         Num_particles = 0;
241         Next_particle = 0;
242
243         for (i=0; i<MAX_PARTICLES; i++ )        {
244                 Particles[i].type = -1;
245         }
246
247         if ( Anim_bitmap_id_fire == -1 )        {
248                 int fps;
249                 Anim_bitmap_id_fire = bm_load_animation( "particleexp01", &Anim_num_frames_fire, &fps, 1 );
250         }
251                 //Anim_bitmap_id = bm_load( "particleglow01" );
252                 //Anim_num_frames = 1;
253
254         if ( Anim_bitmap_id_smoke == -1 )       {
255                 int fps;
256                 Anim_bitmap_id_smoke = bm_load_animation( "particlesmoke01", &Anim_num_frames_smoke, &fps, 1 );
257         }
258
259         if ( Anim_bitmap_id_smoke2 == -1 )      {
260                 int fps;
261                 Anim_bitmap_id_smoke2 = bm_load_animation( "particlesmoke02", &Anim_num_frames_smoke2, &fps, 1 );
262         }
263
264 }
265
266 void particle_page_in()
267 {
268         int i;
269
270         for (i=0; i<MAX_PARTICLES; i++ )        {
271                 Particles[i].type = -1;
272         }
273
274         for (i=0; i<Anim_num_frames_fire; i++ ) {
275                 bm_page_in_texture( Anim_bitmap_id_fire + i );
276         }
277
278         for (i=0; i<Anim_num_frames_smoke; i++ )        {
279                 bm_page_in_texture( Anim_bitmap_id_smoke + i );
280         }
281
282         for (i=0; i<Anim_num_frames_smoke2; i++ )       {
283                 bm_page_in_texture( Anim_bitmap_id_smoke2 + i );
284         }
285 }
286
287 DCF(particles,"Turns particles on/off")
288 {
289         if ( Dc_command )       {       
290                 dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE);                
291                 if ( Dc_arg_type & ARG_TRUE )   Particles_enabled = 1;  
292                 else if ( Dc_arg_type & ARG_FALSE ) Particles_enabled = 0;      
293                 else if ( Dc_arg_type & ARG_NONE ) Particles_enabled ^= 1;      
294         }       
295         if ( Dc_help )  dc_printf( "Usage: particles [bool]\nTurns particle system on/off.  If nothing passed, then toggles it.\n" );   
296         if ( Dc_status )        dc_printf( "particles are %s\n", (Particles_enabled?"ON":"OFF") );      
297
298 //      os_config_write_uint( NULL, "UseParticles", Particles_enabled );
299 }
300
301
302                 //mprintf(( "%s\n", text ));
303
304 int Num_particles_hwm = 0;
305
306 #ifndef NDEBUG
307 int Total_requested = 0;
308 int Total_killed = 0;
309 int next_message = -1;
310 #endif
311
312 // Creates a single particle. See the PARTICLE_?? defines for types.
313 void particle_create( particle_info *pinfo )
314 {
315         int particle_num;
316         particle *p;
317
318 #ifndef NDEBUG
319         if ( next_message == -1 )       {
320                 next_message = timestamp(10000);
321         }
322
323         if ( timestamp_elapsed(next_message)) {
324                 next_message = timestamp(10000);
325                 if ( Total_requested > 1 )      {
326                         nprintf(( "Particles", "Particles: Killed off %d%% of the particles\n", (Total_killed*100/Total_requested) ));
327                 }
328                 Total_requested = 0;
329                 Total_killed = 0;
330         }
331 #endif
332
333         if ( !Particles_enabled ) return;
334
335         #ifndef NDEBUG
336         Total_requested++;
337         #endif
338
339         int RetryCount = 0;
340         int FirstParticleFound = Next_particle;
341 KillAnother:
342         p = &Particles[Next_particle++];
343         if ( Next_particle >= MAX_PARTICLES )   {
344                 Next_particle = 0;
345         }
346
347         if ( p->type > -1 )     {
348                 // Only remove non-persistent ones
349                 if ( p->type != PARTICLE_BITMAP_PERSISTENT )    {
350                         p->type = -1;
351                         Num_particles--;
352                         #ifndef NDEBUG
353                         Total_killed++;
354                         #endif
355                 } else {
356                         RetryCount++;
357                         // Keep trying to find a non-persistent one until we searched through 1/3 the slots.
358                         if ( RetryCount < MAX_PARTICLES/3 )     {
359                                 goto KillAnother;                       
360                         } 
361                         // Couldn't find any non-persistent ones to remove, so just remove the
362                         // first one we would have removed.
363                         mprintf(( "DELETING A PERSISTENT PARTICLE!!! This is ok if this only happens rarely. Get John if not.\n" ));
364                         Next_particle = FirstParticleFound;
365                         p = &Particles[Next_particle++];
366                         if ( Next_particle >= MAX_PARTICLES )   {
367                                 Next_particle = 0;
368                         }
369                 }
370         }
371
372         // increment counter
373         Num_particles++;
374         if (Num_particles > Num_particles_hwm) {
375                 Num_particles_hwm = Num_particles;
376
377                 if ( Num_particles_hwm == MAX_PARTICLES )       {
378                         mprintf(( "All particle slots filled!\n" ));
379                 }
380                 //mprintf(( "Num_particles high water mark = %i\n", Num_particles_hwm));
381         }
382
383         // get objnum
384         particle_num = p-Particles;
385
386         // Init the particle data
387         p->pos = pinfo->pos;
388         p->velocity = pinfo->vel;
389         p->age = 0.0f;
390         p->max_life = pinfo->lifetime;
391         p->radius = pinfo->rad;
392         p->type = pinfo->type;
393         p->optional_data = pinfo->optional_data;
394         p->tracer_length = pinfo->tracer_length;
395         p->attached_objnum = pinfo->attached_objnum;
396         p->attached_sig = pinfo->attached_sig;
397         p->reverse = pinfo->reverse;
398
399         if ( (p->type == PARTICLE_BITMAP) || (p->type == PARTICLE_BITMAP_PERSISTENT) )  {
400                 int fps;
401                 bm_get_info( p->optional_data,NULL, NULL, NULL, &p->nframes, &fps );
402                 if ( p->nframes > 1 )   {
403                         // Recalculate max life for ani's
404                         p->max_life = i2fl(p->nframes) / i2fl(fps);
405                 }
406         } else {
407                 p->nframes = 1;
408         }
409 }
410
411 void particle_create( vector *pos, vector *vel, float lifetime, float rad, int type, uint optional_data )
412 {
413         particle_info pinfo;
414
415         // setup old data
416         pinfo.pos = *pos;
417         pinfo.vel = *vel;
418         pinfo.lifetime = lifetime;
419         pinfo.rad = rad;
420         pinfo.type = type;
421         pinfo.optional_data = optional_data;    
422
423         // setup new data
424         pinfo.tracer_length = -1.0f;
425         pinfo.attached_objnum = -1;
426         pinfo.attached_sig = -1;
427         pinfo.reverse = 0;
428
429         // lower level function
430         particle_create(&pinfo);
431 }
432
433 MONITOR( NumParticles );        
434
435 void particle_move_all(float frametime)
436 {
437         particle *p;
438
439         MONITOR_INC( NumParticles, Num_particles );     
440
441         if ( !Particles_enabled ) return;
442
443         p = Particles;
444
445         int i;
446         for ( i=0; i<MAX_PARTICLES; i++, p++ )  {
447                 
448                 if ( p->type == -1 )    {
449                         continue;
450                 }
451
452                 // bogus attached objnum
453                 if(p->attached_objnum >= MAX_OBJECTS){
454                         p->type = -1;
455
456                         // decrement counter
457                         Num_particles--;
458         
459                         Assert(Num_particles >= 0);
460                         continue;
461                 }
462
463                 // if the vector is attached to an object which has become invalid, kill if
464                 if(p->attached_objnum >= 0){
465                         // if the signature has changed, kill it
466                         if(p->attached_sig != Objects[p->attached_objnum].signature){
467                                 p->type = -1;
468
469                                 // decrement counter
470                                 Num_particles--;
471
472                                 Assert(Num_particles >= 0);
473                                 continue;
474                         }
475                 }
476                 // move as a regular particle
477                 else {
478                         // Move the particle
479                         vm_vec_scale_add2( &p->pos, &p->velocity, frametime );          
480                 }
481
482                 p->age += frametime;
483         
484                 if ( p->age > p->max_life )     {
485
486                         // If it's time expired, remove it from the used list and 
487                         // into the free list
488                         p->type = -1;
489
490                         // decrement counter
491                         Num_particles--;
492
493                         Assert(Num_particles >= 0);
494                 }
495         }
496 }
497
498 // kill all active particles
499 void particle_kill_all()
500 {
501         int idx;
502
503         // kill all active particles
504         Num_particles = 0;
505         Next_particle = 0;
506         Num_particles_hwm = 0;
507         for(idx=0; idx<MAX_PARTICLES; idx++){
508                 Particles[idx].type = -1;
509         }
510 }
511
512 MONITOR( NumParticlesRend );    
513
514 void particle_render_all()
515 {
516         particle *p;
517         ubyte flags;
518         float pct_complete;
519         float alpha;
520         vertex pos;
521         vector ts, te, temp;
522         int rotate = 1;
523
524         if ( !Particles_enabled ) return;
525
526         MONITOR_INC( NumParticlesRend, Num_particles ); 
527
528         int n = 0;
529         int nclipped = 0;
530
531         p = Particles;
532
533         int i;
534         for ( i=0; i<MAX_PARTICLES; i++, p++ )  {
535                 
536                 if ( p->type == -1 )    {
537                         continue;
538                 }
539
540                 n++;
541
542                 // pct complete for the particle
543                 pct_complete = p->age / p->max_life;
544
545                 // calculate the alpha to draw at
546                 alpha = 1.0f;   
547                         
548                 // if this is a tracer style particle, calculate tracer vectors
549                 if(p->tracer_length > 0.0f){                    
550                         ts = p->pos;
551                         temp = p->velocity;
552                         vm_vec_normalize_quick(&temp);
553                         vm_vec_scale_add(&te, &ts, &temp, p->tracer_length);
554
555                         // don't bother rotating
556                         rotate = 0;
557                 }
558                 // if this is an "attached" particle. move it
559                 else if(p->attached_objnum >= 0){
560                         // offset the vector, and transform to view coords
561                         // vm_vec_add(&te, &Objects[p->attached_objnum].pos, &p->pos);
562                         vm_vec_unrotate(&temp, &p->pos, &Objects[p->attached_objnum].orient);
563                         vm_vec_add2(&temp, &Objects[p->attached_objnum].pos);
564
565                         flags = g3_rotate_vertex(&pos, &temp);
566                         if(flags){                              
567                                 nclipped++;
568                                 continue;
569                         }
570                         
571                         // don't bother rotating again
572                         rotate = 0;
573                 }
574
575                 // rotate the vertex
576                 if(rotate){
577                         flags = g3_rotate_vertex( &pos, &p->pos );
578                         if ( flags )    {
579                                 nclipped++;
580                                 continue;
581                         }
582                 }
583
584                 switch( p->type )       {
585
586                         case PARTICLE_DEBUG:                            // A red sphere, no optional data required
587                                 gr_set_color( 255, 0, 0 );
588                                 g3_draw_sphere_ez( &p->pos, p->radius );
589                                 break;
590
591                         case PARTICLE_BITMAP:           
592                         case PARTICLE_BITMAP_PERSISTENT:
593                                 {       // A bitmap, optional data is the bitmap number                                 
594                                         int framenum = p->optional_data;
595
596                                         if ( p->nframes > 1 )   {
597                                                 int n = fl2i(pct_complete * p->nframes + 0.5);
598
599                                                 if ( n < 0 ) n = 0;
600                                                 else if ( n > p->nframes-1 ) n = p->nframes-1;
601
602                                                 framenum += n;
603                                         }
604
605                                         // set the bitmap
606                                         gr_set_bitmap( framenum, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha );
607
608                                         // if this is a tracer style particle
609                                         if(p->tracer_length > 0.0f){                                    
610                                                 g3_draw_laser(&ts, p->radius, &te, p->radius, TMAP_FLAG_TEXTURED|TMAP_FLAG_XPARENT, 25.0f);
611                                         }
612                                         // draw as a regular bitmap
613                                         else {
614                                                 g3_draw_bitmap(&pos, (p-Particles)%8, p->radius, TMAP_FLAG_TEXTURED );
615                                         }
616                                         break;
617                                 }
618
619                         case PARTICLE_FIRE:     {
620                                         int framenum = fl2i(pct_complete * Anim_num_frames_fire + 0.5);
621
622                                         if ( framenum < 0 ) framenum = 0;
623                                         else if ( framenum > Anim_num_frames_fire-1 ) framenum = Anim_num_frames_fire-1;
624
625                                         /*
626                                         vertex pos;
627                                         flags = g3_rotate_vertex( &pos, &p->pos );
628                                         if ( flags )    {
629                                                 nclipped++;
630                                                 break;
631                                         }
632                                         */
633
634                                         // set the bitmap
635                                         gr_set_bitmap(p->reverse ? Anim_bitmap_id_fire+(Anim_num_frames_fire - framenum - 1) : Anim_bitmap_id_fire+framenum, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha );
636
637                                         // if this is a tracer style particle
638                                         if(p->tracer_length > 0.0f){                                    
639                                                 g3_draw_laser(&ts, p->radius, &te, p->radius, TMAP_FLAG_TEXTURED|TMAP_FLAG_XPARENT, 25.0f);
640                                         }
641                                         // draw as a regular bitmap
642                                         else {
643                                                 g3_draw_bitmap(&pos, (p-Particles)%8, p->radius, TMAP_FLAG_TEXTURED );
644                                         }
645                                         break;
646                                 }
647
648                         case PARTICLE_SMOKE:    {
649                                         int framenum = fl2i(pct_complete * Anim_num_frames_smoke + 0.5);
650
651                                         if ( framenum < 0 ) framenum = 0;
652                                         else if ( framenum > Anim_num_frames_smoke-1 ) framenum = Anim_num_frames_smoke-1;
653
654                                         /*
655                                         vertex pos;
656                                         flags = g3_rotate_vertex( &pos, &p->pos );
657                                         if ( flags )    {
658                                                 nclipped++;
659                                                 break;
660                                         }
661                                         */
662
663                                         // set the bitmap
664                                         gr_set_bitmap(p->reverse ? Anim_bitmap_id_smoke+(Anim_num_frames_smoke - framenum - 1) : Anim_bitmap_id_smoke+framenum, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha );
665
666                                         // if this is a tracer style particle
667                                         if(p->tracer_length > 0.0f){                    
668                                                 g3_draw_laser(&ts, p->radius, &te, p->radius, TMAP_FLAG_TEXTURED|TMAP_FLAG_XPARENT, 25.0f);
669                                         }
670                                         // draw as a regular bitmap
671                                         else {
672                                                 g3_draw_bitmap(&pos, (p-Particles)%8, p->radius, TMAP_FLAG_TEXTURED );
673                                         }
674                                         break;
675                                 }
676
677                         case PARTICLE_SMOKE2:   {
678                                         int framenum = fl2i(pct_complete * Anim_num_frames_smoke2 + 0.5);
679
680                                         if ( framenum < 0 ) framenum = 0;
681                                         else if ( framenum > Anim_num_frames_smoke2-1 ) framenum = Anim_num_frames_smoke2-1;
682
683                                         /*
684                                         vertex pos;
685                                         flags = g3_rotate_vertex( &pos, &p->pos );
686                                         if ( flags )    {
687                                                 nclipped++;
688                                                 break;
689                                         }
690                                         */
691
692                                         // set the bitmap
693                                         gr_set_bitmap(p->reverse ? Anim_bitmap_id_smoke2+(Anim_num_frames_smoke2 - framenum - 1) : Anim_bitmap_id_smoke2+framenum, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha );
694                                         
695                                         // if this is a tracer style particle
696                                         if(p->tracer_length > 0.0f){                                    
697                                                 g3_draw_laser(&ts, p->radius, &te, p->radius, TMAP_FLAG_TEXTURED|TMAP_FLAG_XPARENT, 25.0f);
698                                         }
699                                         // draw as a regular bitmap
700                                         else {                                          
701                                                 g3_draw_bitmap(&pos, (p-Particles)%8, p->radius, TMAP_FLAG_TEXTURED );
702                                         }
703                                         break;
704                                 }
705                 }
706         }
707 //      mprintf(( "NP=%d, NCP=%d\n", n, nclipped ));
708 }
709
710
711
712
713 //============================================================================
714 //============== HIGH-LEVEL PARTICLE SYSTEM CREATION CODE ====================
715 //============================================================================
716
717 // Use a structure rather than pass a ton of parameters to particle_emit
718 /*
719 typedef struct particle_emitter {
720         int             num_low;                                // Lowest number of particles to create
721         int             num_high;                       // Highest number of particles to create
722         vector  pos;                                    // Where the particles emit from
723         vector  vel;                                    // Initial velocity of all the particles
724         float           lifetime;                       // How long the particles live
725         vector  normal;                         // What normal the particle emit arond
726         float           normal_variance;        //      How close they stick to that normal 0=good, 1=360 degree
727         float           min_vel;                                // How fast the slowest particle can move
728         float           max_vel;                                // How fast the fastest particle can move
729         float           min_rad;                                // Min radius
730         float           max_rad;                                // Max radius
731 } particle_emitter;
732 */
733
734 #if MAX_DETAIL_LEVEL != 4
735 #error Max details assumed to be 4 here
736 #endif
737 int detail_max_num[5] = { 0, 50, 75, 100, 125 };
738
739 // Creates a bunch of particles. You pass a structure
740 // rather than a bunch of parameters.
741 void particle_emit( particle_emitter *pe, int type, uint optional_data, float range )
742 {
743         int i, n;
744
745         if ( !Particles_enabled ) return;
746
747         int n1, n2;
748
749         // Account for detail
750         #if MAX_DETAIL_LEVEL != 4 
751         #error Code in Particle.cpp assumes MAX_DETAIL_LEVEL == 4
752         #endif
753
754         int percent = detail_max_num[Detail.num_particles];
755
756         //Particle rendering drops out too soon.  Seems to be around 150 m.  Is it detail level controllable?  I'd like it to be 500-1000 
757         float min_dist = 125.0f;
758         float dist = vm_vec_dist_quick( &pe->pos, &Eye_position ) / range;
759         if ( dist > min_dist )  {
760                 percent = fl2i( i2fl(percent)*min_dist / dist );
761                 if ( percent < 1 ) {
762                         return;
763                 }
764         }
765         //mprintf(( "Dist = %.1f, percent = %d%%\n", dist, percent ));
766
767         n1 = (pe->num_low*percent)/100;
768         n2 = (pe->num_high*percent)/100;
769
770         // How many to emit?
771         n = (rand() % (n2-n1+1)) + n1;
772         
773         if ( n < 1 ) return;
774
775
776         for (i=0; i<n; i++ )    {
777                 // Create a particle
778                 vector tmp_vel;
779                 vector normal;                          // What normal the particle emit arond
780
781                 float radius = (( pe->max_rad - pe->min_rad ) * frand()) + pe->min_rad;
782
783                 float speed = (( pe->max_vel - pe->min_vel ) * frand()) + pe->min_vel;
784
785                 float life = (( pe->max_life - pe->min_life ) * frand()) + pe->min_life;
786
787                 normal.xyz.x = pe->normal.xyz.x + (frand()*2.0f - 1.0f)*pe->normal_variance;
788                 normal.xyz.y = pe->normal.xyz.y + (frand()*2.0f - 1.0f)*pe->normal_variance;
789                 normal.xyz.z = pe->normal.xyz.z + (frand()*2.0f - 1.0f)*pe->normal_variance;
790                 vm_vec_normalize_safe( &normal );
791                 vm_vec_scale_add( &tmp_vel, &pe->vel, &normal, speed );
792
793                 particle_create( &pe->pos, &tmp_vel, life, radius, type, optional_data );
794         }
795 }