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