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