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