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