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