From b38e2375ab6c8b302542eba33577da2a979ddbe3 Mon Sep 17 00:00:00 2001 From: Dustinspace217 Date: Tue, 13 Oct 2026 02:13:54 -0700 Subject: [PATCH 1/3] Add the FS1 nebula backgrounds game_settings.tbl switch (default off) --- code/mod_table/mod_table.cpp | 9 +++++++++ code/mod_table/mod_table.h | 1 + 2 files changed, 10 insertions(+) diff --git a/code/mod_table/mod_table.cpp b/code/mod_table/mod_table.cpp index 2ca750af08c..73ed0b83a7c 100644 --- a/code/mod_table/mod_table.cpp +++ b/code/mod_table/mod_table.cpp @@ -29,6 +29,7 @@ int Directive_wait_time; bool True_loop_argument_sexps; bool Skybox_internal_depth_consistency; +bool Fs1_nebula_backgrounds; bool Fixed_turret_collisions; bool Fixed_missile_detonation; bool Damage_impacted_subsystem_first; @@ -979,6 +980,10 @@ void parse_mod_table(const char *filename) stuff_boolean(&Render_insignias_as_decals); } + if (optional_string("$FS1 nebula backgrounds:")) { + stuff_boolean(&Fs1_nebula_backgrounds); + } + optional_string("#NETWORK SETTINGS"); if (optional_string("$FS2NetD port:")) { @@ -1677,6 +1682,9 @@ static void mod_table_log_settings() else mprintf(("Game Settings Table: Skybox submodels will not use internal depth consistency (retail behavior)\n")); + if (Fs1_nebula_backgrounds) + mprintf(("Game Settings Table: FS1 nebula backgrounds will be drawn for missions that name one\n")); + if (Fixed_turret_collisions) mprintf(("Game Settings Table: Using fixed turret collisions (shooting a turret barrel will always register)\n")); else @@ -1865,6 +1873,7 @@ void mod_table_reset() Directive_wait_time = 3000; True_loop_argument_sexps = false; Skybox_internal_depth_consistency = false; + Fs1_nebula_backgrounds = false; Fixed_turret_collisions = false; Fixed_missile_detonation = false; Damage_impacted_subsystem_first = false; diff --git a/code/mod_table/mod_table.h b/code/mod_table/mod_table.h index d502f96098b..2bcc8b3f47b 100644 --- a/code/mod_table/mod_table.h +++ b/code/mod_table/mod_table.h @@ -49,6 +49,7 @@ struct splash_screen { extern int Directive_wait_time; extern bool True_loop_argument_sexps; extern bool Skybox_internal_depth_consistency; +extern bool Fs1_nebula_backgrounds; extern bool Fixed_turret_collisions; extern bool Fixed_missile_detonation; extern bool Damage_impacted_subsystem_first; From e03c9f34cb6385bd7c31a5f80e3514e95cc7760a Mon Sep 17 00:00:00 2001 From: Dustinspace217 Date: Tue, 13 Oct 2026 02:49:44 -0700 Subject: [PATCH 2/3] Draw FS1 nebula backgrounds when the game_settings.tbl switch is on Restores the FS1 background nebula that the engine has parsed and loaded since retail but has not drawn since the palette manager was removed in 2017. The mesh is drawn on the far sphere before the stars, after the skybox, and only when a mission has no skybox model and is not a full nebula mission. The colour ramp is the first 20 entries of the FS1 game palette for the mission's nebula and colour (gamepalette-.pcx), uploaded as a one-row texture so the GPU interpolates between ramp entries. Missing palette or mesh files log a line and draw nothing. The mesh loader now rejects triangle indices outside the point list and closes the file on every failure path, since the renderer consumes the indices. qtFRED's background editor and FRED's nebula colour control reload the nebula when the fields change, so the editors preview it. --- code/starfield/nebula.cpp | 98 ++++++++++++++++++- code/starfield/nebula.h | 1 + code/starfield/starfield.cpp | 6 +- fred2/bgbitmapdlg.cpp | 1 + .../dialogs/BackgroundEditorDialogModel.cpp | 5 + 5 files changed, 108 insertions(+), 3 deletions(-) diff --git a/code/starfield/nebula.cpp b/code/starfield/nebula.cpp index c2718799fd8..42bacf4ef48 100644 --- a/code/starfield/nebula.cpp +++ b/code/starfield/nebula.cpp @@ -9,12 +9,16 @@ +#include "bmpman/bmpman.h" #include "cfile/cfile.h" #include "debugconsole/console.h" #include "math/vecmat.h" #include "mission/missionparse.h" +#include "mod_table/mod_table.h" #include "nebula/neb.h" #include "graphics/material.h" +#include "graphics/matrix.h" +#include "pcxutils/pcxutils.h" #include "render/3d.h" #include "starfield/nebula.h" @@ -32,6 +36,13 @@ static int num_tris = 0; static int tri[MAX_TRIS][3]; static int Nebula_loaded = 0; + +constexpr int NEBULA_RAMP_SIZE = 20; + +static int Nebula_ramp_bitmap = -1; +static ubyte Nebula_ramp_pixels[NEBULA_RAMP_SIZE * 4]; +// too large for the stack; the immediate primitive path copies it at call time +static vertex Nebula_tri_verts[MAX_TRIS * 3]; static angles Nebula_pbh; static matrix Nebula_orient; @@ -41,6 +52,11 @@ int Nebula_heading; void nebula_close() { + if (Nebula_ramp_bitmap >= 0) { + bm_release(Nebula_ramp_bitmap); + Nebula_ramp_bitmap = -1; + } + if (!Nebula_loaded) return; Nebula_loaded = 0; @@ -82,6 +98,7 @@ int load_nebula_sub(const char *filename) fp = cfopen(filename, "rb"); if ( !fp ) { + mprintf(( "Nebula file %s could not be opened.\n", filename )); return 0; } @@ -89,6 +106,7 @@ int load_nebula_sub(const char *filename) cfread( id, 4, 1, fp ); if ( strncmp( id, NEBULA_FILE_ID, 4) != 0) { mprintf(( "Not a valid nebula file.\n" )); + cfclose(fp); return 0; } cfread( &version, sizeof(int), 1, fp ); @@ -96,6 +114,7 @@ int load_nebula_sub(const char *filename) if ( major != NEBULA_MAJOR_VERSION ) { mprintf(( "An out of date nebula file.\n" )); + cfclose(fp); return 0; } @@ -105,8 +124,10 @@ int load_nebula_sub(const char *filename) Assert( num_tris < MAX_TRIS ); // Cyborg - can't load nonsense. - if (num_pts <= 0 || num_pts >= MAX_POINTS || num_tris <= 0 || num_tris >= MAX_TRIS) + if (num_pts <= 0 || num_pts >= MAX_POINTS || num_tris <= 0 || num_tris >= MAX_TRIS) { + cfclose(fp); return 0; + } int i; for (i=0; i= num_pts) { + mprintf(( "Nebula file %s has a triangle index out of range.\n", filename )); + cfclose(fp); + return 0; + } + } } cfclose(fp); @@ -134,6 +163,34 @@ int load_nebula_sub(const char *filename) return 1; } +// one-row texture of the FS1 palette's first NEBULA_RAMP_SIZE entries, so the GPU interpolates in ramp +// space rather than in RGB between vertex colours +static void nebula_load_ramp(int nebula_index, int palette_index) +{ + if (Nebula_ramp_bitmap >= 0) { + bm_release(Nebula_ramp_bitmap); + Nebula_ramp_bitmap = -1; + } + + char filename[MAX_FILENAME_LEN]; + sprintf_safe(filename, "gamepalette%d-%02d", nebula_index + 1, palette_index + 1); + + ubyte palette[768] = {}; + if (pcx_read_header(filename, nullptr, nullptr, nullptr, nullptr, palette) != PCX_ERROR_NONE) { + mprintf(( "FS1 nebula palette %s.pcx could not be read; the nebula will not be drawn.\n", filename )); + return; + } + + for (int i = 0; i < NEBULA_RAMP_SIZE; i++) { + Nebula_ramp_pixels[i * 4 + 0] = palette[i * 3 + 2]; + Nebula_ramp_pixels[i * 4 + 1] = palette[i * 3 + 1]; + Nebula_ramp_pixels[i * 4 + 2] = palette[i * 3 + 0]; + Nebula_ramp_pixels[i * 4 + 3] = 255; + } + + Nebula_ramp_bitmap = bm_create(32, NEBULA_RAMP_SIZE, 1, Nebula_ramp_pixels, 0); +} + void nebula_init( int index, int pitch, int bank, int heading ) { angles a; @@ -143,6 +200,10 @@ void nebula_init( int index, int pitch, int bank, int heading ) a.h = fl_radians(heading); nebula_init(index < 0 ? nullptr : Nebula_filenames[index], &a); + + if (index >= 0 && Nebula_loaded && Fs1_nebula_backgrounds) { + nebula_load_ramp(index, Mission_palette); + } } void nebula_init( const char *filename, angles * pbh ) @@ -167,6 +228,39 @@ void nebula_init( const char *filename, angles * pbh ) } } +void nebula_render() +{ + if (!Fs1_nebula_backgrounds || !Nebula_loaded || Nebula_ramp_bitmap < 0) { + return; + } + if (!Detail.planets_suns) { + return; + } + + // g3_rotate_faraway_vertex would give eye-space points, which this primitive path would transform again + gr_start_instance_matrix(&Eye_position, &Nebula_orient); + + for (int i = 0; i < num_pts; i++) { + nebula_verts[i].world = nebula_vecs[i]; + // map the brightness onto texel centres so the extremes land on the first and last ramp colours + nebula_verts[i].texture_position.u = + (0.5f + (NEBULA_RAMP_SIZE - 1) * (nebula_verts[i].b / 255.0f)) / NEBULA_RAMP_SIZE; + nebula_verts[i].texture_position.v = 0.5f; + } + + for (int i = 0; i < num_tris; i++) { + Nebula_tri_verts[i * 3 + 0] = nebula_verts[tri[i][0]]; + Nebula_tri_verts[i * 3 + 1] = nebula_verts[tri[i][1]]; + Nebula_tri_verts[i * 3 + 2] = nebula_verts[tri[i][2]]; + } + + material mat; + material_set_unlit(&mat, Nebula_ramp_bitmap, 1.0f, true, false); + g3_render_primitives_textured(&mat, Nebula_tri_verts, num_tris * 3, PRIM_TYPE_TRIS, false); + + gr_end_instance_matrix(); +} + DCF(nebula,"Loads a different nebula") { SCP_string filename; diff --git a/code/starfield/nebula.h b/code/starfield/nebula.h index 2d6f86add9d..31e30c72864 100644 --- a/code/starfield/nebula.h +++ b/code/starfield/nebula.h @@ -24,5 +24,6 @@ struct angles; void nebula_init( int index, int pitch, int bank, int heading ); void nebula_init( const char *filename, angles *pbh = /*NULL*/ 0 ); void nebula_close(); +void nebula_render(); #endif //_NEBULA_H diff --git a/code/starfield/starfield.cpp b/code/starfield/starfield.cpp index a90ede83880..a46fd386c18 100644 --- a/code/starfield/starfield.cpp +++ b/code/starfield/starfield.cpp @@ -1928,7 +1928,7 @@ void stars_draw_motion_debris() refresh_motion_debris = false; } -void stars_draw(int show_stars, int show_suns, int /*show_nebulas*/, int show_subspace, int env, bool in_mission) +void stars_draw(int show_stars, int show_suns, int show_nebulas, int show_subspace, int env, bool in_mission) { GR_DEBUG_SCOPE("Draw Stars"); TRACE_SCOPE(tracing::DrawStars); @@ -1966,6 +1966,10 @@ void stars_draw(int show_stars, int show_suns, int /*show_nebulas*/, int show_s stars_draw_background(); } + if ( show_nebulas && (Nmodel_num < 0) && (Game_detail_flags & DETAIL_FLAG_NEBULAS) && !(The_mission.flags[Mission::Mission_Flags::Fullneb]) ) { + nebula_render(); + } + if ( !env && show_stars && (Nmodel_num < 0) && (Game_detail_flags & DETAIL_FLAG_STARS) && !(The_mission.flags[Mission::Mission_Flags::Fullneb]) && (supernova_stage() < SUPERNOVA_STAGE::TOOLTIME) ) { stars_draw_stars(); } diff --git a/fred2/bgbitmapdlg.cpp b/fred2/bgbitmapdlg.cpp index 74e34e88bb1..7e6c593ce26 100644 --- a/fred2/bgbitmapdlg.cpp +++ b/fred2/bgbitmapdlg.cpp @@ -601,6 +601,7 @@ void bg_bitmap_dlg::OnSelchangeNebcolor() UpdateData(TRUE); Mission_palette = m_nebula_color; + nebula_init(Nebula_index, m_pitch, m_bank, m_heading); Update_window = 1; } diff --git a/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.cpp b/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.cpp index 5c1e47c175f..f1b87819103 100644 --- a/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.cpp +++ b/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.cpp @@ -1028,6 +1028,7 @@ void BackgroundEditorDialogModel::setOldNebulaPattern(const SCP_string& name) } modify(Nebula_index, newIndex); + nebula_init(Nebula_index, Nebula_pitch, Nebula_bank, Nebula_heading); } SCP_string BackgroundEditorDialogModel::getOldNebulaColorName() @@ -1045,6 +1046,7 @@ void BackgroundEditorDialogModel::setOldNebulaColorName(const SCP_string& name) for (int i = 0; i < NUM_NEBULA_COLORS; ++i) { if (!stricmp(Nebula_colors[i], name.c_str())) { modify(Mission_palette, i); + nebula_init(Nebula_index, Nebula_pitch, Nebula_bank, Nebula_heading); return; } } @@ -1062,6 +1064,7 @@ void BackgroundEditorDialogModel::setOldNebulaPitch(int deg) if (Nebula_pitch != deg) { Nebula_pitch = deg; modify(Nebula_pitch, deg); + nebula_init(Nebula_index, Nebula_pitch, Nebula_bank, Nebula_heading); } } @@ -1076,6 +1079,7 @@ void BackgroundEditorDialogModel::setOldNebulaBank(int deg) if (Nebula_bank != deg) { Nebula_bank = deg; modify(Nebula_bank, deg); + nebula_init(Nebula_index, Nebula_pitch, Nebula_bank, Nebula_heading); } } @@ -1090,6 +1094,7 @@ void BackgroundEditorDialogModel::setOldNebulaHeading(int deg) if (Nebula_heading != deg) { Nebula_heading = deg; modify(Nebula_heading, deg); + nebula_init(Nebula_index, Nebula_pitch, Nebula_bank, Nebula_heading); } } From 467804e2ec030baca539f8b8258dae8893d92999 Mon Sep 17 00:00:00 2001 From: Dustinspace217 Date: Tue, 13 Oct 2026 07:46:07 -0700 Subject: [PATCH 3/3] Let a mission draw its FS1 nebula over a skybox model Adds an optional `$FS1 Nebula Over Skybox: YES` line at the end of a mission's background section. When it is set and the mission has a skybox model, the FS1 nebula is drawn after the skybox with additive blending, so its dark areas leave the skybox and its stars untouched. Without the line a skybox still hides the nebula, as before. The line sits after `$Environment Map:` so builds that do not know it skip it with the rest of the section; it is written with a version comment. qtFRED's background editor and FRED's background dialog get a "Draw over skybox" checkbox for it, and the Lab reads and resets the flag. --- code/lab/renderer/lab_renderer.cpp | 5 +++++ code/mission/missionparse.cpp | 5 +++++ code/missioneditor/missionsave.cpp | 11 +++++++++++ code/starfield/nebula.cpp | 7 ++++++- code/starfield/nebula.h | 4 +++- code/starfield/starfield.cpp | 4 ++-- fred2/bgbitmapdlg.cpp | 8 ++++++++ fred2/bgbitmapdlg.h | 1 + fred2/fred.rc | 1 + fred2/resource.h | 3 ++- .../mission/dialogs/BackgroundEditorDialogModel.cpp | 10 ++++++++++ .../src/mission/dialogs/BackgroundEditorDialogModel.h | 2 ++ qtfred/src/ui/dialogs/BackgroundEditorDialog.cpp | 7 +++++++ qtfred/src/ui/dialogs/BackgroundEditorDialog.h | 1 + qtfred/ui/BackgroundEditor.ui | 11 +++++++++++ 15 files changed, 75 insertions(+), 5 deletions(-) diff --git a/code/lab/renderer/lab_renderer.cpp b/code/lab/renderer/lab_renderer.cpp index eec2c2d1813..df95ad37889 100644 --- a/code/lab/renderer/lab_renderer.cpp +++ b/code/lab/renderer/lab_renderer.cpp @@ -387,6 +387,7 @@ void LabRenderer::useBackground(const SCP_string& mission_name) { currentMissionBackground = mission_name; stars_pre_level_init(true); + Nebula_over_skybox = false; vm_set_identity(&skybox_orientation); // (DahBlount) - Remember to load the debris anims @@ -584,6 +585,10 @@ void LabRenderer::useBackground(const SCP_string& mission_name) { stuff_string(envmap_name, F_NAME, MAX_FILENAME_LEN); } + if (optional_string("$FS1 Nebula Over Skybox:")) { + stuff_boolean(&Nebula_over_skybox); + } + const int size = 512; int gen_flags = (BMP_FLAG_RENDER_TARGET_STATIC | BMP_FLAG_CUBEMAP | BMP_FLAG_RENDER_TARGET_MIPMAP); diff --git a/code/mission/missionparse.cpp b/code/mission/missionparse.cpp index cccd93941ea..b883c87f553 100644 --- a/code/mission/missionparse.cpp +++ b/code/mission/missionparse.cpp @@ -6468,6 +6468,10 @@ void parse_bitmaps(mission *pm) stuff_string(pm->envmap_name, F_NAME, MAX_FILENAME_LEN); } + if (optional_string("$FS1 Nebula Over Skybox:")) { + stuff_boolean(&Nebula_over_skybox); + } + // bypass spurious stuff from e.g. FS1 missions skip_to_start_of_string("#"); } @@ -7501,6 +7505,7 @@ void mission_init(mission *pm, bool quick_init) Nebula_bank = (int)((float)(Random::next() & 0x0fff) * 360.0f / 4096.0f); Nebula_heading = (int)((float)(Random::next() & 0x0fff) * 360.0f / 4096.0f); Nebula_index = -1; + Nebula_over_skybox = false; Mission_palette = 1; } diff --git a/code/missioneditor/missionsave.cpp b/code/missioneditor/missionsave.cpp index 6f57eba347f..68ef82518de 100644 --- a/code/missioneditor/missionsave.cpp +++ b/code/missioneditor/missionsave.cpp @@ -1089,6 +1089,17 @@ int Fred_mission_save::save_bitmaps() bypass_comment(";;FSO 3.6.9;; $Environment Map:"); } + if (Nebula_over_skybox && Nebula_index >= 0 && !The_mission.flags[Mission::Mission_Flags::Fullneb]) { + fso_comment_push(";;FSO 26.1.0;;"); + if (optional_string_fred("$FS1 Nebula Over Skybox:")) { + parse_comments(2); + fout(" YES"); + } else { + fout_version("\n\n$FS1 Nebula Over Skybox: YES"); + } + fso_comment_pop(); + } + fso_comment_pop(true); return err; diff --git a/code/starfield/nebula.cpp b/code/starfield/nebula.cpp index 42bacf4ef48..b4611dda11d 100644 --- a/code/starfield/nebula.cpp +++ b/code/starfield/nebula.cpp @@ -49,6 +49,7 @@ static matrix Nebula_orient; int Nebula_pitch; int Nebula_bank; int Nebula_heading; +bool Nebula_over_skybox = false; void nebula_close() { @@ -228,7 +229,7 @@ void nebula_init( const char *filename, angles * pbh ) } } -void nebula_render() +void nebula_render(bool over_skybox) { if (!Fs1_nebula_backgrounds || !Nebula_loaded || Nebula_ramp_bitmap < 0) { return; @@ -256,6 +257,10 @@ void nebula_render() material mat; material_set_unlit(&mat, Nebula_ramp_bitmap, 1.0f, true, false); + if (over_skybox) { + // the ramp's dark end is black, so adding leaves the skybox and its stars where the nebula is faint + mat.set_blend_mode(ALPHA_BLEND_ADDITIVE); + } g3_render_primitives_textured(&mat, Nebula_tri_verts, num_tris * 3, PRIM_TYPE_TRIS, false); gr_end_instance_matrix(); diff --git a/code/starfield/nebula.h b/code/starfield/nebula.h index 31e30c72864..31daa360398 100644 --- a/code/starfield/nebula.h +++ b/code/starfield/nebula.h @@ -24,6 +24,8 @@ struct angles; void nebula_init( int index, int pitch, int bank, int heading ); void nebula_init( const char *filename, angles *pbh = /*NULL*/ 0 ); void nebula_close(); -void nebula_render(); +void nebula_render(bool over_skybox); + +extern bool Nebula_over_skybox; #endif //_NEBULA_H diff --git a/code/starfield/starfield.cpp b/code/starfield/starfield.cpp index a46fd386c18..e110bdc835d 100644 --- a/code/starfield/starfield.cpp +++ b/code/starfield/starfield.cpp @@ -1966,8 +1966,8 @@ void stars_draw(int show_stars, int show_suns, int show_nebulas, int show_subspa stars_draw_background(); } - if ( show_nebulas && (Nmodel_num < 0) && (Game_detail_flags & DETAIL_FLAG_NEBULAS) && !(The_mission.flags[Mission::Mission_Flags::Fullneb]) ) { - nebula_render(); + if ( show_nebulas && (Nmodel_num < 0 || Nebula_over_skybox) && (Game_detail_flags & DETAIL_FLAG_NEBULAS) && !(The_mission.flags[Mission::Mission_Flags::Fullneb]) ) { + nebula_render(Nmodel_num >= 0); } if ( !env && show_stars && (Nmodel_num < 0) && (Game_detail_flags & DETAIL_FLAG_STARS) && !(The_mission.flags[Mission::Mission_Flags::Fullneb]) && (supernova_stage() < SUPERNOVA_STAGE::TOOLTIME) ) { diff --git a/fred2/bgbitmapdlg.cpp b/fred2/bgbitmapdlg.cpp index 7e6c593ce26..a7b68f6d13b 100644 --- a/fred2/bgbitmapdlg.cpp +++ b/fred2/bgbitmapdlg.cpp @@ -50,6 +50,7 @@ bg_bitmap_dlg::bg_bitmap_dlg(CWnd* pParent) : CDialog(bg_bitmap_dlg::IDD, pParen m_pitch = 0; m_neb2_texture = 0; m_subspace = FALSE; + m_nebula_over_skybox = FALSE; m_fullneb = FALSE; m_fog_color_override = FALSE; m_fullneb_background_bitmaps = FALSE; @@ -101,6 +102,7 @@ void bg_bitmap_dlg::DoDataExchange(CDataExchange* pDX) DDX_Control(pDX, IDC_SLIDER1, m_slider); DDX_CBIndex(pDX, IDC_NEB2_TEXTURE, m_neb2_texture); DDX_Check(pDX, IDC_SUBSPACE, m_subspace); + DDX_Check(pDX, IDC_NEB_OVER_SKYBOX, m_nebula_over_skybox); DDX_Check(pDX, IDC_FULLNEB, m_fullneb); DDX_Check(pDX, IDC_NEB2_PALETTE_OVERRIDE, m_fog_color_override); DDX_Check(pDX, IDC_NEB2_BACKGROUND_BITMAPS, m_fullneb_background_bitmaps); @@ -339,8 +341,10 @@ void bg_bitmap_dlg::create() m_nebula_index = Nebula_index + 1; m_nebula_color = Mission_palette; + m_nebula_over_skybox = Nebula_over_skybox ? 1 : 0; if (Nebula_index < 0){ GetDlgItem(IDC_NEBCOLOR)->EnableWindow(FALSE); + GetDlgItem(IDC_NEB_OVER_SKYBOX)->EnableWindow(FALSE); } m_pitch = Nebula_pitch; @@ -506,6 +510,7 @@ void bg_bitmap_dlg::OnClose() Nebula_pitch = m_pitch; Nebula_bank = m_bank; Nebula_heading = m_heading; + Nebula_over_skybox = m_nebula_over_skybox != 0; nebula_init(Nebula_index, m_pitch, m_bank, m_heading); The_mission.flags.set(Mission::Mission_Flags::Subspace, m_subspace != 0); @@ -616,6 +621,7 @@ void bg_bitmap_dlg::OnSelchangeNebpattern() Nebula_index = m_nebula_index - 1; GetDlgItem(IDC_NEBCOLOR)->EnableWindow(m_nebula_index ? TRUE : FALSE); + GetDlgItem(IDC_NEB_OVER_SKYBOX)->EnableWindow(m_nebula_index ? TRUE : FALSE); nebula_init(Nebula_index, m_pitch, m_bank, m_heading); Update_window = 1; @@ -679,6 +685,7 @@ void bg_bitmap_dlg::OnFullNeb() // disable non-fullneb controls GetDlgItem(IDC_NEBPATTERN)->EnableWindow(FALSE); GetDlgItem(IDC_NEBCOLOR)->EnableWindow(FALSE); + GetDlgItem(IDC_NEB_OVER_SKYBOX)->EnableWindow(FALSE); GetDlgItem(IDC_PITCH)->EnableWindow(FALSE); GetDlgItem(IDC_BANK)->EnableWindow(FALSE); GetDlgItem(IDC_HEADING)->EnableWindow(FALSE); @@ -686,6 +693,7 @@ void bg_bitmap_dlg::OnFullNeb() // enable all non-fullneb controls GetDlgItem(IDC_NEBPATTERN)->EnableWindow(TRUE); GetDlgItem(IDC_NEBCOLOR)->EnableWindow(TRUE); + GetDlgItem(IDC_NEB_OVER_SKYBOX)->EnableWindow(TRUE); GetDlgItem(IDC_PITCH)->EnableWindow(TRUE); GetDlgItem(IDC_BANK)->EnableWindow(TRUE); GetDlgItem(IDC_HEADING)->EnableWindow(TRUE); diff --git a/fred2/bgbitmapdlg.h b/fred2/bgbitmapdlg.h index 96c8d61b8f7..72d0a55ed24 100644 --- a/fred2/bgbitmapdlg.h +++ b/fred2/bgbitmapdlg.h @@ -56,6 +56,7 @@ class bg_bitmap_dlg : public CDialog CSliderCtrl m_slider; int m_neb2_texture; BOOL m_subspace; + BOOL m_nebula_over_skybox; BOOL m_fullneb; BOOL m_fog_color_override; BOOL m_fullneb_background_bitmaps; diff --git a/fred2/fred.rc b/fred2/fred.rc index d69261b8939..f6e66a88629 100644 --- a/fred2/fred.rc +++ b/fred2/fred.rc @@ -1813,6 +1813,7 @@ BEGIN CONTROL "Toggle ship trails",IDC_NEB2_TOGGLE_TRAILS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,250,65,10 COMBOBOX IDC_NEBPATTERN,42,405,61,108,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP COMBOBOX IDC_NEBCOLOR,42,421,61,108,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Draw over skybox",IDC_NEB_OVER_SKYBOX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,437,90,10 EDITTEXT IDC_PITCH,166,388,40,14,ES_AUTOHSCROLL | ES_NUMBER EDITTEXT IDC_BANK,166,405,40,14,ES_AUTOHSCROLL | ES_NUMBER EDITTEXT IDC_HEADING,166,422,40,14,ES_AUTOHSCROLL | ES_NUMBER diff --git a/fred2/resource.h b/fred2/resource.h index f03b894bf21..ff8aa446e5a 100644 --- a/fred2/resource.h +++ b/fred2/resource.h @@ -563,6 +563,7 @@ #define IDC_SET_FROM_SHIP_CLASS 1203 #define IDC_CURRENT_CONTAINER_NAME 1203 #define IDC_NEBCOLOR 1204 +#define IDC_NEB_OVER_SKYBOX 1747 #define IDC_SUBSTITUTE_EVENT_MUSIC 1205 #define IDC_OPEN_CUSTOM_DATA 1206 #define IDC_AI_PROFILE 1207 @@ -1638,7 +1639,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_3D_CONTROLS 1 #define _APS_NEXT_RESOURCE_VALUE 340 -#define _APS_NEXT_CONTROL_VALUE 1747 +#define _APS_NEXT_CONTROL_VALUE 1748 #define _APS_NEXT_COMMAND_VALUE 33124 #define _APS_NEXT_SYMED_VALUE 105 #endif diff --git a/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.cpp b/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.cpp index f1b87819103..783e1c8f309 100644 --- a/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.cpp +++ b/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.cpp @@ -1053,6 +1053,16 @@ void BackgroundEditorDialogModel::setOldNebulaColorName(const SCP_string& name) // name not found: ignore } +bool BackgroundEditorDialogModel::getOldNebulaOverSkybox() +{ + return Nebula_over_skybox; +} + +void BackgroundEditorDialogModel::setOldNebulaOverSkybox(bool over) +{ + modify(Nebula_over_skybox, over); +} + int BackgroundEditorDialogModel::getOldNebulaPitch() { return Nebula_pitch; diff --git a/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.h b/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.h index 36118b3aa8d..d7a6f595d13 100644 --- a/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.h +++ b/qtfred/src/mission/dialogs/BackgroundEditorDialogModel.h @@ -125,6 +125,8 @@ class BackgroundEditorDialogModel : public AbstractDialogModel { static SCP_vector getOldNebulaColorOptions(); static SCP_string getOldNebulaColorName(); void setOldNebulaColorName(const SCP_string& name); + static bool getOldNebulaOverSkybox(); + void setOldNebulaOverSkybox(bool over); static SCP_string getOldNebulaPattern(); void setOldNebulaPattern(const SCP_string& name); static int getOldNebulaPitch(); diff --git a/qtfred/src/ui/dialogs/BackgroundEditorDialog.cpp b/qtfred/src/ui/dialogs/BackgroundEditorDialog.cpp index 075274477ed..59e0aabc431 100644 --- a/qtfred/src/ui/dialogs/BackgroundEditorDialog.cpp +++ b/qtfred/src/ui/dialogs/BackgroundEditorDialog.cpp @@ -334,12 +334,14 @@ void BackgroundEditorDialog::updateOldNebulaControls() ui->oldNebulaPatternCombo->setEnabled(enabled); ui->oldNebulaColorCombo->setEnabled(enabled && old_enabled); + ui->oldNebulaOverSkyboxCheck->setEnabled(enabled && old_enabled); ui->oldNebulaPitchSpinBox->setEnabled(enabled && old_enabled); ui->oldNebulaBankSpinBox->setEnabled(enabled && old_enabled); ui->oldNebulaHeadingSpinBox->setEnabled(enabled && old_enabled); ui->oldNebulaPatternCombo->setCurrentIndex(ui->oldNebulaPatternCombo->findText(QString::fromStdString(_model->getOldNebulaPattern()))); ui->oldNebulaColorCombo->setCurrentIndex(ui->oldNebulaColorCombo->findText(QString::fromStdString(_model->getOldNebulaColorName()))); + ui->oldNebulaOverSkyboxCheck->setChecked(_model->getOldNebulaOverSkybox()); ui->oldNebulaPitchSpinBox->setValue(_model->getOldNebulaPitch()); ui->oldNebulaBankSpinBox->setValue(_model->getOldNebulaBank()); ui->oldNebulaHeadingSpinBox->setValue(_model->getOldNebulaHeading()); @@ -813,6 +815,11 @@ void BackgroundEditorDialog::on_oldNebulaColorCombo_currentIndexChanged(int inde _model->setOldNebulaColorName(text.toUtf8().constData()); } +void BackgroundEditorDialog::on_oldNebulaOverSkyboxCheck_toggled(bool checked) +{ + _model->setOldNebulaOverSkybox(checked); +} + void BackgroundEditorDialog::on_oldNebulaPitchSpinBox_valueChanged(int arg1) { _model->setOldNebulaPitch(arg1); diff --git a/qtfred/src/ui/dialogs/BackgroundEditorDialog.h b/qtfred/src/ui/dialogs/BackgroundEditorDialog.h index 42ea46fb77f..8b44fb9f5ec 100644 --- a/qtfred/src/ui/dialogs/BackgroundEditorDialog.h +++ b/qtfred/src/ui/dialogs/BackgroundEditorDialog.h @@ -73,6 +73,7 @@ private slots: // Old Nebula void on_oldNebulaPatternCombo_currentIndexChanged(int index); void on_oldNebulaColorCombo_currentIndexChanged(int index); + void on_oldNebulaOverSkyboxCheck_toggled(bool checked); void on_oldNebulaPitchSpinBox_valueChanged(int arg1); void on_oldNebulaBankSpinBox_valueChanged(int arg1); void on_oldNebulaHeadingSpinBox_valueChanged(int arg1); diff --git a/qtfred/ui/BackgroundEditor.ui b/qtfred/ui/BackgroundEditor.ui index 779d8ab0311..24d3e842344 100644 --- a/qtfred/ui/BackgroundEditor.ui +++ b/qtfred/ui/BackgroundEditor.ui @@ -731,6 +731,16 @@ + + + + Draw the FS1 nebula over the skybox model with additive blending; without this a skybox hides it + + + Draw over skybox + + + @@ -1190,6 +1200,7 @@ lightingProfileCombo oldNebulaPatternCombo oldNebulaColorCombo + oldNebulaOverSkyboxCheck oldNebulaPitchSpinBox oldNebulaBankSpinBox oldNebulaHeadingSpinBox