Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions code/lab/renderer/lab_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down
5 changes: 5 additions & 0 deletions code/mission/missionparse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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("#");
}
Expand Down Expand Up @@ -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;
}

Expand Down
11 changes: 11 additions & 0 deletions code/missioneditor/missionsave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions code/mod_table/mod_table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:")) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions code/mod_table/mod_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
103 changes: 101 additions & 2 deletions code/starfield/nebula.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -32,15 +36,28 @@ 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;

int Nebula_pitch;
int Nebula_bank;
int Nebula_heading;
bool Nebula_over_skybox = false;

void nebula_close()
{
if (Nebula_ramp_bitmap >= 0) {
bm_release(Nebula_ramp_bitmap);
Nebula_ramp_bitmap = -1;
}

if (!Nebula_loaded) return;

Nebula_loaded = 0;
Expand Down Expand Up @@ -82,20 +99,23 @@ 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;
}

// ID of NEBU
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 );
major = version / 100;

if ( major != NEBULA_MAJOR_VERSION ) {
mprintf(( "An out of date nebula file.\n" ));
cfclose(fp);
return 0;
}

Expand All @@ -105,8 +125,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; i++ ) {
Expand All @@ -117,7 +139,8 @@ int load_nebula_sub(const char *filename)
cfread( &yf, sizeof(float), 1, fp );
cfread( &l, sizeof(int), 1, fp );
project_2d_onto_sphere( &nebula_vecs[i], 1.0f - xf, yf );
vm_vec_scale( &nebula_vecs[i], 10.0f );
// same radius as the background bitmap sphere, so a mod's $Min draw distance: cannot clip it
vm_vec_scale( &nebula_vecs[i], 1000.0f );
nebula_verts[i].b = ubyte((l*255)/31);

// throw in some randomness to the nebula vertices depth
Expand All @@ -127,13 +150,48 @@ int load_nebula_sub(const char *filename)
cfread( &tri[i][0], sizeof(int), 1, fp );
cfread( &tri[i][1], sizeof(int), 1, fp );
cfread( &tri[i][2], sizeof(int), 1, fp );
for (int k = 0; k < 3; k++) {
if (tri[i][k] < 0 || tri[i][k] >= num_pts) {
mprintf(( "Nebula file %s has a triangle index out of range.\n", filename ));
cfclose(fp);
return 0;
}
}
}

cfclose(fp);

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;
Expand All @@ -143,6 +201,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 )
Expand All @@ -167,6 +229,43 @@ void nebula_init( const char *filename, angles * pbh )
}
}

void nebula_render(bool over_skybox)
{
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);
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();
}

DCF(nebula,"Loads a different nebula")
{
SCP_string filename;
Expand Down
3 changes: 3 additions & 0 deletions code/starfield/nebula.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +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(bool over_skybox);

extern bool Nebula_over_skybox;

#endif //_NEBULA_H
6 changes: 5 additions & 1 deletion code/starfield/starfield.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 || 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) ) {
stars_draw_stars();
}
Expand Down
9 changes: 9 additions & 0 deletions fred2/bgbitmapdlg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -601,6 +606,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;
}
Expand All @@ -615,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;
Expand Down Expand Up @@ -678,13 +685,15 @@ 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);
} else {
// 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);
Expand Down
Loading
Loading