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