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