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