]> icculus.org git repositories - taylor/freespace2.git/blob - src/nebula/neb.cpp
More changes, took out a lot of the sound/* stuff which will bite later but
[taylor/freespace2.git] / src / nebula / neb.cpp
1 /*
2  * $Logfile: /Freespace2/code/Nebula/Neb.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Nebula effect
8  *
9  * $Log$
10  * Revision 1.2  2002/05/04 04:36:56  theoddone33
11  * More changes, took out a lot of the sound/* stuff which will bite later but
12  * I don't care.
13  *
14  * Revision 1.1.1.1  2002/05/03 03:28:10  root
15  * Initial import.
16  * 
17  * 
18  * 50    8/30/99 5:01p Dave
19  * Made d3d do less state changing in the nebula. Use new chat server for
20  * PXO.
21  * 
22  * 49    8/10/99 6:54p Dave
23  * Mad optimizations. Added paging to the nebula effect.
24  * 
25  * 48    8/05/99 2:05a Dave
26  * Whee.
27  * 
28  * 47    7/30/99 10:55a Anoop
29  * Hmm. Fixed release build problem again, with area-rotated bitmaps.
30  * 
31  * 46    7/29/99 10:47p Dave
32  * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
33  * 
34  * 44    7/29/99 12:05a Dave
35  * Nebula speed optimizations.
36  * 
37  * 43    7/19/99 7:20p Dave
38  * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula
39  * pre-rendering.
40  * 
41  * 42    7/18/99 5:20p Dave
42  * Jump node icon. Fixed debris fogging. Framerate warning stuff.
43  * 
44  * 41    7/13/99 1:15p Dave
45  * 32 bit support. Whee!
46  * 
47  * 40    7/09/99 5:54p Dave
48  * Seperated cruiser types into individual types. Added tons of new
49  * briefing icons. Campaign screen.
50  * 
51  * 39    7/07/99 10:44a Jamesa
52  * Make sure the nebula regens properly after loading a mission.
53  * 
54  * 38    6/11/99 2:32p Dave
55  * Toned down nebula brightness a bit.
56  * 
57  * 37    5/26/99 3:39p Dave
58  * Fixed nebula regeneration problem. Removed optimizations from
59  * neblightning.cpp
60  * 
61  * 36    5/26/99 11:46a Dave
62  * Added ship-blasting lighting and made the randomization of lighting
63  * much more customizable.
64  * 
65  * 35    5/24/99 5:45p Dave
66  * Added detail levels to the nebula, with a decent speedup. Split nebula
67  * lightning into its own section.
68  * 
69  * $NoKeywords: $
70  */
71
72 #include "neb.h"
73 #include "vecmat.h"
74 #include "3d.h"
75 #include "bmpman.h"
76 #include "2d.h"
77 #include "object.h"
78 #ifndef PLAT_UNIX
79 #include "glide.h"
80 #endif
81 #include "timer.h"
82 #include "multi.h"
83 #include "freespace.h"
84 #include "key.h"
85 #include "nebula.h"
86 #include "starfield.h"
87 #include "parselo.h"
88 #include "beam.h"
89 #include "sound.h"
90 #include "gamesnd.h"
91 #include "grinternal.h"
92
93 #include "alphacolors.h"
94
95 // --------------------------------------------------------------------------------------------------------
96 // NEBULA DEFINES/VARS
97 //
98
99 // #define NEB2_THUMBNAIL
100
101 /*
102 3D CARDS THAT FOG PROPERLY
103 Voodoo1
104 Voodoo2
105 G200
106 TNT
107
108 3D CARDS THAT DON'T FOG PROPERLY
109 Permedia2
110 AccelStar II
111 */
112
113 // if nebula rendering is active (DCF stuff - not mission specific)
114 int Neb2_render_mode = NEB2_RENDER_NONE;
115
116 // array of neb2 poofs
117 char Neb2_poof_filenames[MAX_NEB2_POOFS][MAX_FILENAME_LEN] = {
118         "", "", "", "", "", ""
119 };
120 int Neb2_poofs[MAX_NEB2_POOFS] = { -1, -1, -1, -1, -1, -1 };
121 int Neb2_poof_flags = ( (1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5) );
122 int Neb2_poof_count = 0;
123
124 // array of neb2 bitmaps
125 char Neb2_bitmap_filenames[MAX_NEB2_BITMAPS][MAX_FILENAME_LEN] = {
126         "", "", "", "", "", ""
127 };
128 int Neb2_bitmap[MAX_NEB2_BITMAPS] = { -1, -1, -1, -1, -1, -1 };
129 int Neb2_bitmap_count = 0;
130
131 // texture to use for this level
132 char Neb2_texture_name[MAX_FILENAME_LEN] = "";
133
134 // nebula flags
135 #define NF_USED                                         (1<<0)          // if this nebula slot is used
136
137 float max_rotation = 3.75f;
138 float neb2_flash_fade = 0.3f;
139
140 // fog values for different ship types
141 float Neb_ship_fog_vals_glide[MAX_SHIP_TYPE_COUNTS][2] = {
142         {0.0f, 0.0f},                           // SHIP_TYPE_NONE
143         {10.0f, 500.0f},                        // SHIP_TYPE_CARGO
144         {10.0f, 500.0f},                        // SHIP_TYPE_FIGHTER_BOMBER
145         {10.0f, 600.0f},                        // SHIP_TYPE_CRUISER
146         {10.0f, 600.0f},                        // SHIP_TYPE_FREIGHTER
147         {10.0f, 750.0f},                        // SHIP_TYPE_CAPITAL
148         {10.0f, 500.0f},                        // SHIP_TYPE_TRANSPORT
149         {10.0f, 500.0f},                        // SHIP_TYPE_REPAIR_REARM
150         {10.0f, 500.0f},                        // SHIP_TYPE_NAVBUOY
151         {10.0f, 500.0f},                        // SHIP_TYPE_SENTRYGUN
152         {10.0f, 600.0f},                        // SHIP_TYPE_ESCAPEPOD
153         {10.0f, 1000.0f},                       // SHIP_TYPE_SUPERCAP
154         {10.0f, 500.0f},                        // SHIP_TYPE_STEALTH
155         {10.0f, 500.0f},                        // SHIP_TYPE_FIGHTER
156         {10.0f, 500.0f},                        // SHIP_TYPE_BOMBER
157         {10.0f, 750.0f},                        // SHIP_TYPE_DRYDOCK
158         {10.0f, 600.0f},                        // SHIP_TYPE_AWACS
159         {10.0f, 600.0f},                        // SHIP_TYPE_GAS_MINER
160         {10.0f, 600.0f},                        // SHIP_TYPE_CORVETTE
161         {10.0f, 1000.0f},                       // SHIP_TYPE_KNOSSOS_DEVICE
162 };
163 float Neb_ship_fog_vals_d3d[MAX_SHIP_TYPE_COUNTS][2] = {
164         {0.0f, 0.0f},                           // SHIP_TYPE_NONE
165         {10.0f, 500.0f},                        // SHIP_TYPE_CARGO
166         {10.0f, 500.0f},                        // SHIP_TYPE_FIGHTER_BOMBER
167         {10.0f, 600.0f},                        // SHIP_TYPE_CRUISER
168         {10.0f, 600.0f},                        // SHIP_TYPE_FREIGHTER
169         {10.0f, 750.0f},                        // SHIP_TYPE_CAPITAL
170         {10.0f, 500.0f},                        // SHIP_TYPE_TRANSPORT
171         {10.0f, 500.0f},                        // SHIP_TYPE_REPAIR_REARM
172         {10.0f, 500.0f},                        // SHIP_TYPE_NAVBUOY
173         {10.0f, 500.0f},                        // SHIP_TYPE_SENTRYGUN
174         {10.0f, 600.0f},                        // SHIP_TYPE_ESCAPEPOD
175         {10.0f, 1000.0f},                       // SHIP_TYPE_SUPERCAP
176         {10.0f, 500.0f},                        // SHIP_TYPE_STEALTH
177         {10.0f, 500.0f},                        // SHIP_TYPE_FIGHTER
178         {10.0f, 500.0f},                        // SHIP_TYPE_BOMBER
179         {10.0f, 750.0f},                        // SHIP_TYPE_DRYDOCK
180         {10.0f, 600.0f},                        // SHIP_TYPE_AWACS
181         {10.0f, 600.0f},                        // SHIP_TYPE_GAS_MINER
182         {10.0f, 600.0f},                        // SHIP_TYPE_CORVETTE
183         {10.0f, 1000.0f},                       // SHIP_TYPE_KNOSSOS_DEVICE
184 };
185
186 // fog near and far values for rendering the background nebula
187 #define NEB_BACKG_FOG_NEAR_GLIDE                                2.5f
188 #define NEB_BACKG_FOG_NEAR_D3D                          4.5f
189 #define NEB_BACKG_FOG_FAR_GLIDE                         10.0f
190 #define NEB_BACKG_FOG_FAR_D3D                                   10.0f
191 float Neb_backg_fog_near = NEB_BACKG_FOG_NEAR_GLIDE;
192 float Neb_backg_fog_far = NEB_BACKG_FOG_FAR_GLIDE;
193
194 // stats
195 int pneb_tried = 0;                             // total pnebs tried to render
196 int pneb_tossed_alpha = 0;              // pnebs tossed because of alpha optimization
197 int pneb_tossed_dot = 0;                // pnebs tossed because of dot product
198 int pneb_tossed_off = 0;                // pnebs tossed because of being offscree
199 int neb_tried = 0;                              // total nebs tried
200 int neb_tossed_alpha = 0;               // nebs tossed because of alpha
201 int neb_tossed_dot = 0;                 // nebs tossed because of dot product
202 int neb_tossed_count = 0;               // nebs tossed because of max render count 
203
204 // the AWACS suppresion level for the nebula
205 float Neb2_awacs = -1.0f;
206
207 // how many "slices" are in the current player nebuls
208 int Neb2_slices = 5;
209
210 cube_poof Neb2_cubes[MAX_CPTS][MAX_CPTS][MAX_CPTS];
211
212 // nebula detail level
213 typedef struct neb2_detail {
214         float max_alpha_glide;                                  // max alpha for this detail level in Glide
215         float max_alpha_d3d;                                            // max alpha for this detail level in D3d
216         float break_alpha;                                              // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot
217         float break_x, break_y;                                 // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen 
218         float cube_dim;                                                 // total dimension of player poof cube
219         float cube_inner;                                                       // inner radius of the player poof cube
220         float cube_outer;                                                       // outer radius of the player pood cube
221         float prad;                                                                     // radius of the poofs
222         float wj, hj, dj;                                                       // width, height, depth jittering. best left at 1.0     
223 } neb2_detail;
224 neb2_detail     Neb2_detail[MAX_DETAIL_LEVEL] = {
225         { // lowest detail level
226                 0.575f,                                                                         // max alpha for this detail level in Glide
227                 0.71f,                                                                  // max alpha for this detail level in D3d
228                 0.13f,                                                                  // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot
229                 150.0f, 150.0f / 1.3333f,                       // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen 
230                 510.0f,                                                                 // total dimension of player poof cube
231                 50.0f,                                                                  // inner radius of the player poof cube
232                 250.0f,                                                                 // outer radius of the player pood cube
233                 120.0f,                                                                 // radius of the poofs
234                 1.0f, 1.0f, 1.0f                                                // width, height, depth jittering. best left at 1.0     
235         },      
236         { // 2nd lowest detail level
237                 0.575f,                                                                         // max alpha for this detail level in Glide
238                 0.71f,                                                                  // max alpha for this detail level in D3d
239                 0.125f,                                                                 // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot
240                 300.0f, 300.0f / 1.3333f,                       // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen 
241                 550.0f,                                                                 // total dimension of player poof cube
242                 100.0f,                                                                 // inner radius of the player poof cube
243                 250.0f,                                                                 // outer radius of the player pood cube
244                 125.0f,                                                                 // radius of the poofs
245                 1.0f, 1.0f, 1.0f                                                // width, height, depth jittering. best left at 1.0     
246         },
247         { // 2nd highest detail level
248                 0.575f,                                                                         // max alpha for this detail level in Glide
249                 0.71f,                                                                  // max alpha for this detail level in D3d
250                 0.1f,                                                                           // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot
251                 300.0f, 300.0f / 1.3333f,                       // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen 
252                 550.0f,                                                                 // total dimension of player poof cube
253                 150.0f,                                                                 // inner radius of the player poof cube
254                 250.0f,                                                                 // outer radius of the player pood cube
255                 125.0f,                                                                 // radius of the poofs
256                 1.0f, 1.0f, 1.0f                                                // width, height, depth jittering. best left at 1.0     
257         },
258         { // higest detail level
259                 0.475f,                                                                 // max alpha for this detail level in Glide
260                 0.575f,                                                                 // max alpha for this detail level in D3d
261                 0.05f,                                                                  // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot
262                 200.0f, 200.0f / 1.3333f,                       // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen 
263                 750.0f,                                                                 // total dimension of player poof cube
264                 200.0f,                                                                 // inner radius of the player poof cube
265                 360.0f,                                                                 // outer radius of the player pood cube
266                 150.0f,                                                                 // radius of the poofs
267                 1.0f, 1.0f, 1.0f                                                // width, height, depth jittering. best left at 1.0     
268         },              
269 };
270 neb2_detail *Nd = &Neb2_detail[MAX_DETAIL_LEVEL - 2];
271
272 int Neb2_background_color[3] = {0, 0, 255};                     // rgb background color (used for lame rendering)
273
274 int Neb2_regen = 0;
275
276 // --------------------------------------------------------------------------------------------------------
277 // NEBULA FORWARD DECLARATIONS
278 //
279
280 // return the alpha the passed poof should be rendered with, for a 2 shell nebula
281 float neb2_get_alpha_2shell(float inner_radius, float outer_radius, float magic_num, vector *v);
282
283 // return an alpha value for a bitmap offscreen based upon "break" value
284 float neb2_get_alpha_offscreen(float sx, float sy, float incoming_alpha);
285
286 // do a pre-render of the background nebula
287 void neb2_pre_render(vector *eye_pos, matrix *eye_orient);
288
289 // fill in the position of the eye for this frame
290 void neb2_get_eye_pos(vector *eye);
291
292 // fill in the eye orient for this frame
293 void neb2_get_eye_orient(matrix *eye);
294
295 // get a (semi) random bitmap to use for a poof
296 int neb2_get_bitmap();
297
298 // regenerate the player nebula
299 void neb2_regen();
300
301
302 // --------------------------------------------------------------------------------------------------------
303 // NEBULA FUNCTIONS
304 //
305
306 // initialize neb2 stuff at game startup
307 void neb2_init()
308 {       
309         char name[255] = "";
310
311         // read in the nebula.tbl
312         read_file_text("nebula.tbl");
313         reset_parse();
314
315         // background bitmaps
316         Neb2_bitmap_count = 0;
317         while(!optional_string("#end")){
318                 // nebula
319                 required_string("+Nebula:");
320                 stuff_string(name, F_NAME, NULL);
321
322                 if(Neb2_bitmap_count < MAX_NEB2_BITMAPS){
323                         strcpy(Neb2_bitmap_filenames[Neb2_bitmap_count++], name);
324                 }
325         }
326
327         // poofs
328         Neb2_poof_count = 0;
329         while(!optional_string("#end")){
330                 // nebula
331                 required_string("+Poof:");
332                 stuff_string(name, F_NAME, NULL);
333
334                 if(Neb2_poof_count < MAX_NEB2_POOFS){
335                         strcpy(Neb2_poof_filenames[Neb2_poof_count++], name);
336                 }
337         }
338
339         // should always have 6 neb poofs
340         Assert(Neb2_poof_count == 6);
341 }
342
343 // set detail level
344 void neb2_set_detail_level(int level)
345 {
346         // sanity
347         if(level < 0){
348                 Nd = &Neb2_detail[0];
349                 return;
350         }
351         if(level >= MAX_DETAIL_LEVEL){
352                 Nd = &Neb2_detail[MAX_DETAIL_LEVEL-1];
353                 return;
354         }
355
356         Nd = &Neb2_detail[level];
357
358         // regen the player neb
359         Neb2_regen = 1;
360 }
361
362 // initialize nebula stuff - call from game_post_level_init(), so the mission has been loaded
363 void neb2_level_init()
364 {
365         int idx;                
366
367         // standalone servers can bail here
368         if(Game_mode & GM_STANDALONE_SERVER){
369                 return;
370         }
371
372         // if the mission is not a fullneb mission, skip
373         if(!(The_mission.flags & MISSION_FLAG_FULLNEB)){
374                 Neb2_render_mode = NEB2_RENDER_NONE;
375                 Neb2_awacs = -1.0f;
376                 return;
377         }
378
379         /*
380         if(gr_screen.mode == GR_DIRECT3D){
381                 max_alpha_player = NEB2_MAX_ALPHA_D3D;
382         } else {
383                 max_alpha_player = NEB2_MAX_ALPHA_GLIDE;
384         }
385         */
386
387         // by default we'll use pof rendering
388         Neb2_render_mode = NEB2_RENDER_POF;
389         stars_set_background_model(BACKGROUND_MODEL_FILENAME, Neb2_texture_name);
390
391         // load in all nebula bitmaps
392         for(idx=0; idx<Neb2_poof_count; idx++){
393                 if(Neb2_poofs[idx] < 0){
394                         Neb2_poofs[idx] = bm_load(Neb2_poof_filenames[idx]);
395                 }
396         }
397
398         pneb_tried = 0;         
399         pneb_tossed_alpha = 0;          
400         pneb_tossed_dot = 0;
401         neb_tried = 0;          
402         neb_tossed_alpha = 0;           
403         neb_tossed_dot = 0;
404         neb_tossed_count = 0;
405
406         // setup proper fogging values
407         switch(gr_screen.mode){
408         case GR_GLIDE:
409                 Neb_backg_fog_near = NEB_BACKG_FOG_NEAR_GLIDE;
410                 Neb_backg_fog_far = NEB_BACKG_FOG_FAR_GLIDE;                            
411                 break;
412         case GR_DIRECT3D:
413                 Neb_backg_fog_near = NEB_BACKG_FOG_NEAR_D3D;
414                 Neb_backg_fog_far = NEB_BACKG_FOG_FAR_D3D;                                      
415                 break;
416         case GR_SOFTWARE:
417                 Assert(Fred_running);
418                 break;
419         default :
420                 Int3();
421         }       
422
423         // regen the nebula
424         neb2_eye_changed();
425 }
426
427 // shutdown nebula stuff
428 void neb2_level_close()
429 {
430         int idx;
431         
432         // standalone servers can bail here
433         if(Game_mode & GM_STANDALONE_SERVER){
434                 return;
435         }
436
437         // if the mission is not a fullneb mission, skip
438         if(!(The_mission.flags & MISSION_FLAG_FULLNEB)){
439                 return;
440         }
441
442         // unload all nebula bitmaps
443         for(idx=0; idx<Neb2_poof_count; idx++){
444                 if(Neb2_poofs[idx] >= 0){
445                         bm_unload(Neb2_poofs[idx]);
446                         Neb2_poofs[idx] = -1;
447                 }
448         }       
449
450         // unflag the mission as being fullneb so stuff doesn't fog in the techdata room :D
451         The_mission.flags &= ~MISSION_FLAG_FULLNEB;
452 }
453
454 // call before beginning all rendering
455 void neb2_render_setup(vector *eye_pos, matrix *eye_orient)
456 {
457         // standalone servers can bail here
458         if(Game_mode & GM_STANDALONE_SERVER){
459                 return;
460         }
461
462         // if the mission is not a fullneb mission, skip
463         if(!(The_mission.flags & MISSION_FLAG_FULLNEB)){                
464                 return;
465         }
466
467         // pre-render the real background nebula
468         neb2_pre_render(eye_pos, eye_orient);           
469 }
470
471 // level paging code
472 void neb2_page_in()
473 {
474         int idx;
475
476         // load in all nebula bitmaps
477         for(idx=0; idx<Neb2_poof_count; idx++){
478                 if((Neb2_poofs[idx] >= 0) && (Neb2_poof_flags & (1<<idx))){
479                         bm_page_in_texture(Neb2_poofs[idx]);
480                 }
481         }
482 }
483
484 // should we not render this object because its obscured by the nebula?
485 int neb_skip_opt = 1;
486 DCF(neb_skip, "")
487 {
488         neb_skip_opt = !neb_skip_opt;
489         if(neb_skip_opt){
490                 dc_printf("Using neb object skipping!\n");
491         } else {
492                 dc_printf("Not using neb object skipping!\n");
493         }
494 }
495 int neb2_skip_render(object *objp, float z_depth)
496 {
497         float fog_near, fog_far;                
498
499         // if we're never skipping
500         if(!neb_skip_opt){
501                 return 0;
502         }
503
504         // lame rendering
505         if(Neb2_render_mode == NEB2_RENDER_LAME){
506                 return 0;
507         }
508
509         // get near and far fog values based upon object type and rendering mode
510         neb2_get_fog_values(&fog_near, &fog_far, objp);
511
512         // by object type
513         switch( objp->type )    {
514         // some objects we always render
515         case OBJ_SHOCKWAVE:
516         case OBJ_JUMP_NODE:
517         case OBJ_NONE:
518         case OBJ_GHOST:
519         case OBJ_BEAM:
520                 return 0;                       
521                 
522         // any weapon over 500 meters away 
523         case OBJ_WEAPON:                
524                 if(z_depth >= 500.0f){
525                         return 1;
526                 }
527                 break;
528
529         // any small ship over the fog limit, or any cruiser 50% further than the fog limit
530         case OBJ_SHIP:  
531                 ship_info *sip;
532                 if((objp->instance >= 0) && (Ships[objp->instance].ship_info_index >= 0)){
533                         sip = &Ship_info[Ships[objp->instance].ship_info_index];
534                 } else {
535                         return 0;
536                 }
537
538                 // small ships over the fog limit by a small factor
539                 if((sip->flags & SIF_SMALL_SHIP) && (z_depth >= (fog_far * 1.3f))){
540                         return 1;
541                 }
542
543                 // big ships
544                 if((sip->flags & SIF_BIG_SHIP) && (z_depth >= (fog_far * 2.0f))){
545                         return 1;
546                 }
547
548                 // huge ships
549                 if((sip->flags & SIF_HUGE_SHIP) && (z_depth >= (fog_far * 3.0f))){
550                         return 1;
551                 }
552                 break;
553
554         // any fireball over the fog limit for small ships
555         case OBJ_FIREBALL:              
556                 /*
557                 if(z_depth >= fog_far){
558                         return 1;
559                 }
560                 */
561                 return 0;
562                 break;  
563
564         // any debris over the fog limit for small ships
565         case OBJ_DEBRIS:                
566                 /*
567                 if(z_depth >= fog_far){
568                         return 1;
569                 }
570                 */
571                 return 0;
572                 break;
573
574         // any asteroids 50% farther than the fog limit for small ships
575         case OBJ_ASTEROID:              
576                 if(z_depth >= (fog_far * 1.5f)){
577                         return 1;
578                 }
579                 break;
580
581         // any countermeasures over 100 meters away
582         case OBJ_CMEASURE:              
583                 if(z_depth >= 100.0f){
584                         return 1;
585                 }
586                 break;  
587
588         // hmmm. unknown object type - should probably let it through
589         default:
590                 return 0;
591         }
592
593         return 0;
594 }
595
596 // extend LOD 
597 float neb2_get_lod_scale(int objnum)
598 {       
599         ship *shipp;
600         ship_info *sip;
601
602         // bogus
603         if((objnum < 0) || (objnum >= MAX_OBJECTS) || (Objects[objnum].type != OBJ_SHIP) || (Objects[objnum].instance < 0) || (Objects[objnum].instance >= MAX_SHIPS)){
604                 return 1.0f;
605         }
606         shipp = &Ships[Objects[objnum].instance];
607         sip = &Ship_info[shipp->ship_info_index];
608
609         // small ship?
610         if(sip->flags & SIF_SMALL_SHIP){
611                 return 1.8f;
612         } else if(sip->flags & SIF_BIG_SHIP){
613                 return 1.4f;
614         }       
615
616         // hmm
617         return 1.0f;
618 }
619
620
621 // --------------------------------------------------------------------------------------------------------
622 // NEBULA FORWARD DEFINITIONS
623 //
624
625 // return the alpha the passed poof should be rendered with, for a 2 shell nebula
626 float neb2_get_alpha_2shell(float inner_radius, float outer_radius, float magic_num, vector *v)
627 {                       
628         float dist;
629         float alpha;
630         vector eye_pos;
631
632         // get the eye position
633         neb2_get_eye_pos(&eye_pos);
634         
635         // determine what alpha to draw this bitmap with
636         // higher alpha the closer the bitmap gets to the eye
637         dist = vm_vec_dist_quick(&eye_pos, v);  
638
639         // if the point is inside the inner radius, alpha is based on distance to the player's eye, 
640         // becoming more transparent as it gets close
641         if(dist <= inner_radius){
642                 // alpha per meter between the magic # and the inner radius
643                 alpha = Nd->max_alpha_glide / (inner_radius - magic_num);
644
645                 // above value times the # of meters away we are
646                 alpha *= (dist - magic_num);
647                 return alpha < 0.0f ? 0.0f : alpha;
648         }
649         // if the point is outside the inner radius, it starts out as completely transparent at max     
650         // outer radius, and becomes more opaque as it moves towards inner radius       
651         else if(dist <= outer_radius){                          
652                 // alpha per meter between the outer radius and the inner radius
653                 alpha = Nd->max_alpha_glide / (outer_radius - inner_radius);
654
655                 // above value times the range between the outer radius and the poof
656                 return alpha < 0.0f ? 0.0f : alpha * (outer_radius - dist);
657         }
658
659         // otherwise transparent
660         return 0.0f;    
661 }
662
663 // return an alpha value for a bitmap offscreen based upon "break" value
664 float neb2_get_alpha_offscreen(float sx, float sy, float incoming_alpha)
665 {       
666         float alpha = 0.0f;
667         float per_pixel_x = incoming_alpha / (float)Nd->break_x;
668         float per_pixel_y = incoming_alpha / (float)Nd->break_y;
669         int off_x = ((sx < 0.0f) || (sx > (float)gr_screen.max_w));     
670         int off_y = ((sy < 0.0f) || (sy > (float)gr_screen.max_h));
671         float off_x_amount = 0.0f;
672         float off_y_amount = 0.0f;
673
674         // determine how many pixels outside we are
675         if(off_x){
676                 if(sx < 0.0f){
677                         off_x_amount = fl_abs(sx);
678                 } else {
679                         off_x_amount = sx - (float)gr_screen.max_w;
680                 }
681         }
682         if(off_y){
683                 if(sy < 0.0f){
684                         off_y_amount = fl_abs(sy);
685                 } else {
686                         off_y_amount = sy - (float)gr_screen.max_h;
687                 }
688         }
689
690         // if offscreen X
691         if(off_x){
692                 // offscreen X and Y - and Y is greater
693                 if(off_y && (off_y_amount > off_x_amount)){                     
694                         alpha = incoming_alpha - (off_y_amount * per_pixel_y);
695                 } else {
696                         alpha = incoming_alpha - (off_x_amount * per_pixel_x);
697                 }
698         }
699         // offscreen y
700         else if(off_y){
701                 alpha = incoming_alpha - (off_y_amount * per_pixel_y);
702         }
703         // should never get here
704         else {          
705                 Int3();
706         }
707
708         return alpha < 0.0f ? 0.0f : alpha;                     
709 }
710
711 // -------------------------------------------------------------------------------------------------
712 // WACKY LOCAL PLAYER NEBULA STUFF
713 //
714
715 vector cube_cen;
716
717 int crossed_border()
718 {
719         vector eye_pos;
720         float ws = Nd->cube_dim / (float)Neb2_slices;
721         float hs = Nd->cube_dim / (float)Neb2_slices;
722         float ds = Nd->cube_dim / (float)Neb2_slices;
723
724         // get the eye position
725         neb2_get_eye_pos(&eye_pos);
726
727         // check left, right (0, and 1, x and -x)
728         if(cube_cen.x - eye_pos.x > ws){
729                 // -x
730                 return 0;
731         } else if(eye_pos.x - cube_cen.x > ws){
732                 // +x
733                 return 1;
734         }
735
736         // check up, down (2, and 3, y and -y)
737         if(cube_cen.y - eye_pos.y > hs){
738                 // -y
739                 return 2;
740         } else if(eye_pos.y - cube_cen.y > hs){
741                 // +y
742                 return 3;
743         }
744
745         // check front, back (4, and 5, z and -z)
746         if(cube_cen.z - eye_pos.z > ds){
747                 // -z
748                 return 4;
749         } else if(eye_pos.z - cube_cen.z > ds){
750                 // +z
751                 return 5;
752         }
753
754         // nothing
755         return -1;
756 }
757
758 void neb2_copy(int xyz, int src, int dest)
759 {
760         int idx1, idx2;
761
762         switch(xyz){
763         case 0:
764                 for(idx1=0; idx1<Neb2_slices; idx1++){
765                         for(idx2=0; idx2<Neb2_slices; idx2++){
766                                 Neb2_cubes[dest][idx1][idx2] = Neb2_cubes[src][idx1][idx2];                             
767                         }
768                 }
769                 break;
770         case 1:
771                 for(idx1=0; idx1<Neb2_slices; idx1++){
772                         for(idx2=0; idx2<Neb2_slices; idx2++){                          
773                                 Neb2_cubes[idx1][dest][idx2] = Neb2_cubes[idx1][src][idx2];                             
774                         }
775                 }
776                 break;
777         case 2:
778                 for(idx1=0; idx1<Neb2_slices; idx1++){
779                         for(idx2=0; idx2<Neb2_slices; idx2++){
780                                 Neb2_cubes[idx1][idx2][dest] = Neb2_cubes[idx1][idx2][src];                             
781                         }
782                 }
783                 break;
784         default:
785                 Int3();
786                 break;
787         }
788 }
789
790 void neb2_gen_slice(int xyz, int src, vector *cube_center)
791 {
792         int idx1, idx2; 
793         float h_incw, h_inch, h_incd;
794         float ws, hs, ds;
795         vector cube_corner;     
796         vector *v;
797
798         ws = Nd->cube_dim / (float)Neb2_slices;
799         h_incw = ws / 2.0f;
800         hs = Nd->cube_dim / (float)Neb2_slices;
801         h_inch = hs / 2.0f;
802         ds = Nd->cube_dim / (float)Neb2_slices; 
803         h_incd = ds / 2.0f;
804         cube_corner = *cube_center;             
805         cube_corner.x -= (Nd->cube_dim / 2.0f);                 
806         cube_corner.y -= (Nd->cube_dim / 2.0f); 
807         cube_corner.z -= (Nd->cube_dim / 2.0f); 
808         switch(xyz){
809         case 0:
810                 for(idx1=0; idx1<Neb2_slices; idx1++){
811                         for(idx2=0; idx2<Neb2_slices; idx2++){
812                                 v = &Neb2_cubes[src][idx1][idx2].pt;
813
814                                 v->x = h_incw + (ws * (float)src) + frand_range(-Nd->wj, Nd->wj);
815                                 v->y = h_inch + (hs * (float)idx1) + frand_range(-Nd->hj, Nd->hj);
816                                 v->z = h_incd + (ds * (float)idx2) + frand_range(-Nd->dj, Nd->dj);
817                                 vm_vec_add2(v, &cube_corner);
818
819                                 // set the bitmap
820                                 Neb2_cubes[src][idx1][idx2].bmap = neb2_get_bitmap();
821
822                                 // set the rotation speed
823                                 Neb2_cubes[src][idx1][idx2].rot = 0.0f;
824                                 Neb2_cubes[src][idx1][idx2].rot_speed = frand_range(-max_rotation, max_rotation);
825                                 Neb2_cubes[src][idx1][idx2].flash = 0.0f;
826                         }
827                 }
828                 break;
829         case 1:
830                 for(idx1=0; idx1<Neb2_slices; idx1++){
831                         for(idx2=0; idx2<Neb2_slices; idx2++){
832                                 v = &Neb2_cubes[idx1][src][idx2].pt;
833                                 
834                                 v->x = h_incw + (ws * (float)idx1) + frand_range(-Nd->wj, Nd->wj);
835                                 v->y = h_inch + (hs * (float)src) + frand_range(-Nd->hj, Nd->hj);
836                                 v->z = h_incd + (ds * (float)idx2) + frand_range(-Nd->dj, Nd->dj);
837                                 vm_vec_add2(v, &cube_corner);
838
839                                 // set the bitmap
840                                 Neb2_cubes[idx1][src][idx2].bmap = neb2_get_bitmap();
841
842                                 // set the rotation speed
843                                 Neb2_cubes[idx1][src][idx2].rot = 0.0f;
844                                 Neb2_cubes[idx1][src][idx2].rot_speed = frand_range(-max_rotation, max_rotation);
845                                 Neb2_cubes[src][idx1][idx2].flash = 0.0f;
846                         }
847                 }
848                 break;
849         case 2:
850                 for(idx1=0; idx1<Neb2_slices; idx1++){
851                         for(idx2=0; idx2<Neb2_slices; idx2++){
852                                 v = &Neb2_cubes[idx1][idx2][src].pt;
853
854                                 v->x = h_incw + (ws * (float)idx1) + frand_range(-Nd->wj, Nd->wj);
855                                 v->y = h_inch + (hs * (float)idx2) + frand_range(-Nd->hj, Nd->hj);
856                                 v->z = h_incd + (ds * (float)src) + frand_range(-Nd->dj, Nd->dj);
857                                 vm_vec_add2(v, &cube_corner);
858                                 
859                                 // set the bitmap
860                                 Neb2_cubes[idx1][idx2][src].bmap = neb2_get_bitmap();
861
862                                 // set the rotation speed
863                                 Neb2_cubes[idx1][idx2][src].rot = 0.0f;
864                                 Neb2_cubes[idx1][idx2][src].rot_speed = frand_range(-max_rotation, max_rotation);
865                                 Neb2_cubes[src][idx1][idx2].flash = 0.0f;
866                         }
867                 }
868                 break;
869         default:
870                 Int3();
871                 break;
872         }
873 }
874
875 // regenerate the player nebula
876 void neb2_regen()
877 {
878         int idx;
879         vector eye_pos; 
880         matrix eye_orient;
881
882         mprintf(("Regenerating local nebula!\n"));
883
884         // get eye position and orientation
885         neb2_get_eye_pos(&eye_pos);
886         neb2_get_eye_orient(&eye_orient);       
887
888         // determine the corner of the cube
889         cube_cen = eye_pos;
890                 
891         // generate slices of the cube
892         for(idx=0; idx<Neb2_slices; idx++){
893                 neb2_gen_slice(0, idx, &cube_cen);
894         }
895 }
896
897 float max_area = 100000000.0f;
898 DCF(max_area, "")
899 {
900         dc_get_arg(ARG_FLOAT);
901         max_area = Dc_arg_float;
902 }
903
904 float g3_draw_rotated_bitmap_area(vertex *pnt, float angle, float rad, uint tmap_flags, float area);
905 int neb_mode = 1;
906 int frames_total = 0;
907 int frame_count = 0;
908 float frame_avg;
909 void neb2_render_player()
910 {       
911         vertex p, ptemp;
912         int idx1, idx2, idx3;
913         float alpha;
914         int frame_rendered;     
915         vector eye_pos;
916         matrix eye_orient;
917
918 #ifndef NDEBUG
919         float this_area;
920         float frame_area = max_area;
921         float total_area = 0.0f;
922 #endif
923
924         // standalone servers can bail here
925         if(Game_mode & GM_STANDALONE_SERVER){
926                 return;
927         }
928
929         // if the mission is not a fullneb mission, skip
930         if(!(The_mission.flags & MISSION_FLAG_FULLNEB)){
931                 return;
932         }               
933
934         if(Neb2_regen){
935                 neb2_regen();
936                 Neb2_regen = 0;
937         }
938
939         // don't render in lame mode
940         if((Neb2_render_mode == NEB2_RENDER_LAME) || (Neb2_render_mode == NEB2_RENDER_NONE)){
941                 return;
942         }
943
944         // get eye position and orientation
945         neb2_get_eye_pos(&eye_pos);
946         neb2_get_eye_orient(&eye_orient);
947         
948         // maybe swap stuff around if the player crossed a "border"     
949         for(idx2=0; idx2<3; idx2++){
950                 switch(crossed_border()){
951                 case -1:
952                         break;
953                 // -x
954                 case 0 :
955                         cube_cen.x -= Nd->cube_dim / (float)Neb2_slices;
956                         for(idx1=Neb2_slices-1; idx1>0; idx1--){
957                                 neb2_copy(0, idx1-1, idx1);
958                         }
959                         neb2_gen_slice(0, 0, &cube_cen);                                
960                         break;
961                 // x
962                 case 1 :
963                         cube_cen.x += Nd->cube_dim / (float)Neb2_slices;
964                         for(idx1=0; idx1<Neb2_slices-1; idx1++){
965                                 neb2_copy(0, idx1+1, idx1);
966                         }                               
967                         neb2_gen_slice(0, Neb2_slices - 1, &cube_cen);                          
968                         break;
969                 // -y
970                 case 2 :                        
971                         cube_cen.y -= Nd->cube_dim / (float)Neb2_slices;
972                         for(idx1=Neb2_slices-1; idx1>0; idx1--){
973                                 neb2_copy(1, idx1-1, idx1);
974                         }                               
975                         neb2_gen_slice(1, 0, &cube_cen);                                
976                         break;
977                 // y
978                 case 3 :                                                
979                         cube_cen.y += Nd->cube_dim / (float)Neb2_slices;
980                         for(idx1=0; idx1<Neb2_slices-1; idx1++){
981                                 neb2_copy(1, idx1+1, idx1);
982                         }                               
983                         neb2_gen_slice(1, Neb2_slices - 1, &cube_cen);                          
984                         break;
985                 // -z
986                 case 4 :                        
987                         cube_cen.z -= Nd->cube_dim / (float)Neb2_slices;
988                         for(idx1=Neb2_slices-1; idx1>0; idx1--){
989                                 neb2_copy(2, idx1-1, idx1);
990                         }                                                               
991                         neb2_gen_slice(2, 0, &cube_cen);                                
992                         break;
993                 // z
994                 case 5 :                                                
995                         cube_cen.z += Nd->cube_dim / (float)Neb2_slices;
996                         for(idx1=0; idx1<Neb2_slices-1; idx1++){
997                                 neb2_copy(2, idx1+1, idx1);
998                         }                                                                                               
999                         neb2_gen_slice(2, Neb2_slices - 1, &cube_cen);                          
1000                         break;
1001                 }       
1002         }       
1003
1004         // if we've switched nebula rendering off
1005         if(Neb2_render_mode == NEB2_RENDER_NONE){
1006                 return;
1007         }       
1008         
1009         frame_rendered = 0;
1010         // render the nebula
1011         for(idx1=0; idx1<Neb2_slices; idx1++){
1012                 for(idx2=0; idx2<Neb2_slices; idx2++){
1013                         for(idx3=0; idx3<Neb2_slices; idx3++){
1014                                 pneb_tried++;                           
1015
1016                                 // rotate the poof
1017                                 Neb2_cubes[idx1][idx2][idx3].rot += (Neb2_cubes[idx1][idx2][idx3].rot_speed * flFrametime);
1018                                 if(Neb2_cubes[idx1][idx2][idx3].rot >= 360.0f){
1019                                         Neb2_cubes[idx1][idx2][idx3].rot = 0.0f;
1020                                 }                               
1021                                 
1022                                 // optimization 1 - don't draw backfacing poly's
1023                                 // useless
1024                                 if(vm_vec_dot_to_point(&eye_orient.fvec, &eye_pos, &Neb2_cubes[idx1][idx2][idx3].pt) <= 0.0f){
1025                                         pneb_tossed_dot++;
1026                                         continue;
1027                                 }
1028
1029                                 // rotate and project the vertex into viewspace
1030                                 g3_rotate_vertex(&p, &Neb2_cubes[idx1][idx2][idx3].pt);
1031                                 ptemp = p;
1032                                 g3_project_vertex(&ptemp);
1033
1034                                 // get the proper alpha value                           
1035                                 alpha = neb2_get_alpha_2shell(Nd->cube_inner, Nd->cube_outer, Nd->prad/4.0f, &Neb2_cubes[idx1][idx2][idx3].pt);
1036
1037                                 // optimization 2 - don't draw 0.0f or less poly's
1038                                 // this amounts to big savings
1039                                 if(alpha <= Nd->break_alpha){
1040                                         pneb_tossed_alpha++;
1041                                         continue;
1042                                 }
1043
1044                                 // drop poly's which are offscreen at all                               
1045                                 // if the poly's are offscreen                                          
1046                                 if((ptemp.sx < 0.0f) || (ptemp.sx > (float)gr_screen.max_w) || (ptemp.sy < 0.0f) || (ptemp.sy > (float)gr_screen.max_h) ){
1047                                         alpha = neb2_get_alpha_offscreen(ptemp.sx, ptemp.sy, alpha);
1048                                 }                               
1049
1050                                 // optimization 2 - don't draw 0.0f or less poly's
1051                                 // this amounts to big savings
1052                                 if(alpha <= Nd->break_alpha){
1053                                         pneb_tossed_alpha++;
1054                                         continue;
1055                                 }
1056         
1057                                 // set the bitmap and render                            
1058                                 gr_set_bitmap(Neb2_cubes[idx1][idx2][idx3].bmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha + Neb2_cubes[idx1][idx2][idx3].flash);
1059
1060 #ifndef NDEBUG
1061                                 this_area = g3_draw_rotated_bitmap_area(&p, fl_radian(Neb2_cubes[idx1][idx2][idx3].rot), Nd->prad, TMAP_FLAG_TEXTURED, max_area);                               
1062                                 total_area += this_area;
1063                                 frame_area -= this_area;
1064                                 frame_rendered++;                       
1065 #else 
1066                                 g3_draw_rotated_bitmap(&p, fl_radian(Neb2_cubes[idx1][idx2][idx3].rot), Nd->prad, TMAP_FLAG_TEXTURED);
1067 #endif
1068                         }
1069                 }
1070         }       
1071
1072         frames_total += frame_rendered;
1073         frame_count++;
1074         frame_avg = (float)frames_total / (float)frame_count;   
1075
1076         // gr_set_color_fast(&Color_bright_red);
1077         // gr_printf(30, 100, "Area %.3f", total_area);
1078 #ifdef NEB2_THUMBNAIL
1079         extern int tbmap;
1080         if(tbmap != -1){
1081                 gr_set_bitmap(tbmap);
1082                 gr_bitmap(0, 0);
1083         }
1084 #endif
1085 }       
1086
1087 // call this when the player's viewpoint has changed, this will cause the code to properly reset
1088 // the eye's local poofs
1089 void neb2_eye_changed()
1090 {
1091         Neb2_regen = 1;
1092 }
1093
1094 // get near and far fog values based upon object type and rendering mode
1095 void neb2_get_fog_values(float *fnear, float *ffar, object *objp)
1096 {
1097         int fog_index;
1098
1099         // default values in case something truly nasty happens
1100         *fnear = 10.0f;
1101         *ffar = 1000.0f;
1102
1103         // determine what fog index to use
1104         if(objp->type == OBJ_SHIP){
1105                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
1106                 if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
1107                         fog_index = SHIP_TYPE_FIGHTER_BOMBER;
1108                 } else {
1109                         fog_index = ship_query_general_type(objp->instance);
1110                         Assert(fog_index >= 0);
1111                         if(fog_index < 0){
1112                                 fog_index = SHIP_TYPE_FIGHTER_BOMBER;
1113                         }
1114                 }
1115         }
1116         // fog everything else like a fighter
1117         else {
1118                 fog_index = SHIP_TYPE_FIGHTER_BOMBER;
1119         }
1120
1121         // get the values
1122         switch(gr_screen.mode){
1123         case GR_GLIDE:
1124                 *fnear = Neb_ship_fog_vals_glide[fog_index][0];
1125                 *ffar = Neb_ship_fog_vals_glide[fog_index][1];
1126                 break;
1127
1128         case GR_DIRECT3D:
1129                 *fnear = Neb_ship_fog_vals_d3d[fog_index][0];
1130                 *ffar = Neb_ship_fog_vals_d3d[fog_index][1];
1131                 break;
1132
1133         default:
1134                 Int3();
1135         }
1136 }
1137
1138 // given a position in space, return a value from 0.0 to 1.0 representing the fog level 
1139 float neb2_get_fog_intensity(object *obj)
1140 {
1141         float f_near, f_far, pct;
1142
1143         // get near and far fog values based upon object type and rendering mode
1144         neb2_get_fog_values(&f_near, &f_far, obj);
1145
1146         // get the fog pct
1147         pct = vm_vec_dist_quick(&Eye_position, &obj->pos) / (f_far - f_near);
1148         if(pct < 0.0f){
1149                 return 0.0f;
1150         } else if(pct > 1.0f){
1151                 return 1.0f;
1152         }
1153
1154         return pct;
1155 }
1156
1157 // fogging stuff --------------------------------------------------------------------
1158
1159 // do a pre-render of the background nebula
1160 #define ESIZE                                   32
1161 ubyte tpixels[ESIZE * ESIZE * 4];               // for 32 bits
1162 int last_esize = -1;
1163 int this_esize = ESIZE;
1164 extern float Viewer_zoom;
1165 float ex_scale, ey_scale;
1166 int tbmap = -1;
1167 void neb2_pre_render(vector *eye_pos, matrix *eye_orient)
1168 {       
1169         // bail early in lame and poly modes
1170         if(Neb2_render_mode != NEB2_RENDER_POF){
1171                 return;
1172         }
1173
1174         // set the view clip
1175         gr_screen.clip_width = this_esize;
1176         gr_screen.clip_height = this_esize;
1177         g3_start_frame(1);                                                      // Turn on zbuffering
1178         g3_set_view_matrix(eye_pos, eye_orient, Viewer_zoom);
1179         gr_set_clip(0, 0, this_esize, this_esize);              
1180
1181         // render the background properly
1182         // hack - turn off nebula stuff
1183         int neb_save = Neb2_render_mode;
1184         Neb2_render_mode = NEB2_RENDER_NONE;
1185
1186         // draw background stuff nebula                 
1187         extern void stars_draw_background();
1188         stars_draw_background();                
1189
1190         Neb2_render_mode = neb_save;    
1191
1192         // HACK - flush d3d here so everything is rendered
1193         if(gr_screen.mode == GR_DIRECT3D){
1194                 extern void d3d_flush();
1195                 d3d_flush();
1196         }
1197
1198         // grab the region
1199         gr_get_region(0, this_esize, this_esize, (ubyte*)tpixels);      
1200
1201 #ifdef NEB2_THUMBNAIL
1202         if(tbmap == -1){
1203                 tbmap = bm_create(16, this_esize, this_esize, tpixels, 0);
1204                 bm_lock(tbmap, 16, 0);
1205                 bm_unlock(tbmap);
1206         }
1207 #endif
1208
1209         // maybe do some swizzling
1210         
1211         // end the frame
1212         g3_end_frame();
1213         
1214         gr_clear();     
1215
1216         // HACK - flush d3d here so everything is properly cleared
1217         if(gr_screen.mode == GR_DIRECT3D){
1218                 extern void d3d_flush();
1219                 d3d_flush();
1220         }
1221
1222         // if the size has changed between frames, make a new bitmap
1223         if(this_esize != last_esize){
1224                 last_esize = this_esize;                                                
1225                 
1226                 // recalculate ex_scale and ey_scale values for looking up color values
1227                 ex_scale = (float)this_esize / (float)gr_screen.max_w;
1228                 ey_scale = (float)this_esize / (float)gr_screen.max_h;
1229         }       
1230                 
1231         // restore the game clip stuff
1232         extern void game_set_view_clip();
1233         game_set_view_clip();   
1234 }
1235
1236 // wacky scheme for smoothing colors
1237 int wacky_scheme = 3;
1238
1239 // get the color of the pixel in the small pre-rendered background nebula
1240 #define PIXEL_INDEX_SMALL(xv, yv)       ( (this_esize * (yv) * gr_screen.bytes_per_pixel) + ((xv) * gr_screen.bytes_per_pixel) )
1241 void neb2_get_pixel(int x, int y, int *r, int *g, int *b)
1242 {       
1243         int ra, ga, ba;
1244         ubyte rv, gv, bv;       
1245         int avg_count;
1246         int xs, ys;
1247
1248         // if we're in lame rendering mode, return a constant value
1249         if(Neb2_render_mode == NEB2_RENDER_LAME){
1250                 *r = Neb2_background_color[0];
1251                 *g = Neb2_background_color[1];
1252                 *b = Neb2_background_color[2];
1253
1254                 return;
1255         }               
1256         
1257         // get the proper pixel index to be looking up
1258         rv = gv = bv = 0;       
1259         
1260         // select screen format
1261         BM_SELECT_SCREEN_FORMAT();
1262
1263         // pixel plus all immediate neighbors (on the small screen - should be more effective than 2 or 1)      
1264         xs = (int)(ex_scale * x);
1265         ys = (int)(ey_scale * y);               
1266
1267         // sometimes goes over by 1 in direct3d
1268         if(ys >= (this_esize - 1)){
1269                 ys--;
1270         }
1271
1272         avg_count = 0;
1273         bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs, ys)], &rv, &gv, &bv, NULL);
1274         ra = rv;
1275         ga = gv;
1276         ba = bv;
1277         avg_count++;
1278         if(xs > 0){                     
1279                 bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs - 1, ys)], &rv, &gv, &bv, NULL);        // left
1280                 ra += rv;
1281                 ba += bv;
1282                 ga += gv;
1283                 avg_count++;
1284         }
1285         if(xs < this_esize - 1){                        
1286                 bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs + 1, ys)], &rv, &gv, &bv, NULL);        // right
1287                 ra += rv;
1288                 ba += bv;
1289                 ga += gv;
1290                 avg_count++;
1291         }
1292         if(ys > 0){                     
1293                 bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs, ys - 1)], &rv, &gv, &bv, NULL);        // top
1294                 ra += rv;
1295                 ba += bv;
1296                 ga += gv;
1297                 avg_count++;
1298         }                               
1299         if(ys < this_esize - 2){                        
1300                 bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs, ys + 1)], &rv, &gv, &bv, NULL);        // bottom
1301                 ra += rv;
1302                 ba += bv;
1303                 ga += gv;
1304                 avg_count++;
1305         }
1306                 
1307         if((xs > 0) && (ys > 0)){                       
1308                 bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs - 1, ys - 1)], &rv, &gv, &bv, NULL);    // upper left
1309                 ra += rv;
1310                 ba += bv;
1311                 ga += gv;
1312                 avg_count++;
1313         }
1314         if((xs < this_esize - 1) && (ys < this_esize - 1)){                     
1315                 bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs + 1, ys + 1)], &rv, &gv, &bv, NULL);    // lower right
1316                 ra += rv;
1317                 ba += bv;
1318                 ga += gv;
1319                 avg_count++;
1320         }
1321         if((ys > 0) && (xs < this_esize - 1)){                  
1322                 bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs + 1, ys - 1)], &rv, &gv, &bv, NULL);    // lower left
1323                 ra += rv;
1324                 ba += bv;
1325                 ga += gv;
1326                 avg_count++;
1327         }
1328         if((ys < this_esize - 1) && (xs > 0)){                  
1329                 bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs - 1, ys + 1)], &rv, &gv, &bv, NULL);    // upper right
1330                 ra += rv;
1331                 ba += bv;
1332                 ga += gv;
1333                 avg_count++;
1334         }               
1335
1336         rv = (ubyte) (ra / avg_count);
1337         gv = (ubyte) (ga / avg_count);
1338         bv = (ubyte) (ba / avg_count);  
1339
1340         // return values
1341         *r = (int)rv;
1342         *g = (int)gv;
1343         *b = (int)bv;
1344 }
1345
1346 // get the color to fog the background color to
1347 void neb2_get_backg_color(int *r, int *g, int *b)
1348 {
1349         *r = Neb2_background_color[0];
1350         *g = Neb2_background_color[1];
1351         *b = Neb2_background_color[2];
1352 }
1353
1354 // set the background color
1355 void neb2_set_backg_color(int r, int g, int b)
1356 {
1357         Neb2_background_color[0] = r;
1358         Neb2_background_color[1] = g;
1359         Neb2_background_color[2] = b;
1360 }
1361
1362 // fill in the position of the eye for this frame
1363 void neb2_get_eye_pos(vector *eye)
1364 {
1365         *eye = Eye_position;
1366 }
1367
1368 // fill in the eye orient for this frame
1369 void neb2_get_eye_orient(matrix *eye)
1370 {
1371         *eye = Eye_matrix;
1372 }
1373
1374 // get a (semi) random bitmap to use for a poof
1375 int neb2_get_bitmap()
1376 {
1377         int count = 0;
1378         int huh;
1379         static int neb2_choose = 0;
1380
1381         // get a random count
1382         count = (int)frand_range(1.0f, 5.0f);
1383
1384         // very ad-hoc
1385         while(count > 0){
1386                 // don't cycle too many times
1387                 huh = 0;                
1388                 do {
1389                         if(neb2_choose == MAX_NEB2_POOFS - 1){
1390                                 neb2_choose = 0;
1391                         } else {
1392                                 neb2_choose++;
1393                         }
1394
1395                         huh++;
1396                 } while(!(Neb2_poof_flags & (1<<neb2_choose)) && (huh < 10));
1397
1398                 count--;
1399         }
1400
1401         // bitmap 0
1402         if(Neb2_poofs[neb2_choose] < 0){        
1403                 return Neb2_poofs[0];
1404         } 
1405         return Neb2_poofs[neb2_choose];
1406 }
1407
1408 // nebula DCF functions ------------------------------------------------------
1409
1410 DCF(neb2, "list nebula console commands")
1411 {               
1412         dc_printf("neb2_fog <X> <float> <float>  : set near and far fog planes for ship type X\n");
1413         dc_printf("where X is an integer from 1 - 11\n");
1414         dc_printf("1 = cargo containers, 2 = fighters/bombers, 3 = cruisers\n");
1415         dc_printf("4 = freighters, 5 = capital ships, 6 = transports, 7 = support ships\n");
1416         dc_printf("8 = navbuoys, 9 = sentryguns, 10 = escape pods, 11 = background nebula polygons\n\n");
1417         
1418         dc_printf("neb2_max_alpha   : max alpha value (0.0 to 1.0) for cloud poofs. 0.0 is completely transparent\n");
1419         dc_printf("neb2_break_alpha : alpha value (0.0 to 1.0) at which faded polygons are not drawn. higher values generally equals higher framerate, with more visual cloud popping\n");
1420         dc_printf("neb2_break_off   : how many pixels offscreen (left, right, top, bottom) when a cloud poof becomes fully transparent. Lower values cause quicker fading\n");
1421         dc_printf("neb2_smooth      : magic fog smoothing modes (0 - 3)\n");
1422         dc_printf("neb2_select      : <int> <int>  where the first # is the bitmap to be adjusting (0 through 5), and the second int is a 0 or 1, to turn off and on\n");
1423         dc_printf("neb2_rot         : set max rotation speed for poofs\n");
1424         dc_printf("neb2_prad        : set cloud poof radius\n");
1425         dc_printf("neb2_cdim        : poof cube dimension\n");
1426         dc_printf("neb2_cinner      : poof cube inner dimension\n");
1427         dc_printf("neb2_couter      : poof cube outer dimension\n");
1428         dc_printf("neb2_jitter      : poof jitter\n");
1429         dc_printf("neb2_mode        : switch between no nebula, polygon background, amd pof background (0, 1 and 2 respectively)\n\n"); 
1430         dc_printf("neb2_ff          : flash fade/sec\n");
1431         dc_printf("neb2_background       : rgb background color\n");
1432
1433         dc_printf("neb2_fog_vals    : display all the current settings for all above values\n");        
1434 }
1435
1436 DCF(neb2_prad, "")
1437 {
1438         dc_get_arg(ARG_FLOAT);
1439         Nd->prad = Dc_arg_float;
1440 }
1441 DCF(neb2_cdim, "")
1442 {
1443         dc_get_arg(ARG_FLOAT);
1444         Nd->cube_dim = Dc_arg_float;
1445 }
1446
1447 DCF(neb2_cinner, "")
1448 {
1449         dc_get_arg(ARG_FLOAT);
1450         Nd->cube_inner = Dc_arg_float;
1451 }
1452
1453 DCF(neb2_couter, "")
1454 {
1455         dc_get_arg(ARG_FLOAT);
1456         Nd->cube_outer = Dc_arg_float;
1457 }
1458
1459 DCF(neb2_jitter, "")
1460 {
1461         dc_get_arg(ARG_FLOAT);
1462         Nd->hj = Nd->dj = Nd->wj = Dc_arg_float;
1463 }
1464
1465 DCF(neb2_fog, "")
1466 {
1467         int index;
1468         float fnear, ffar;
1469         dc_get_arg(ARG_INT);
1470         index = Dc_arg_int;
1471         dc_get_arg(ARG_FLOAT);
1472         fnear = Dc_arg_float;
1473         dc_get_arg(ARG_FLOAT);
1474         ffar = Dc_arg_float;
1475
1476         if((index >= 1) && (index <= 11) && (fnear >= 0.0f) && (ffar >= 0.0f) && (ffar > fnear)){
1477                 if(index == 11){
1478                         Neb_backg_fog_near = fnear;
1479                         Neb_backg_fog_far = ffar;
1480                 } else {
1481                         if(gr_screen.mode == GR_GLIDE){
1482                                 Neb_ship_fog_vals_glide[index][0] = fnear;
1483                                 Neb_ship_fog_vals_glide[index][1] = ffar;
1484                         } else {
1485                                 Assert(gr_screen.mode == GR_DIRECT3D);
1486                                 Neb_ship_fog_vals_d3d[index][0] = fnear;
1487                                 Neb_ship_fog_vals_d3d[index][1] = ffar;
1488                         }
1489                 }
1490         }
1491 }
1492
1493 DCF(neb2_max_alpha, "")
1494 {
1495         dc_get_arg(ARG_FLOAT);
1496         Nd->max_alpha_glide = Dc_arg_float;
1497 }
1498
1499 DCF(neb2_break_alpha, "")
1500 {
1501         dc_get_arg(ARG_FLOAT);
1502         Nd->break_alpha = Dc_arg_float;
1503 }
1504
1505 DCF(neb2_break_off, "")
1506 {
1507         dc_get_arg(ARG_INT);
1508         Nd->break_y = (float)Dc_arg_int;
1509         Nd->break_x = Nd->break_y * 1.3333f;
1510 }
1511
1512 DCF(neb2_smooth, "")
1513 {
1514         int index;
1515         dc_get_arg(ARG_INT);
1516         index = Dc_arg_int;
1517         if((index >= 0) && (index <= 3)){
1518                 wacky_scheme = index;
1519         }
1520 }
1521
1522 DCF(neb2_select, "")
1523 {
1524         dc_get_arg(ARG_INT);    
1525         int bmap = Dc_arg_int;
1526         if((bmap >= 0) && (bmap < Neb2_poof_count)){
1527                 dc_get_arg(ARG_INT);
1528
1529                 if(Dc_arg_int){
1530                         Neb2_poof_flags |= (1<<bmap);
1531                 } else {
1532                         Neb2_poof_flags &= ~(1<<bmap);
1533                 }
1534         }
1535 }
1536
1537 DCF(neb2_rot, "")
1538 {
1539         dc_get_arg(ARG_FLOAT);
1540         max_rotation = Dc_arg_float;
1541 }
1542
1543 DCF(neb2_ff, "")
1544 {
1545         dc_get_arg(ARG_FLOAT);
1546         neb2_flash_fade = Dc_arg_float;
1547 }
1548
1549 DCF(neb2_mode, "")
1550 {
1551         dc_get_arg(ARG_INT);
1552
1553         switch(Dc_arg_int){
1554         case NEB2_RENDER_NONE:
1555                 Neb2_render_mode = NEB2_RENDER_NONE;
1556                 break;
1557
1558         case NEB2_RENDER_POLY:
1559                 Neb2_render_mode = NEB2_RENDER_POLY;
1560                 break;
1561
1562         case NEB2_RENDER_POF:
1563                 Neb2_render_mode = NEB2_RENDER_POF;
1564                 stars_set_background_model(BACKGROUND_MODEL_FILENAME, "Eraseme3");
1565                 break;
1566
1567         case NEB2_RENDER_LAME:
1568                 Neb2_render_mode = NEB2_RENDER_LAME;
1569                 break;
1570         }
1571 }
1572
1573 DCF(neb2_slices, "")
1574 {
1575         dc_get_arg(ARG_INT);
1576         Neb2_slices = Dc_arg_int;
1577         Neb2_regen = 1;
1578 }
1579
1580 DCF(neb2_background, "")
1581 {
1582         int r, g, b;
1583
1584         dc_get_arg(ARG_INT);
1585         r = Dc_arg_int;
1586         dc_get_arg(ARG_INT);
1587         g = Dc_arg_int;
1588         dc_get_arg(ARG_INT);
1589         b = Dc_arg_int;
1590
1591         Neb2_background_color[0] = r;
1592         Neb2_background_color[1] = g;
1593         Neb2_background_color[2] = b;
1594 }
1595
1596 DCF(neb2_fog_vals, "")
1597 {
1598         dc_printf("neb2_fog : \n");
1599         if(gr_screen.mode == GR_GLIDE){         
1600                 dc_printf("(1)cargo containers : %f, %f\n", Neb_ship_fog_vals_glide[1][0], Neb_ship_fog_vals_glide[1][1]);
1601                 dc_printf("(2)fighters/bombers : %f, %f\n", Neb_ship_fog_vals_glide[2][0], Neb_ship_fog_vals_glide[2][1]);
1602                 dc_printf("(3)cruisers : %f, %f\n", Neb_ship_fog_vals_glide[3][0], Neb_ship_fog_vals_glide[3][1]);
1603                 dc_printf("(4)freighters : %f, %f\n", Neb_ship_fog_vals_glide[4][0], Neb_ship_fog_vals_glide[4][1]);
1604                 dc_printf("(5)cap ships : %f, %f\n", Neb_ship_fog_vals_glide[5][0], Neb_ship_fog_vals_glide[5][1]);
1605                 dc_printf("(6)transports : %f, %f\n", Neb_ship_fog_vals_glide[6][0], Neb_ship_fog_vals_glide[6][1]);
1606                 dc_printf("(7)support ships : %f, %f\n", Neb_ship_fog_vals_glide[7][0], Neb_ship_fog_vals_glide[7][1]);
1607                 dc_printf("(8)navbuoys : %f, %f\n", Neb_ship_fog_vals_glide[8][0], Neb_ship_fog_vals_glide[8][1]);
1608                 dc_printf("(9)sentry guns : %f, %f\n", Neb_ship_fog_vals_glide[9][0], Neb_ship_fog_vals_glide[9][1]);
1609                 dc_printf("(10)escape pods : %f, %f\n", Neb_ship_fog_vals_glide[10][0], Neb_ship_fog_vals_glide[10][1]);
1610                 dc_printf("(11)background polys : %f, %f\n\n", Neb_backg_fog_near, Neb_backg_fog_far);
1611
1612         } else {
1613                 Assert(gr_screen.mode == GR_DIRECT3D);
1614                 dc_printf("(1)cargo containers : %f, %f\n", Neb_ship_fog_vals_d3d[1][0], Neb_ship_fog_vals_d3d[1][1]);
1615                 dc_printf("(2)fighters/bombers : %f, %f\n", Neb_ship_fog_vals_d3d[2][0], Neb_ship_fog_vals_d3d[2][1]);
1616                 dc_printf("(3)cruisers : %f, %f\n", Neb_ship_fog_vals_d3d[3][0], Neb_ship_fog_vals_d3d[3][1]);
1617                 dc_printf("(4)freighters : %f, %f\n", Neb_ship_fog_vals_d3d[4][0], Neb_ship_fog_vals_d3d[4][1]);
1618                 dc_printf("(5)cap ships : %f, %f\n", Neb_ship_fog_vals_d3d[5][0], Neb_ship_fog_vals_d3d[5][1]);
1619                 dc_printf("(6)transports : %f, %f\n", Neb_ship_fog_vals_d3d[6][0], Neb_ship_fog_vals_d3d[6][1]);
1620                 dc_printf("(7)support ships : %f, %f\n", Neb_ship_fog_vals_d3d[7][0], Neb_ship_fog_vals_d3d[7][1]);
1621                 dc_printf("(8)navbuoys : %f, %f\n", Neb_ship_fog_vals_d3d[8][0], Neb_ship_fog_vals_d3d[8][1]);
1622                 dc_printf("(9)sentry guns : %f, %f\n", Neb_ship_fog_vals_d3d[9][0], Neb_ship_fog_vals_d3d[9][1]);
1623                 dc_printf("(10)escape pods : %f, %f\n", Neb_ship_fog_vals_d3d[10][0], Neb_ship_fog_vals_d3d[10][1]);
1624                 dc_printf("(11)background polys : %f, %f\n\n", Neb_backg_fog_near, Neb_backg_fog_far);          
1625         }
1626         dc_printf("neb2_max_alpha   : %f\n", Nd->max_alpha_glide);
1627         dc_printf("neb2_break_alpha : %f\n", Nd->break_alpha);
1628         dc_printf("neb2_break_off   : %d\n", (int)Nd->break_y);
1629         dc_printf("neb2_smooth      : %d\n", wacky_scheme);
1630         dc_printf("neb2_toggle      : %s\n", Neb2_render_mode ? "on" : "off");
1631         dc_printf("neb2_rot                      : %f\n", max_rotation);
1632         dc_printf("neb2_prad                     : %f\n", Nd->prad);
1633         dc_printf("neb2_cdim                     : %f\n", Nd->cube_dim);
1634         dc_printf("neb2_couter      : %f\n", Nd->cube_outer);
1635         dc_printf("neb2_cinner           : %f\n", Nd->cube_inner);
1636         dc_printf("neb2_jitter           : %f\n", Nd->wj);
1637         dc_printf("neb2_ff                       : %f\n", neb2_flash_fade);
1638         dc_printf("neb2_background       : %d %d %d\n", Neb2_background_color[0], Neb2_background_color[1], Neb2_background_color[2]);
1639 }
1640
1641 /* Obsolete !?
1642 DCF(neb2_create, "create a basic nebula")
1643 {
1644         int points = 0;
1645         float rad1 = 0.0f;
1646         float rad2 = 0.0f;
1647         
1648         dc_get_arg(ARG_INT);
1649         if(Dc_arg_type & ARG_INT){
1650                 points = Dc_arg_int;
1651         }
1652         dc_get_arg(ARG_FLOAT);
1653         if(Dc_arg_type & ARG_FLOAT){
1654                 rad1 = Dc_arg_float;
1655         }
1656         dc_get_arg(ARG_FLOAT);
1657         if(Dc_arg_type & ARG_FLOAT){
1658                 rad2 = Dc_arg_float;
1659         }
1660         neb2_create(&vmd_zero_vector, points, rad1, -1.0f, rad2);
1661 }
1662
1663 DCF(neb2_del, "delete existing nebulae")
1664 {
1665         for(int idx=0; idx<MAX_OBJECTS; idx++){
1666                 if(Objects[idx].type == OBJ_NEB2){
1667                         obj_delete(idx);                        
1668                 }
1669         }
1670 }
1671
1672 int magic0 = 15;
1673 float magic1 = 1000.0f;
1674 float magic2 = -1.0f;
1675 float magic3 = 700.0f;
1676 DCF(neb2_def, "create a default nebula")
1677 {
1678         vector a,b,c,d,e,f;
1679         vm_vec_make(&a, 0.0f, 0.0f, 0.0f);
1680         vm_vec_make(&b, 3600.0f, 700.0f, 0.0f);
1681         vm_vec_make(&c, -3000.0f, 20.0f, 480.0f);
1682         vm_vec_make(&d, -4000.0f, 100.0f, 100.0f);
1683         vm_vec_make(&e, 0.0f, 3000.0f, -400.0f);
1684         vm_vec_make(&f, 670.0f, -2500.0f, -1600.0f);
1685
1686         neb2_create(&a, magic0, magic1, magic2, magic3);
1687         neb2_create(&b, magic0, magic1, magic2, magic3);
1688         neb2_create(&c, magic0, magic1, magic2, magic3);
1689         neb2_create(&d, magic0, magic1, magic2, magic3);
1690         neb2_create(&e, magic0, magic1, magic2, magic3);
1691         neb2_create(&f, magic0, magic1, magic2, magic3);
1692 }
1693
1694 DCF(neb2_plr, "regenerate the player's nebula")
1695 {
1696         Neb2_regen = 0;
1697 }
1698
1699 DCF(neb2_stats, "display info about the nebula rendering")
1700 {
1701         dc_printf("Player poofs tried : %d\n", pneb_tried);
1702         dc_printf("Player poofs tossed (alpha): %d\n", pneb_tossed_alpha);
1703         dc_printf("Player poofs tossed (dot): %d\n", pneb_tossed_dot);
1704         dc_printf("Player poofs tossed (off): %d\n", pneb_tossed_off);
1705
1706         dc_printf("Poofs tried : %d\n", neb_tried);
1707         dc_printf("Poofs tossed (alpha): %d\n", neb_tossed_alpha);
1708         dc_printf("Poofs tossed (dot): %d\n", neb_tossed_dot);
1709         dc_printf("Poofs tossed (count): %d\n", neb_tossed_count);
1710
1711         dc_printf("Avg poofs/frame: %f\n", frame_avg);
1712 }
1713
1714 // create a nebula object, return objnum of the nebula or -1 on fail
1715 // NOTE : in most cases you will want to pass -1.0f for outer_radius. Trust me on this
1716 int neb2_create(vector *offset, int num_poofs, float inner_radius, float outer_radius, float max_poof_radius)
1717 {       
1718         Int3();
1719         return -1;
1720 }
1721
1722 // delete a nebula object
1723 void neb2_delete(object *objp)
1724 {       
1725         Int3();
1726 }
1727
1728 // renders a nebula object
1729 void neb2_render(object *objp)
1730 {       
1731         Int3();
1732 }
1733
1734 // preprocess the nebula object before simulation
1735 void neb2_process_pre(object *objp)
1736 {
1737         Int3();
1738 }
1739
1740 // process the nebula object after simulating, but before rendering
1741 void neb2_process_post(object *objp)
1742 {       
1743         Int3();
1744 }
1745 */
1746
1747 /*
1748 // add N poofs to the inner shell of the nebula
1749 // if orient and ang are specified, generate the poofs so that they are "visible" around
1750 // the orient fvec in a cone of ang degrees
1751 void neb2_add_inner(neb2 *neb, int num_poofs, matrix *orient, float ang)
1752 {       
1753         int idx;
1754         vector pt, pt2, pt3;
1755         int final_index = (neb->num_poofs + num_poofs) > neb->max_poofs ? neb->max_poofs : (neb->num_poofs + num_poofs);
1756
1757         // add the points a pick a random bitmap
1758         for(idx=neb->num_poofs; idx<final_index; idx++){
1759                 if(orient != NULL){
1760                         // put a point directly in front of the player, between 0 and inner_radius distance away
1761                         vm_vec_copy_scale(&pt, &orient->fvec, frand_range(neb->magic_num, neb->inner_radius));
1762
1763                         // rotate the point by -ang <-> ang around the up vector
1764                         vm_rot_point_around_line(&pt2, &pt, fl_radian(frand_range(-ang, ang)), &vmd_zero_vector, &orient->uvec);
1765
1766                         // rotate the point by -ang <-> ang around the right vector
1767                         vm_rot_point_around_line(&pt3, &pt2, fl_radian(frand_range(-ang, ang)), &vmd_zero_vector, &orient->rvec);
1768
1769                         // now add in the center of the nebula so its placed properly (ie, not around the origin)
1770                         vm_vec_add(&neb->pts[idx], &pt3, &Objects[neb->objnum].pos);
1771                 } else {
1772                         neb->pts[idx].x = frand_range(-1.0f * neb->inner_radius, neb->inner_radius) + Objects[neb->objnum].pos.x;
1773                         neb->pts[idx].y = frand_range(-1.0f * neb->inner_radius, neb->inner_radius) + Objects[neb->objnum].pos.y;
1774                         neb->pts[idx].z = frand_range(-1.0f * neb->inner_radius, neb->inner_radius) + Objects[neb->objnum].pos.z;
1775                 }
1776
1777                 neb->bmaps[idx] = (int)frand_range(0.0f, (float)2);
1778                 neb->num_poofs++;
1779         }
1780 }
1781
1782 // add N poofs to the outer shell of the nebula
1783 // if orient and ang are specified, generate the poofs so that they are "visible" around
1784 // the orient fvec in a cone of ang degrees
1785 void neb2_add_outer(neb2 *neb, int num_poofs, matrix *orient, float ang)
1786 {
1787         int idx;
1788         float phi, theta;
1789         vector pt, pt2, pt3;
1790         int final_index = (neb->num_poofs + num_poofs) > neb->max_poofs ? neb->max_poofs : (neb->num_poofs + num_poofs);
1791
1792         // add the points a pick a random bitmap
1793         for(idx=neb->num_poofs; idx<final_index; idx++){
1794                 if(orient != NULL){
1795                         // put a point directly in front of the player, at outer_radius distance away
1796                         vm_vec_copy_scale(&pt, &orient->fvec, neb->outer_radius);
1797
1798                         // rotate the point by -ang <-> ang around the up vector
1799                         vm_rot_point_around_line(&pt2, &pt, fl_radian(frand_range(-ang, ang)), &vmd_zero_vector, &orient->uvec);
1800
1801                         // rotate the point by -ang <-> ang around the right vector
1802                         vm_rot_point_around_line(&pt3, &pt2, fl_radian(frand_range(-ang, ang)), &vmd_zero_vector, &orient->rvec);
1803
1804                         // now add in the center of the nebula so its placed properly (ie, not around the origin)
1805                         vm_vec_add(&neb->pts[idx], &pt, &Objects[neb->objnum].pos);
1806                 } else {
1807                         // get a random point on the very outer radius, using spherical coords
1808                         phi = fl_radian(frand_range(0.0f, 360.0f));
1809                         theta = fl_radian(frand_range(0.0f, 360.f));
1810         
1811                         neb->pts[idx].x = neb->outer_radius * (float)sin(phi) * (float)cos(theta);
1812                         neb->pts[idx].y = neb->outer_radius * (float)sin(phi) * (float)sin(theta);
1813                         neb->pts[idx].z = neb->outer_radius * (float)cos(phi);                  
1814                 }
1815
1816                 // pick a random bitmap and increment the # of poofs
1817                 neb->bmaps[idx] = (int)frand_range(0.0f, (float)2);
1818                 neb->num_poofs++;
1819         }
1820 }
1821
1822 // return the alpha the passed poof should be rendered with, for a 1 shell nebula
1823 float neb2_get_alpha_1shell(neb2 *neb, int poof_index)
1824 {
1825         float dist;
1826         float alpha;
1827         vector eye_pos;
1828
1829         // get the eye position
1830         neb2_get_eye_pos(&eye_pos);
1831         
1832         // determine what alpha to draw this bitmap with
1833         // higher alpha the closer the bitmap gets to the eye
1834         dist = vm_vec_dist_quick(&eye_pos, &neb->pts[poof_index]);
1835         
1836         // at poof radius or greater, alpha should be 1.0
1837         // scale from 0.0 to 1.0 between radius and magic
1838         if(dist >= neb->max_poof_radius){
1839                 return max_alpha - 0.0001f;
1840         } else if(dist > neb->magic_num){
1841                 // alpha per meter between the magic # and the max radius
1842                 alpha = max_alpha / (neb->max_poof_radius - neb->magic_num);
1843
1844                 // above value times the # of meters away we are
1845                 return alpha * (dist - neb->magic_num);
1846         }       
1847         
1848         // otherwise transparent
1849         return 0.0f;
1850 }
1851 */