@@ -177,6 +177,10 @@ int Num_debris_nebula = 0;
177177bool Dynamic_environment = false ;
178178bool Motion_debris_override = false ;
179179
180+ static int Default_env_map = -1 ;
181+ static int Mission_env_map = -1 ;
182+ static bool Env_cubemap_drawn = false ;
183+
180184void stars_release_debris_vclips (debris_vclip *vclips)
181185{
182186 int i;
@@ -702,6 +706,10 @@ void stars_init()
702706 parse_startbl (" stars.tbl" );
703707
704708 parse_modular_table (" *-str.tbm" , parse_startbl);
709+
710+ if (Cmdline_env) {
711+ ENVMAP = Default_env_map = bm_load (" cubemap" );
712+ }
705713}
706714
707715// call only from game_shutdown()!!
@@ -778,6 +786,41 @@ void stars_pre_level_init(bool clear_backgrounds)
778786
779787 Dynamic_environment = false ;
780788 Motion_debris_override = false ;
789+
790+ Env_cubemap_drawn = false ;
791+ }
792+
793+ // setup the render target ready for this mission's environment map
794+ static void environment_map_gen ()
795+ {
796+ const int size = 512 ;
797+ int gen_flags = (BMP_FLAG_RENDER_TARGET_STATIC | BMP_FLAG_CUBEMAP | BMP_FLAG_RENDER_TARGET_MIPMAP);
798+
799+ if ( !Cmdline_env ) {
800+ return ;
801+ }
802+
803+ if (gr_screen.envmap_render_target >= 0 ) {
804+ if ( !bm_release (gr_screen.envmap_render_target , 1 ) ) {
805+ Warning (LOCATION, " Unable to release environment map render target." );
806+ }
807+
808+ gr_screen.envmap_render_target = -1 ;
809+ }
810+
811+ if ( Dynamic_environment || (The_mission.flags [Mission::Mission_Flags::Subspace]) ) {
812+ Dynamic_environment = true ;
813+ gen_flags &= ~BMP_FLAG_RENDER_TARGET_STATIC;
814+ gen_flags |= BMP_FLAG_RENDER_TARGET_DYNAMIC;
815+ }
816+ // bail if we are going to be static, and have an envmap specified already
817+ else if ( strlen (The_mission.envmap_name ) ) {
818+ // Load the mission map so we can use it later
819+ Mission_env_map = bm_load (The_mission.envmap_name );
820+ return ;
821+ }
822+
823+ gr_screen.envmap_render_target = bm_make_render_target (size, size, gen_flags);
781824}
782825
783826// call this in game_post_level_init() so we know whether we're running in full nebula mode or not
@@ -853,6 +896,23 @@ void stars_post_level_init()
853896 }
854897
855898 starfield_generate_bitmap_buffers ();
899+
900+ environment_map_gen ();
901+ }
902+
903+ void stars_level_close () {
904+ if (gr_screen.envmap_render_target >= 0 ) {
905+ if ( bm_release (gr_screen.envmap_render_target , 1 ) ) {
906+ gr_screen.envmap_render_target = -1 ;
907+ }
908+ }
909+
910+ if (Mission_env_map >= 0 ) {
911+ bm_release (Mission_env_map);
912+ Mission_env_map = -1 ;
913+ }
914+
915+ ENVMAP = Default_env_map;
856916}
857917
858918
@@ -2092,6 +2152,9 @@ void stars_set_background_model(const char *model_name, const char *texture_name
20922152 Nmodel_instance_num = model_create_instance (false , Nmodel_num);
20932153 }
20942154 }
2155+
2156+ // Since we have a new skybox we need to rerender the environment map
2157+ stars_invalidate_environment_map ();
20952158}
20962159
20972160// call this to set a specific orientation for the background
@@ -2216,6 +2279,9 @@ int stars_add_sun_entry(starfield_list_entry *sun_ptr)
22162279 }
22172280 }
22182281
2282+ // The background changed so we need to invalidate the environment map
2283+ stars_invalidate_environment_map ();
2284+
22192285 // now check if we can make use of a previously discarded instance entry
22202286 // this should never happen with FRED
22212287 if ( !Fred_running ) {
@@ -2275,6 +2341,9 @@ int stars_add_bitmap_entry(starfield_list_entry *sle)
22752341 }
22762342 }
22772343
2344+ // The background changed so we need to invalidate the environment map
2345+ stars_invalidate_environment_map ();
2346+
22782347 // now check if we can make use of a previously discarded instance entry
22792348 for (int i = 0 ; i < (int )Starfield_bitmap_instances.size (); i++) {
22802349 if ( Starfield_bitmap_instances[i].star_bitmap_index < 0 ) {
@@ -2379,6 +2448,9 @@ void stars_mark_instance_unused(int index, bool is_a_sun)
23792448 delete [] Starfield_bitmap_instances[index].verts ;
23802449 Starfield_bitmap_instances[index].verts = NULL ;
23812450 }
2451+
2452+ // The background changed so we need to invalidate the environment map
2453+ stars_invalidate_environment_map ();
23822454}
23832455
23842456// retrieves the name from starfield_bitmap for the instance index
@@ -2427,6 +2499,8 @@ void stars_set_nebula(bool activate)
24272499 Debris_vclips = Debris_vclips_normal;
24282500 HUD_contrast = 0 ;
24292501 }
2502+ // We need to reload the environment map now
2503+ stars_invalidate_environment_map ();
24302504}
24312505
24322506// retrieves the name from starfield_bitmap, really only used by FRED2
@@ -2663,3 +2737,149 @@ void stars_pack_backgrounds()
26632737 }
26642738 }
26652739}
2740+
2741+ static void render_environment (int i, vec3d *eye_pos, matrix *new_orient, float new_zoom)
2742+ {
2743+ bm_set_render_target (gr_screen.envmap_render_target , i);
2744+
2745+ gr_clear ();
2746+
2747+ g3_set_view_matrix ( eye_pos, new_orient, new_zoom );
2748+
2749+ gr_set_proj_matrix ( PI_2 * new_zoom, 1 .0f , Min_draw_distance, Max_draw_distance);
2750+ gr_set_view_matrix ( &Eye_position, &Eye_matrix );
2751+
2752+ if ( Game_subspace_effect ) {
2753+ stars_draw (0 , 0 , 0 , 1 , 1 );
2754+ } else {
2755+ stars_draw (0 , 1 , 1 , 0 , 1 );
2756+ }
2757+
2758+ gr_end_view_matrix ();
2759+ gr_end_proj_matrix ();
2760+ }
2761+
2762+ void stars_setup_environment_mapping (camid cid) {
2763+ matrix new_orient = IDENTITY_MATRIX;
2764+
2765+ extern float View_zoom;
2766+ float old_zoom = View_zoom, new_zoom = 1 .0f ;// 0.925f;
2767+ int i = 0 ;
2768+
2769+ if (!cid.isValid ())
2770+ return ;
2771+
2772+ vec3d cam_pos;
2773+ matrix cam_orient;
2774+ cid.getCamera ()->get_info (&cam_pos, &cam_orient);
2775+
2776+ // prefer the mission specified envmap over the static-generated envmap, but
2777+ // the dynamic envmap should always get preference if in a subspace mission
2778+ if ( !Dynamic_environment && Mission_env_map >= 0 ) {
2779+ ENVMAP = Mission_env_map;
2780+ return ;
2781+ }
2782+
2783+ if (gr_screen.envmap_render_target < 0 ) {
2784+ if (ENVMAP >= 0 )
2785+ return ;
2786+
2787+ if (Mission_env_map >= 0 ) {
2788+ ENVMAP = Mission_env_map;
2789+ } else {
2790+ ENVMAP = Default_env_map;
2791+ }
2792+
2793+ return ;
2794+ }
2795+
2796+ if (Env_cubemap_drawn) {
2797+ // Nothing to do here anymore
2798+ return ;
2799+ }
2800+
2801+ GR_DEBUG_SCOPE (" Environment Mapping" );
2802+ TRACE_SCOPE (tracing::EnvironmentMapping);
2803+
2804+ ENVMAP = gr_screen.envmap_render_target ;
2805+
2806+ /*
2807+ * Envmap matrix setup -- left-handed
2808+ * -------------------------------------------------
2809+ * Face -- Forward Up Right
2810+ * px +X +Y -Z
2811+ * nx -X +Y +Z
2812+ * py +Y -Z +X
2813+ * ny -Y +Z +X
2814+ * pz +Z +Y +X
2815+ * nz -Z +Y -X
2816+ */
2817+ // NOTE: OpenGL needs up/down reversed
2818+
2819+ // Save the previous render target so we can reset it once we are done here
2820+ auto previous_target = gr_screen.rendering_to_texture ;
2821+
2822+ // face 1 (px / right)
2823+ memset ( &new_orient, 0 , sizeof (matrix) );
2824+ new_orient.vec .fvec .xyz .x = 1 .0f ;
2825+ new_orient.vec .uvec .xyz .y = 1 .0f ;
2826+ new_orient.vec .rvec .xyz .z = -1 .0f ;
2827+ render_environment (i, &cam_pos, &new_orient, new_zoom);
2828+ i++; // bump!
2829+
2830+ // face 2 (nx / left)
2831+ memset ( &new_orient, 0 , sizeof (matrix) );
2832+ new_orient.vec .fvec .xyz .x = -1 .0f ;
2833+ new_orient.vec .uvec .xyz .y = 1 .0f ;
2834+ new_orient.vec .rvec .xyz .z = 1 .0f ;
2835+ render_environment (i, &cam_pos, &new_orient, new_zoom);
2836+ i++; // bump!
2837+
2838+ // face 3 (py / up)
2839+ memset ( &new_orient, 0 , sizeof (matrix) );
2840+ new_orient.vec .fvec .xyz .y = (gr_screen.mode == GR_OPENGL) ? 1 .0f : -1 .0f ;
2841+ new_orient.vec .uvec .xyz .z = (gr_screen.mode == GR_OPENGL) ? -1 .0f : 1 .0f ;
2842+ new_orient.vec .rvec .xyz .x = 1 .0f ;
2843+ render_environment (i, &cam_pos, &new_orient, new_zoom);
2844+ i++; // bump!
2845+
2846+ // face 4 (ny / down)
2847+ memset ( &new_orient, 0 , sizeof (matrix) );
2848+ new_orient.vec .fvec .xyz .y = (gr_screen.mode == GR_OPENGL) ? -1 .0f : 1 .0f ;
2849+ new_orient.vec .uvec .xyz .z = (gr_screen.mode == GR_OPENGL) ? 1 .0f : -1 .0f ;
2850+ new_orient.vec .rvec .xyz .x = 1 .0f ;
2851+ render_environment (i, &cam_pos, &new_orient, new_zoom);
2852+ i++; // bump!
2853+
2854+ // face 5 (pz / forward)
2855+ memset ( &new_orient, 0 , sizeof (matrix) );
2856+ new_orient.vec .fvec .xyz .z = 1 .0f ;
2857+ new_orient.vec .uvec .xyz .y = 1 .0f ;
2858+ new_orient.vec .rvec .xyz .x = 1 .0f ;
2859+ render_environment (i, &cam_pos, &new_orient, new_zoom);
2860+ i++; // bump!
2861+
2862+ // face 6 (nz / back)
2863+ memset ( &new_orient, 0 , sizeof (matrix) );
2864+ new_orient.vec .fvec .xyz .z = -1 .0f ;
2865+ new_orient.vec .uvec .xyz .y = 1 .0f ;
2866+ new_orient.vec .rvec .xyz .x = -1 .0f ;
2867+ render_environment (i, &cam_pos, &new_orient, new_zoom);
2868+
2869+
2870+ // we're done, so now reset
2871+ bm_set_render_target (previous_target);
2872+ g3_set_view_matrix ( &cam_pos, &cam_orient, old_zoom );
2873+
2874+ if ( !Dynamic_environment ) {
2875+ Env_cubemap_drawn = true ;
2876+ }
2877+ }
2878+ void stars_set_dynamic_environment (bool dynamic) {
2879+ Dynamic_environment = dynamic;
2880+ stars_invalidate_environment_map ();
2881+ }
2882+ void stars_invalidate_environment_map () {
2883+ // This will cause a redraw in the next frame
2884+ Env_cubemap_drawn = false ;
2885+ }
0 commit comments