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