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