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