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