diff --git a/Descent3/GameLoop.cpp b/Descent3/GameLoop.cpp index cb6deb695..194ad42ac 100644 --- a/Descent3/GameLoop.cpp +++ b/Descent3/GameLoop.cpp @@ -870,7 +870,7 @@ extern bool Display_renderer_stats; // Current zoom factor (this is the tan of 29.25, which is half our FOV of 58.5) -float Render_FOV = D3_DEFAULT_FOV; +float Render_FOV = Render_FOV_setting; float Render_zoom = D3_DEFAULT_ZOOM; // How long (in seconds) the last frame took @@ -2117,7 +2117,7 @@ void ProcessTestKeys(int key) { break; case KEY_8: - Render_FOV = D3_DEFAULT_FOV; + Render_FOV = Render_FOV_setting; AddHUDMessage("FOV has been reset to %f", Render_FOV); break; diff --git a/Descent3/WeaponFire.cpp b/Descent3/WeaponFire.cpp index 198d6423e..b0e7e4c27 100644 --- a/Descent3/WeaponFire.cpp +++ b/Descent3/WeaponFire.cpp @@ -2680,7 +2680,7 @@ void DoZoomStay() { if (Zoom_fov_time < 0) { Zoom_fov_time = 0; Players[Player_num].flags &= ~PLAYER_FLAGS_ZOOMED; - Render_FOV = D3_DEFAULT_FOV; + Render_FOV = Render_FOV_setting; } } } @@ -2689,7 +2689,7 @@ void DoZoomStay() { void DoZoomEffect(player_weapon *pw, uint8_t clear) { if (pw->firing_time < .5) { Players[Player_num].turn_scalar = 1.0; - Render_FOV = D3_DEFAULT_FOV; + Render_FOV = Render_FOV_setting; if (!clear) DoZoomStay(); @@ -2738,7 +2738,7 @@ void DoZoomEffect(player_weapon *pw, uint8_t clear) { // calculate zoom effect float norm = (pw->firing_time - .5) * 2; - Render_FOV = (ZOOM_FOV_TARGET * norm) + ((1.0 - norm) * D3_DEFAULT_FOV); + Render_FOV = (ZOOM_FOV_TARGET * norm) + ((1.0 - norm) * Render_FOV_setting); return; } diff --git a/Descent3/config.cpp b/Descent3/config.cpp index 5abf772d8..bcdbedb50 100644 --- a/Descent3/config.cpp +++ b/Descent3/config.cpp @@ -303,14 +303,132 @@ #include "sounds.h" #include "ctlconfig.h" #include "d3music.h" +#include "gameloop.h" +#include "args.h" + +#include + +#include +#include +#include #define STAT_SCORE STAT_TIMER -tVideoResolution Video_res_list[N_SUPPORTED_VIDRES] = { - {512, 384}, {640, 480}, {800, 600}, {960, 720}, {1024, 768}, {1280, 960}, {1600, 1200}, {640, 480} // custom -}; +// This list is only used if `ConfigureDisplayResolutions` fails +std::vector Video_res_list = {{512, 384}, + {640, 480}, + {800, 600}, + {960, 720}, + {1024, 768}, + {1280, 960}, + {1600, 1200}, + // 16:9 + {1280, 720}, + {1366, 768}, + {1368, 768}, + {1680, 1050}, + {1920, 1080}, + {2560, 1440}, + {3840, 2160}, + // 16:10 + {1280, 800}, + {1920, 1200}, + {2560, 1600}, + // Ultrawide + {2560, 1080}, + {2880, 1200}, + {3440, 1440}, + {3840, 1600}}; + +int Default_resolution_id = 7; // 1280x720 in the default list +int Current_video_resolution_id = Default_resolution_id; +float Render_FOV_setting = 72.0f; +int Display_id = 0; + +void ConfigureDisplayResolutions() { + int display_count = 0; + SDL_DisplayID *displays = SDL_GetDisplays(&display_count); + if (!displays) { + return; + } + + std::set resolutions; + for (int d = 0; d < display_count; d++) { + SDL_DisplayID display_id = displays[d]; + + int modes_count = 0; + SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(display_id, &modes_count); + if (!modes) { + return; + } + for (int modes_id = 0; modes_id < modes_count; modes_id++) { + SDL_DisplayMode *mode = modes[modes_id]; + resolutions.emplace(tVideoResolution{static_cast(mode->w), static_cast(mode->h)}); + } + + SDL_free(modes); + } + + // Take width and height argument into account + int widtharg = FindArg("-Width"); + int heightarg = FindArg("-Height"); + + if (widtharg && !heightarg) { + LOG_ERROR << "Specify '-Height' argument when setting '-Width'"; + } + if (heightarg && !widtharg) { + LOG_ERROR << "Specify '-Width' argument when setting '-Height'"; + } -int Game_video_resolution = 1; + // Use the first display by default, or the one specified by "-display" + int display_arg = FindArg("-display"); + int display_num = 0; + if (display_arg != 0) { + if (const char *arg_index_str = GetArg(display_arg + 1); arg_index_str == nullptr) { + LOG_WARNING << "No parameter for -display given"; + } else { + int arg_index = atoi(arg_index_str); + if ((arg_index < 0) || (arg_index >= display_count)) { + LOG_WARNING.printf("Parameter for -display must be in the range 0..%i", display_count - 1); + } else { + display_num = arg_index; + } + } + } + + Display_id = displays[display_num]; + + // Use either the CLI-provided resolution or the current display resolution as default + tVideoResolution current_resolution; + if (widtharg && heightarg) { + current_resolution.width = static_cast(atoi(GameArgs[widtharg + 1])); + current_resolution.height = static_cast(atoi(GameArgs[heightarg + 1])); + resolutions.emplace(current_resolution); + } else { + const SDL_DisplayMode *current_mode = SDL_GetCurrentDisplayMode(Display_id); + current_resolution.width = static_cast(current_mode->w); + current_resolution.height = static_cast(current_mode->h); + } + + // Fill in Video_res_list from the set of unique resolutions + std::vector resolutions_vec; + std::copy(resolutions.begin(), resolutions.end(), std::back_inserter(resolutions_vec)); + if (resolutions_vec.empty()) { + return; + } + std::swap(resolutions_vec, Video_res_list); + SDL_free(displays); + + // Find the index of the current screen resolution in the list + auto current_res_id = std::find(Video_res_list.begin(), Video_res_list.end(), current_resolution); + if (current_res_id != Video_res_list.end()) { + Default_resolution_id = static_cast(current_res_id - Video_res_list.begin()); + } else { + Default_resolution_id = 0; // default to the highest supported resolution + } + + Current_video_resolution_id = Default_resolution_id; +} tDetailSettings Detail_settings; int Default_detail_level = DETAIL_LEVEL_MED; @@ -464,7 +582,8 @@ void ConfigSetDetailLevel(int level) { #define IDV_GAMMAAPPLY 5 #define IDV_AUTOGAMMA 6 - +#define IDV_CHANGE_RES_WINDOW 10 +#define UID_RESOLUTION 110 static void gamma_callback(newuiTiledWindow *wnd, void *data) { int bm_handle = *((int *)data); @@ -490,22 +609,22 @@ static void gamma_callback(newuiTiledWindow *wnd, void *data) { pnts[i].p3_flags = PF_PROJECTED; } - pnts[0].p3_sx = startx; + pnts[0].p3_sx = static_cast(startx); pnts[0].p3_sy = GAMMA_SLICE_Y; pnts[0].p3_u = 0; pnts[0].p3_v = 0; - pnts[1].p3_sx = startx + GAMMA_SLICE_WIDTH; + pnts[1].p3_sx = static_cast(startx + GAMMA_SLICE_WIDTH); pnts[1].p3_sy = GAMMA_SLICE_Y; pnts[1].p3_u = 2; pnts[1].p3_v = 0; - pnts[2].p3_sx = startx + GAMMA_SLICE_WIDTH; + pnts[2].p3_sx = static_cast(startx + GAMMA_SLICE_WIDTH); pnts[2].p3_sy = GAMMA_SLICE_Y + GAMMA_SLICE_HEIGHT; pnts[2].p3_u = 2; pnts[2].p3_v = 1; - pnts[3].p3_sx = startx; + pnts[3].p3_sx = static_cast(startx); pnts[3].p3_sy = GAMMA_SLICE_Y + GAMMA_SLICE_HEIGHT; pnts[3].p3_u = 0; pnts[3].p3_v = 1; @@ -631,38 +750,43 @@ static void config_gamma() { } } - ////////////////////////////////////////////////////////////////// // VIDEO MENU // struct video_menu { newuiSheet *sheet; - bool *filtering; // settings - bool *mipmapping; - bool *vsync; + // settings + bool *filtering = nullptr; + bool *mipmapping = nullptr; + bool *vsync = nullptr; + char *resolution_string = nullptr; + short *fov = nullptr; - int *bitdepth; // bitdepths - int *resolution; // all resolutions + int *bitdepth = nullptr; // bitdepths // sets the menu up. newuiSheet *setup(newuiMenu *menu) { - int iTemp; sheet = menu->AddOption(IDV_VCONFIG, TXT_OPTVIDEO, NEWUIMENU_MEDIUM); // video resolution - iTemp = Game_video_resolution; sheet->NewGroup(TXT_RESOLUTION, 0, 0); - resolution = sheet->AddFirstLongRadioButton("512x384"); - sheet->AddLongRadioButton("640x480"); - sheet->AddLongRadioButton("800x600"); - sheet->AddLongRadioButton("960x720"); - sheet->AddLongRadioButton("1024x768"); - sheet->AddLongRadioButton("1280x960"); - sheet->AddLongRadioButton("1600x1200"); - *resolution = iTemp; + constexpr int RES_BUFFER_SIZE = 15; + resolution_string = sheet->AddChangeableText(RES_BUFFER_SIZE); + std::string res = Video_res_list[Current_video_resolution_id].getName(); + snprintf(resolution_string, res.size() + 1, res.c_str()); + sheet->AddLongButton("Change", IDV_CHANGE_RES_WINDOW); + + // FOV setting 72deg -> 90deg + tSliderSettings settings = {}; + settings.min_val.f = D3_DEFAULT_FOV; + settings.max_val.f = 90.f; + settings.type = SLIDER_UNITS_FLOAT; + fov = sheet->AddSlider("FOV", static_cast(settings.max_val.f - settings.min_val.f), + static_cast(Render_FOV_setting - D3_DEFAULT_FOV), &settings); #if !defined(POSIX) + int iTemp = 0; // renderer bit depth switch (Render_preferred_bitdepth) { case 16: @@ -685,11 +809,11 @@ struct video_menu { } #endif // video settings - sheet->NewGroup(TXT_TOGGLES, 0, 120); + sheet->NewGroup(TXT_TOGGLES, 0, 80); filtering = sheet->AddLongCheckBox(TXT_BILINEAR, (Render_preferred_state.filtering != 0)); mipmapping = sheet->AddLongCheckBox(TXT_MIPMAPPING, (Render_preferred_state.mipping != 0)); - sheet->NewGroup(TXT_MONITOR, 0, 180); + sheet->NewGroup(TXT_MONITOR, 0, 130); vsync = sheet->AddLongCheckBox(TXT_CFG_VSYNCENABLED, (Render_preferred_state.vsync_on != 0)); sheet->AddText(""); @@ -713,30 +837,65 @@ struct video_menu { if (GetScreenMode() == SM_GAME) { Render_preferred_state.bit_depth = Render_preferred_bitdepth; rend_SetPreferredState(&Render_preferred_state); - } - - if ((*resolution) != Game_video_resolution) { - // if in game, do resolution change. - int temp_w, temp_h; - int old_sm = GetScreenMode(); - Game_video_resolution = *resolution; + SetScreenMode(GetScreenMode(), true); - if (old_sm == SM_GAME) { - SetScreenMode(SM_NULL); - SetScreenMode(old_sm, true); - } - temp_w = Video_res_list[Game_video_resolution].width; - temp_h = Video_res_list[Game_video_resolution].height; + int temp_w = Video_res_list[Current_video_resolution_id].width; + int temp_h = Video_res_list[Current_video_resolution_id].height; Current_pilot.set_hud_data(NULL, NULL, NULL, &temp_w, &temp_h); } + Render_FOV_setting = static_cast(fov[0]) + D3_DEFAULT_FOV; + if (Render_FOV != Render_FOV_setting) { + Render_FOV = Render_FOV_setting; // ISB: this may cause discontinuities if FOV is changed while zoomed. + } + sheet = NULL; }; // process void process(int res) { switch (res) { + case IDV_CHANGE_RES_WINDOW: { + // Resolution configuration window + newuiTiledWindow menu; + newuiSheet *select_sheet; + newuiListBox *resolution_list; + + menu.Create("Resolution", 0, 0, 300, 400); + select_sheet = menu.GetSheet(); + select_sheet->NewGroup(NULL, 10, 0); + resolution_list = select_sheet->AddListBox(208, 257, UID_RESOLUTION, UILB_NOSORT); + select_sheet->NewGroup(NULL, 100, 300, NEWUI_ALIGN_HORIZ); + select_sheet->AddButton(TXT_OK, UID_OK); + select_sheet->AddButton(TXT_CANCEL, UID_CANCEL); + + for (auto &resolution : Video_res_list) { + resolution_list->AddItem(resolution.getName().c_str()); + } + + menu.Open(); + + resolution_list->SetCurrentIndex(Current_video_resolution_id); + + int res; + do { + res = menu.DoUI(); + } while (res != UID_OK && res != UID_CANCEL); + + if (res == UID_OK) { + int newindex = resolution_list->GetCurrentIndex(); + if (static_cast(newindex) < Video_res_list.size()) { + Current_video_resolution_id = newindex; + std::string res = Video_res_list[Current_video_resolution_id].getName(); + snprintf(resolution_string, res.size() + 1, res.c_str()); + } + } + + menu.Close(); + menu.Destroy(); + break; + } case IDV_AUTOGAMMA: config_gamma(); break; @@ -744,7 +903,6 @@ struct video_menu { }; }; - ////////////////////////////////////////////////////////////////// // SOUND MENU // @@ -755,7 +913,7 @@ struct sound_menu { int16_t *fxvolume, *musicvolume; // volume sliders int16_t *fxquantity; // sound fx quantity limit - int *fxquality; // sfx quality low/high + int *fxquality; // sfx quality low/high int16_t old_fxquantity; @@ -920,7 +1078,6 @@ struct sound_menu { }; }; - ////////////////////////////////////////////////////////////////// // GENERAL SETTINGS (TOGGLES) MENU // @@ -1036,7 +1193,6 @@ struct toggles_menu { }; }; - ////////////////////////////////////////////////////////////////// // HUD CONFIG MENU // @@ -1158,7 +1314,6 @@ struct hud_menu { }; }; - ////////////////////////////////////////////////////////////////// // DETAILS MENU // @@ -1171,7 +1326,7 @@ struct details_menu { bool *specmap, *headlight, *mirror, // check boxes *dynamic, *fog, *coronas, *procedurals, *powerup_halo, *scorches, *weapon_coronas; int16_t *pixel_err, // 0-27 (1-28) - *rend_dist; // 0-120 (80-200) + *rend_dist; // 0-120 (80-200) int *texture_quality; @@ -1208,7 +1363,7 @@ struct details_menu { // sliders tSliderSettings slider_set; sheet->NewGroup(TXT_GEOMETRY, 90, 0); - iTemp = MAXIMUM_TERRAIN_DETAIL - Detail_settings.Pixel_error - MINIMUM_TERRAIN_DETAIL; + iTemp = static_cast(MAXIMUM_TERRAIN_DETAIL - Detail_settings.Pixel_error - MINIMUM_TERRAIN_DETAIL); if (iTemp < 0) iTemp = 0; slider_set.min_val.i = MINIMUM_TERRAIN_DETAIL; @@ -1242,7 +1397,7 @@ struct details_menu { Detail_settings.Fog_enabled = *fog; Detail_settings.Mirrored_surfaces = *mirror; Detail_settings.Object_complexity = *objcomp; - Detail_settings.Pixel_error = MAXIMUM_TERRAIN_DETAIL - ((*pixel_err) + MINIMUM_TERRAIN_DETAIL); + Detail_settings.Pixel_error = static_cast(MAXIMUM_TERRAIN_DETAIL - ((*pixel_err) + MINIMUM_TERRAIN_DETAIL)); Detail_settings.Powerup_halos = *powerup_halo; Detail_settings.Procedurals_enabled = *procedurals; Detail_settings.Scorches_enabled = *scorches; @@ -1306,7 +1461,7 @@ void details_menu::set_preset_details(int setting) { // now go through all the config items and set to the new values int iTemp; - iTemp = MAXIMUM_TERRAIN_DETAIL - ds.Pixel_error - MINIMUM_TERRAIN_DETAIL; + iTemp = static_cast(MAXIMUM_TERRAIN_DETAIL - ds.Pixel_error - MINIMUM_TERRAIN_DETAIL); if (iTemp < 0) iTemp = 0; *pixel_err = (int16_t)(iTemp); diff --git a/Descent3/config.h b/Descent3/config.h index 15be446c5..8086a3b2f 100644 --- a/Descent3/config.h +++ b/Descent3/config.h @@ -118,6 +118,8 @@ #define CONFIG_H_ #include +#include +#include // Main menu configuration functions @@ -130,24 +132,46 @@ struct tGameToggles { extern tGameToggles Game_toggles; -// this list should match the list in config.cpp to work. -#define N_SUPPORTED_VIDRES 8 - -#define RES_512X384 0 -#define RES_640X480 1 -#define RES_800X600 2 -#define RES_960X720 3 -#define RES_1024X768 4 -#define RES_1280X960 5 -#define RES_1600X1200 6 // stored resolution list and desired game resolution -struct tVideoResolution { +struct tVideoResolution +{ uint16_t width; uint16_t height; + + std::string getName() const + { + std::stringstream ss; + ss << this->width << "x" << this->height; + return ss.str(); + } + + bool operator==(const tVideoResolution& other) { + return other.width == this->width && other.height == this->height; + } + + struct tVideoResolutionCompare + { + bool operator()(const tVideoResolution &lres, const tVideoResolution &rres) const + { + if (lres.width != rres.width) + { + return lres.width < rres.width; + } + return lres.height < rres.height; + } + }; }; -extern tVideoResolution Video_res_list[]; -extern int Game_video_resolution; +extern std::vector Video_res_list; +extern int Default_resolution_id; +extern int Current_video_resolution_id; +extern int Display_id; + +/** + * List all unique resolutions for the detected displays, + * and set variables Video_res_list, Game_video_resolution and Display_id + */ +void ConfigureDisplayResolutions(); //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // KEEP THESE MEMBERS IN THE SAME ORDER, IF YOU ADD,REMOVE, OR CHANGE ANYTHING IN THIS STRUCT, MAKE SURE YOU diff --git a/Descent3/descent.cpp b/Descent3/descent.cpp index f09cb4f95..0bf0c64e2 100644 --- a/Descent3/descent.cpp +++ b/Descent3/descent.cpp @@ -407,6 +407,7 @@ #include "args.h" #include "multi_dll_mgr.h" #include "localization.h" +#include "config.h" #include "uisys.h" // --------------------------------------------------------------------------- @@ -475,6 +476,7 @@ void Descent3() { } // Show intro & loading screens if not dedicated server if (!Dedicated_server) { + ConfigureDisplayResolutions(); SetScreenMode(SM_CINEMATIC); // Show the intro movie diff --git a/Descent3/descent.h b/Descent3/descent.h index b111ccf21..a5c8414df 100644 --- a/Descent3/descent.h +++ b/Descent3/descent.h @@ -174,7 +174,7 @@ enum function_mode { extern bool Descent_overrided_intro; // This is the default FOV -#define D3_DEFAULT_FOV 72.0 +#define D3_DEFAULT_FOV 72.0f // This is the default zoom factor to be used for the game 3D view. #define D3_DEFAULT_ZOOM 0.726f diff --git a/Descent3/game.cpp b/Descent3/game.cpp index 9a74c0a3d..787796f2e 100644 --- a/Descent3/game.cpp +++ b/Descent3/game.cpp @@ -1,5 +1,5 @@ /* -* Descent 3 +* Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify @@ -939,13 +939,12 @@ void SetScreenMode(int sm, bool force_res_change) { rend_Close(); rend_initted = 0; } - } - else { + } else { int scr_width, scr_height, scr_bitdepth; if (sm == SM_GAME) { - scr_width = Video_res_list[Game_video_resolution].width; - scr_height = Video_res_list[Game_video_resolution].height; + scr_width = Video_res_list[Current_video_resolution_id].width; + scr_height = Video_res_list[Current_video_resolution_id].height; scr_bitdepth = Render_preferred_bitdepth; } else { scr_width = FIXED_SCREEN_WIDTH; @@ -982,7 +981,7 @@ void SetScreenMode(int sm, bool force_res_change) { // We're using the default, so change some values for the menus rend_initted = 1; LOG_INFO << "Changing menu settings to default!"; - Game_video_resolution = RES_640X480; + Current_video_resolution_id = Default_resolution_id; Render_preferred_state.bit_depth = 32; scr_width = 640; scr_height = 480; @@ -1000,7 +999,7 @@ void SetScreenMode(int sm, bool force_res_change) { // We're using the default, so change some values for the menus rend_initted = 1; LOG_INFO << "Changing menu settings to default!"; - Game_video_resolution = RES_640X480; + Current_video_resolution_id = Default_resolution_id; Render_preferred_state.bit_depth = 32; scr_width = 640; scr_height = 480; diff --git a/Descent3/gameloop.h b/Descent3/gameloop.h index 0bcdb3d7a..e43663fe6 100644 --- a/Descent3/gameloop.h +++ b/Descent3/gameloop.h @@ -108,6 +108,7 @@ void GameFrame(void); void GameRenderWorld(object *viewer, vector *viewer_eye, int viewer_roomnum, matrix *viewer_orient, float zoom, bool rear_view); +extern float Render_FOV_setting; extern float Render_zoom; extern float Render_FOV; diff --git a/Descent3/hud.cpp b/Descent3/hud.cpp index 24b28a516..319aaff82 100644 --- a/Descent3/hud.cpp +++ b/Descent3/hud.cpp @@ -1416,12 +1416,16 @@ void RenderHUDItems(tStatMask stat_mask) { font_aspect_x = (float)Game_window_w / Max_window_w; font_aspect_y = (float)Game_window_h / Max_window_h; + // Add HUD text scaling for large screens + constexpr float scaling_height_threshold = 1080.0f; + float scaling_factor = std::max(1.0f, Max_window_h / scaling_height_threshold); + if (font_aspect_x <= 0.60f) { - grtext_SetFontScale(0.60f); + grtext_SetFontScale(0.60f * scaling_factor); } else if (font_aspect_x <= 0.80f) { - grtext_SetFontScale(0.80f); + grtext_SetFontScale(0.80f * scaling_factor); } else { - grtext_SetFontScale(1.0f); + grtext_SetFontScale(scaling_factor); } // do framerate calculations diff --git a/Descent3/huddisplay.cpp b/Descent3/huddisplay.cpp index 4b8fa0092..27d0b1e40 100644 --- a/Descent3/huddisplay.cpp +++ b/Descent3/huddisplay.cpp @@ -963,7 +963,6 @@ void RenderHUDScore(tHUDItem *item) { snprintf(buf, sizeof(buf), "%s: %d ", TXT_SCORE, Players[Player_num].score); win_w = (Max_window_w - Game_window_w) * (Hud_aspect_x); - // if (Game_video_resolution==RES_512X384) { win_w = win_w + 10; } int w = RenderHUDGetTextLineWidth(buf); // * win_w)/(Game_window_w); RenderHUDText(item->color, HUD_ALPHA, 0, item->x - w - win_w, item->y, buf); diff --git a/Descent3/init.cpp b/Descent3/init.cpp index 99fb0c515..a54d2417a 100644 --- a/Descent3/init.cpp +++ b/Descent3/init.cpp @@ -980,6 +980,8 @@ #include "gamecinematics.h" #include "debuggraph.h" +#include + // Uncomment this to allow all languages #define ALLOW_ALL_LANG 1 @@ -1155,7 +1157,8 @@ void SaveGameSettings() { Database->write("DetailObjectComp", Detail_settings.Object_complexity); Database->write("DetailPowerupHalos", Detail_settings.Powerup_halos); - Database->write("RS_resolution", Game_video_resolution); + Database->write("RS_resolution", Current_video_resolution_id); + Database->write("RS_fov", static_cast(Render_FOV_setting)); Database->write("RS_bitdepth", Render_preferred_bitdepth); Database->write("RS_bilear", Render_preferred_state.filtering); @@ -1231,7 +1234,7 @@ void LoadGameSettings() { D3Use_force_feedback = true; D3Force_gain = 1.0f; D3Force_auto_center = true; - Game_video_resolution = RES_640X480; + Current_video_resolution_id = Default_resolution_id; PlayPowerupVoice = true; PlayVoices = true; Sound_mixer = SOUND_MIXER_SOFTWARE_16; @@ -1287,7 +1290,14 @@ void LoadGameSettings() { Database->read_int("RoomLeveling", &Default_player_room_leveling); Database->read("Specmapping", &Detail_settings.Specular_lighting); Database->read("RS_bitdepth", &Render_preferred_bitdepth, sizeof(Render_preferred_bitdepth)); - Database->read_int("RS_resolution", &Game_video_resolution); + Database->read_int("RS_resolution", &Current_video_resolution_id); + + int tempfov = 0; + Database->read_int("RS_fov", &tempfov); + tempfov = std::clamp(tempfov, static_cast(D3_DEFAULT_FOV), 90); + Render_FOV_setting = static_cast(tempfov); + Render_FOV = Render_FOV_setting; + Database->read_int("RS_bilear", &Render_preferred_state.filtering); Database->read_int("RS_mipping", &Render_preferred_state.mipping); Database->read_int("RS_color_model", &Render_state.cur_color_model); @@ -1359,16 +1369,6 @@ void LoadGameSettings() { Database->read_int("PredefDetailSetting", &level); ConfigSetDetailLevel(level); - int widtharg = FindArg("-Width"); - int heightarg = FindArg("-Height"); - if (widtharg) { - Video_res_list[N_SUPPORTED_VIDRES - 1].width = atoi(GameArgs[widtharg + 1]); - Game_video_resolution = N_SUPPORTED_VIDRES - 1; - } - if (heightarg) { - Video_res_list[N_SUPPORTED_VIDRES - 1].height = atoi(GameArgs[heightarg + 1]); - Game_video_resolution = N_SUPPORTED_VIDRES - 1; - } // Motion blur Use_motion_blur = 0; @@ -1419,7 +1419,8 @@ void InitIOSystems(bool editor) { while (0 != (additionaldirarg = FindArg("-additionaldir", additionaldirarg))) { const auto dir_to_add = GetArg(additionaldirarg + 1); if (dir_to_add == NULL) { - LOG_WARNING << "-additionaldir was at the end of the argument list. It should never be at the end of the argument list."; + LOG_WARNING + << "-additionaldir was at the end of the argument list. It should never be at the end of the argument list."; break; } else { cf_AddBaseDirectory(std::filesystem::path(dir_to_add)); @@ -1909,7 +1910,7 @@ void InitD3Systems2(bool editor) { // the remaining sound system InitVoices(); - InitD3Music(FindArg("-nomusic") || FindArg("-nosound") ? false : true); + InitD3Music(FindArg("-nomusic") || FindArg("-nosound") ? false : true); InitAmbientSoundSystem(); InitGameSystems(editor); @@ -1957,7 +1958,7 @@ void SetupTempDirectory(void) { std::error_code ec; std::filesystem::path tempPath = std::filesystem::temp_directory_path(ec); if (ec) { - Error("Could not find temporary directory: \"%s\"", ec.message().c_str() ); + Error("Could not find temporary directory: \"%s\"", ec.message().c_str()); exit(1); } Descent3_temp_directory = tempPath / "Descent3" / "cache"; @@ -2032,8 +2033,8 @@ void DeleteTempFiles() { ddio_DoForeachFile(Descent3_temp_directory, std::regex("d3[smocti].+\\.tmp"), [](const std::filesystem::path &path) { std::error_code ec; std::filesystem::remove(path, ec); - LOG_WARNING_IF(ec).printf("Unable to remove temporary file %s: %s\n", - path.u8string().c_str(), ec.message().c_str()); + LOG_WARNING_IF(ec).printf("Unable to remove temporary file %s: %s\n", path.u8string().c_str(), + ec.message().c_str()); }); } diff --git a/Descent3/object.cpp b/Descent3/object.cpp index 1ee5b8a69..130177e05 100644 --- a/Descent3/object.cpp +++ b/Descent3/object.cpp @@ -2490,7 +2490,7 @@ void ObjDoEffects(object *obj) { // Stop doing liquid effect obj->effect_info->type_flags &= ~EF_LIQUID; if (obj == Viewer_object) - Render_FOV = D3_DEFAULT_FOV; + Render_FOV = Render_FOV_setting; } else { if (obj == Viewer_object) { int inttime = Gametime; @@ -2502,7 +2502,7 @@ void ObjDoEffects(object *obj) { if (obj->effect_info->liquid_time_left < 1) scalar *= (obj->effect_info->liquid_time_left); - Render_FOV = D3_DEFAULT_FOV + scalar; + Render_FOV = Render_FOV_setting + scalar; } } } diff --git a/Descent3/pilot_class.cpp b/Descent3/pilot_class.cpp index d459fff1a..59f101a3a 100644 --- a/Descent3/pilot_class.cpp +++ b/Descent3/pilot_class.cpp @@ -205,8 +205,8 @@ void pilot::initialize(void) { hud_mode = (uint8_t)HUD_COCKPIT; hud_stat = 0; hud_graphical_stat = STAT_STANDARD; - game_window_w = Video_res_list[Game_video_resolution].width; - game_window_h = Video_res_list[Game_video_resolution].height; + game_window_w = Video_res_list[Current_video_resolution_id].width; + game_window_h = Video_res_list[Current_video_resolution_id].height; num_missions_flown = 0; mission_data = NULL; mouselook_control = false; diff --git a/renderer/HardwareOpenGL.cpp b/renderer/HardwareOpenGL.cpp index 1ef1893dc..55459c476 100644 --- a/renderer/HardwareOpenGL.cpp +++ b/renderer/HardwareOpenGL.cpp @@ -353,8 +353,8 @@ void opengl_SetDefaults() { extern renderer_preferred_state Render_preferred_state; int opengl_Setup(oeApplication *app, const int *width, const int *height) { - int winw = Video_res_list[Game_video_resolution].width; - int winh = Video_res_list[Game_video_resolution].height; + int winw = Video_res_list[Current_video_resolution_id].width; + int winh = Video_res_list[Current_video_resolution_id].height; SDL_ClearError(); if (!SDL_WasInit(SDL_INIT_VIDEO)) { @@ -424,27 +424,9 @@ int opengl_Setup(oeApplication *app, const int *width, const int *height) { int display_arg = FindArg("-display"); int display_count = 0; - SDL_DisplayID* displays = SDL_GetDisplays(&display_count); - - if (display_arg != 0) { - if (const char * arg_index_str = GetArg (display_arg + 1); arg_index_str == nullptr) { - LOG_WARNING << "No parameter for -display given"; - } else { - int arg_index = atoi(arg_index_str); - if ((arg_index < 0) || (arg_index >= display_count)) { - LOG_WARNING.printf( "Parameter for -display must be in the range 0..%i", display_count-1 ); - } else { - display_num = arg_index; - } - } - } - - int display_id = displays[display_num]; - SDL_free(displays); - //High-DPI support { - float scale = SDL_GetDisplayContentScale(display_id); + float scale = SDL_GetDisplayContentScale(Display_id); LOG_WARNING.printf("Using content scale %f", scale); winw = std::floor(static_cast(winw)*scale); winh = std::floor(static_cast(winh)*scale); @@ -453,8 +435,8 @@ int opengl_Setup(oeApplication *app, const int *width, const int *height) { SDL_PropertiesID props = SDL_CreateProperties(); SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, "Descent 3"); - SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_UNDEFINED_DISPLAY(display_id)); - SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_UNDEFINED_DISPLAY(display_id)); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_UNDEFINED_DISPLAY(Display_id)); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_UNDEFINED_DISPLAY(Display_id)); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, winw); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, winh); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, SDL_WINDOW_OPENGL); diff --git a/renderer/HardwareSetup.cpp b/renderer/HardwareSetup.cpp index 8bee1d512..fee5a2463 100644 --- a/renderer/HardwareSetup.cpp +++ b/renderer/HardwareSetup.cpp @@ -1,5 +1,5 @@ /* -* Descent 3 +* Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify @@ -54,23 +54,29 @@ void g3_GetProjectionMatrix(float zoom, float *projMat) { int viewportWidth, viewportHeight; rend_GetProjectionParameters(&viewportWidth, &viewportHeight); - // compute aspect ratio for this ViewPort - float screenAspect = rend_GetAspectRatio(); - if (sAspect != 0.0f) { - // check for user override - screenAspect = screenAspect * 4.0f / 3.0f / sAspect; - } - float s = screenAspect * ((float)viewportWidth) / ((float)viewportHeight); + float s = ((float)viewportWidth) / ((float)viewportHeight); + float vertical_fov = zoom * 3.0f / 4.0f; // setup the matrix memset(projMat, 0, sizeof(float) * 16); // calculate 1/tan(fov) - float oOT = 1.0f / zoom; + float oOT = 1.0f / vertical_fov; // fill in the matrix - projMat[0] = oOT; - projMat[5] = oOT * s; + // Go read https://www.songho.ca/opengl/gl_projectionmatrix.html + // if you feel like doing the math again :) + if (s <= 1.0f) + { + projMat[0] = oOT; + projMat[5] = oOT * s; + } + else + { + projMat[0] = oOT / s; + projMat[5] = oOT; + } + projMat[10] = 1.0f; projMat[11] = 1.0f; projMat[14] = -1.0f; @@ -91,16 +97,11 @@ void g3_StartFrame(vector *view_pos, matrix *view_matrix, float zoom) { Window_w2 = ((float)Window_width) * 0.5f; Window_h2 = ((float)Window_height) * 0.5f; - // Compute aspect ratio for this window - float screen_aspect = rend_GetAspectRatio(); - if (sAspect != 0.0f) { - // check for user override - screen_aspect = screen_aspect * 4.0f / 3.0f / sAspect; - } - float s = screen_aspect * (float)Window_height / (float)Window_width; + // ISB trick: use the window aspect only, screen aspect ratio + // is not important because we assume pixels are square + float s = (float)Window_height / (float)Window_width; - if (s <= 0.0f) // JEFF: Should this have been 1.0f? - { + if (s <= 1.0f) { // scale x Matrix_scale.x = s; Matrix_scale.y = 1.0f; @@ -109,6 +110,9 @@ void g3_StartFrame(vector *view_pos, matrix *view_matrix, float zoom) { Matrix_scale.x = 1.0f; } + //ISB: Convert zoom into vertical FOV for convenience + zoom *= 3.f / 4.f; + Matrix_scale.z = 1.0f; // Set the view variables @@ -116,16 +120,10 @@ void g3_StartFrame(vector *view_pos, matrix *view_matrix, float zoom) { View_zoom = zoom; Unscaled_matrix = *view_matrix; - // Compute matrix scale for zoom and aspect ratio - if (View_zoom <= 1.0f) { - // zoom in by scaling z - Matrix_scale.z = Matrix_scale.z * View_zoom; - } else { - // zoom out by scaling x and y - float oOZ = 1.0f / View_zoom; - Matrix_scale.x = Matrix_scale.x * oOZ; - Matrix_scale.y = Matrix_scale.y * oOZ; - } + // Scale x and y to zoom in or out; + float oOZ = 1.0f / View_zoom; + Matrix_scale.x = Matrix_scale.x * oOZ; + Matrix_scale.y = Matrix_scale.y * oOZ; // Scale the matrix elements View_matrix.rvec = Unscaled_matrix.rvec * Matrix_scale.x;