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