tr2/ui/graphic_settings: add further controls to graphics dialog

This makes several visual/rendering options available in TR2's graphic
dialog, including adding support for enum types.
This commit is contained in:
lahm86 2025-04-18 11:11:01 +01:00
parent dd2af3377a
commit 70fe64c991
9 changed files with 259 additions and 73 deletions

View file

@ -471,12 +471,32 @@
"CONTROLS_CUSTOM_2": "User Keys 2",
"CONTROLS_CUSTOM_3": "User Keys 3",
"CONTROLS_DEFAULT_KEYS": "Default Keys",
"DETAIL_ASPECT_MODE": "Aspect mode",
"DETAIL_ASPECT_MODE_16_9": "16:9",
"DETAIL_ASPECT_MODE_4_3": "4:3",
"DETAIL_ASPECT_MODE_ANY": "Any",
"DETAIL_BILINEAR": "Bilinear",
"DETAIL_DEPTH_BUFFER": "Z-Buffer",
"DETAIL_FLOAT_FMT": "%.1f",
"DETAIL_FOG_END": "Fog end",
"DETAIL_FOG_START": "Fog start",
"DETAIL_FOV": "Field of view",
"DETAIL_FPS": "FPS",
"DETAIL_INTEGER_FMT": "%d",
"DETAIL_LIGHTING_CONTRAST": "Lighting contrast",
"DETAIL_LIGHTING_CONTRAST_HIGH": "High",
"DETAIL_LIGHTING_CONTRAST_LOW": "Low",
"DETAIL_LIGHTING_CONTRAST_MEDIUM": "Medium",
"DETAIL_RENDER_MODE": "Render mode",
"DETAIL_RENDER_MODE_HARDWARE": "Hardware",
"DETAIL_RENDER_MODE_SOFTWARE": "Software",
"DETAIL_SCALER": "Scaler",
"DETAIL_SIZER": "Sizer",
"DETAIL_TEXTURE_FILTER": "Texture filter",
"DETAIL_TITLE": "Graphic Options",
"DETAIL_TRAPEZOID_FILTER": "Trapezoid filter",
"DETAIL_UI_BAR_SCALE": "UI bar scale",
"DETAIL_UI_TEXT_SCALE": "UI text scale",
"DETAIL_USE_PSX_FOV": "Use PSX FOV",
"DETAIL_WATER_COLOR_B": "Water color (B)",
"DETAIL_WATER_COLOR_G": "Water color (G)",

View file

@ -45,6 +45,7 @@
- improved pause exit dialog - it can now be canceled with escape
- removed the need to specify in the game flow levels that have no secrets (secrets will be automatically counted) (#1582)
- removed the hard-coded end-level behaviour of the bird guardian for custom levels (#1583)
- removed the FPS and aspect mode options from the config tool (now available in-game in the graphics options)
## [0.10](https://github.com/LostArtefacts/TRX/compare/tr2-0.9.2...tr2-0.10) - 2025-03-18
- added support for 60 FPS rendering

View file

@ -135,9 +135,16 @@ GS_DEFINE(OSD_TRAPEZOID_FILTER_OFF, "Trapezoid filter disabled")
GS_DEFINE(DETAIL_INTEGER_FMT, "%d")
GS_DEFINE(DETAIL_FLOAT_FMT, "%.1f")
GS_DEFINE(DETAIL_TITLE, "Graphic Options")
GS_DEFINE(DETAIL_FPS, "FPS")
GS_DEFINE(DETAIL_FOG_START, "Fog start")
GS_DEFINE(DETAIL_FOG_END, "Fog end")
GS_DEFINE(DETAIL_WATER_COLOR_R, "Water color (R)")
GS_DEFINE(DETAIL_WATER_COLOR_G, "Water color (G)")
GS_DEFINE(DETAIL_WATER_COLOR_B, "Water color (B)")
GS_DEFINE(DETAIL_TEXTURE_FILTER, "Texture filter")
GS_DEFINE(DETAIL_BILINEAR, "Bilinear")
GS_DEFINE(DETAIL_TRAPEZOID_FILTER, "Trapezoid filter")
GS_DEFINE(DETAIL_RENDER_MODE, "Render mode")
GS_DEFINE(DETAIL_UI_TEXT_SCALE, "UI text scale")
GS_DEFINE(DETAIL_UI_BAR_SCALE, "UI bar scale")
GS_DEFINE(PAGINATION_NAV, "%d / %d")

View file

@ -2,17 +2,10 @@ GS_DEFINE(PASSPORT_RESTART_LEVEL, "Restart Level")
GS_DEFINE(PASSPORT_STORY_SO_FAR, "Story so far...")
GS_DEFINE(PASSPORT_LEGACY_SELECT_LEVEL_1, "Legacy saves do not")
GS_DEFINE(PASSPORT_LEGACY_SELECT_LEVEL_2, "support this feature.")
GS_DEFINE(DETAIL_FPS, "FPS")
GS_DEFINE(DETAIL_TRAPEZOID_FILTER, "Trapezoid filter")
GS_DEFINE(DETAIL_REFLECTIONS, "Reflections")
GS_DEFINE(DETAIL_BILINEAR, "Bilinear")
GS_DEFINE(DETAIL_TEXTURE_FILTER, "Texture filter")
GS_DEFINE(DETAIL_FBO_FILTER, "FBO filter")
GS_DEFINE(DETAIL_VSYNC, "VSync")
GS_DEFINE(DETAIL_BRIGHTNESS, "Brightness")
GS_DEFINE(DETAIL_UI_TEXT_SCALE, "UI text scale")
GS_DEFINE(DETAIL_UI_BAR_SCALE, "UI bar scale")
GS_DEFINE(DETAIL_RENDER_MODE, "Render mode")
GS_DEFINE(DETAIL_RENDER_MODE_LEGACY, "Window size")
GS_DEFINE(DETAIL_RENDER_MODE_FBO, "Framebuffer")
GS_DEFINE(DETAIL_RESOLUTION, "Resolution")

View file

@ -44,3 +44,16 @@ GS_DEFINE(OSD_PERSPECTIVE_FILTER_ON, "Perspective correction: on")
GS_DEFINE(OSD_PERSPECTIVE_FILTER_OFF, "Perspective correction: off")
GS_DEFINE(DETAIL_FOV, "Field of view")
GS_DEFINE(DETAIL_USE_PSX_FOV, "Use PSX FOV")
GS_DEFINE(DETAIL_DEPTH_BUFFER, "Z-Buffer")
GS_DEFINE(DETAIL_LIGHTING_CONTRAST, "Lighting contrast")
GS_DEFINE(DETAIL_LIGHTING_CONTRAST_LOW, "Low")
GS_DEFINE(DETAIL_LIGHTING_CONTRAST_MEDIUM, "Medium")
GS_DEFINE(DETAIL_LIGHTING_CONTRAST_HIGH, "High")
GS_DEFINE(DETAIL_RENDER_MODE_SOFTWARE, "Software")
GS_DEFINE(DETAIL_RENDER_MODE_HARDWARE, "Hardware")
GS_DEFINE(DETAIL_ASPECT_MODE, "Aspect mode")
GS_DEFINE(DETAIL_ASPECT_MODE_4_3, "4:3")
GS_DEFINE(DETAIL_ASPECT_MODE_16_9, "16:9")
GS_DEFINE(DETAIL_ASPECT_MODE_ANY, "Any")
GS_DEFINE(DETAIL_SCALER, "Scaler")
GS_DEFINE(DETAIL_SIZER, "Sizer")

View file

@ -24,10 +24,65 @@ typedef struct {
int32_t max_value;
int32_t delta_slow;
int32_t delta_fast;
int32_t misc;
const void *misc;
} M_OPTION;
static M_OPTION m_Options[] = {
typedef struct {
int32_t value;
GAME_STRING_ID name;
} M_ENUM_ENTRY;
typedef struct {
const M_ENUM_ENTRY *entry;
int32_t position;
int32_t count;
} M_ENUM_LOOKUP;
static const M_ENUM_ENTRY m_TextureFilterOptions[] = {
// clang-format off
{ GFX_TF_NN, GS_ID(MISC_OFF), },
{ GFX_TF_BILINEAR, GS_ID(DETAIL_BILINEAR), },
{ -1, nullptr, },
// clang-format on
};
static const M_ENUM_ENTRY m_LightingContrastOptions[] = {
// clang-format off
{ LIGHTING_CONTRAST_LOW, GS_ID(DETAIL_LIGHTING_CONTRAST_LOW), },
{ LIGHTING_CONTRAST_MEDIUM, GS_ID(DETAIL_LIGHTING_CONTRAST_MEDIUM), },
{ LIGHTING_CONTRAST_HIGH, GS_ID(DETAIL_LIGHTING_CONTRAST_HIGH), },
{ -1, nullptr, },
// clang-format on
};
static const M_ENUM_ENTRY m_RenderModeOptions[] = {
// clang-format off
{ RM_SOFTWARE, GS_ID(DETAIL_RENDER_MODE_SOFTWARE), },
{ RM_HARDWARE, GS_ID(DETAIL_RENDER_MODE_HARDWARE), },
{ -1, nullptr, },
// clang-format on
};
static const M_ENUM_ENTRY m_AspectModeOptions[] = {
// clang-format off
{ AM_4_3, GS_ID(DETAIL_ASPECT_MODE_4_3), },
{ AM_16_9, GS_ID(DETAIL_ASPECT_MODE_16_9), },
{ AM_ANY, GS_ID(DETAIL_ASPECT_MODE_ANY), },
{ -1, nullptr, },
// clang-format on
};
static const M_OPTION m_Options[] = {
{
.option_type = COT_INT32,
.label_id = GS_ID(DETAIL_FPS),
.target = &g_Config.rendering.fps,
.min_value = 30,
.max_value = 60,
.delta_slow = 30,
.delta_fast = 30,
},
{
.option_type = COT_INT32,
.label_id = GS_ID(DETAIL_FOG_START),
@ -52,7 +107,7 @@ static M_OPTION m_Options[] = {
.max_value = 255,
.delta_slow = 1,
.delta_fast = 10,
.misc = 0,
.misc = (void *)(intptr_t)0,
},
{
@ -63,7 +118,7 @@ static M_OPTION m_Options[] = {
.max_value = 255,
.delta_slow = 1,
.delta_fast = 10,
.misc = 1,
.misc = (void *)(intptr_t)1,
},
{
@ -74,7 +129,7 @@ static M_OPTION m_Options[] = {
.max_value = 255,
.delta_slow = 1,
.delta_fast = 10,
.misc = 2,
.misc = (void *)(intptr_t)2,
},
{
@ -93,12 +148,101 @@ static M_OPTION m_Options[] = {
.target = &g_Config.visuals.use_psx_fov,
},
{
.option_type = COT_ENUM,
.label_id = GS_ID(DETAIL_TEXTURE_FILTER),
.target = &g_Config.rendering.texture_filter,
.delta_slow = 1,
.delta_fast = 1,
.misc = m_TextureFilterOptions,
},
{
.option_type = COT_BOOL,
.label_id = GS_ID(DETAIL_TRAPEZOID_FILTER),
.target = &g_Config.rendering.enable_trapezoid_filter,
},
{
.option_type = COT_BOOL,
.label_id = GS_ID(DETAIL_DEPTH_BUFFER),
.target = &g_Config.rendering.enable_zbuffer,
},
{
.option_type = COT_ENUM,
.label_id = GS_ID(DETAIL_LIGHTING_CONTRAST),
.target = &g_Config.rendering.lighting_contrast,
.delta_slow = 1,
.delta_fast = 1,
.misc = m_LightingContrastOptions,
},
{
.option_type = COT_ENUM,
.label_id = GS_ID(DETAIL_RENDER_MODE),
.target = &g_Config.rendering.render_mode,
.delta_slow = 1,
.delta_fast = 1,
.misc = m_RenderModeOptions,
},
{
.option_type = COT_ENUM,
.label_id = GS_ID(DETAIL_ASPECT_MODE),
.target = &g_Config.rendering.aspect_mode,
.delta_slow = 1,
.delta_fast = 1,
.misc = m_AspectModeOptions,
},
{
.option_type = COT_DOUBLE,
.label_id = GS_ID(DETAIL_UI_TEXT_SCALE),
.target = &g_Config.ui.text_scale,
.min_value = 50,
.max_value = 200,
.delta_slow = 10,
.delta_fast = 10,
},
{
.option_type = COT_DOUBLE,
.label_id = GS_ID(DETAIL_UI_BAR_SCALE),
.target = &g_Config.ui.bar_scale,
.min_value = 50,
.max_value = 200,
.delta_slow = 10,
.delta_fast = 10,
},
{
.option_type = COT_INT32,
.label_id = GS_ID(DETAIL_SCALER),
.target = &g_Config.rendering.scaler,
.min_value = 1,
.max_value = 4,
.delta_slow = 1,
.delta_fast = 1,
},
{
.option_type = COT_FLOAT,
.label_id = GS_ID(DETAIL_SIZER),
.target = &g_Config.rendering.sizer,
.min_value = 40,
.max_value = 200,
.delta_slow = 10,
.delta_fast = 10,
},
{
.target = nullptr,
},
};
static uint8_t *M_GetColorComponent(const M_OPTION *option);
static M_ENUM_LOOKUP M_GetEnumEntry(const M_OPTION *option);
static char *M_FormatRowValue(int32_t row_idx);
static bool M_CanChangeValue(int32_t row_idx, int32_t dir);
static bool M_RequestChangeValue(int32_t row_idx, int32_t dir);
@ -106,7 +250,7 @@ static bool M_RequestChangeValue(int32_t row_idx, int32_t dir);
static uint8_t *M_GetColorComponent(const M_OPTION *const option)
{
RGB_888 *const color = option->target;
switch (option->misc) {
switch ((int32_t)(intptr_t)option->misc) {
case 0:
return &color->r;
case 1:
@ -118,6 +262,27 @@ static uint8_t *M_GetColorComponent(const M_OPTION *const option)
return nullptr;
}
static M_ENUM_LOOKUP M_GetEnumEntry(const M_OPTION *const option)
{
M_ENUM_LOOKUP result = {
.entry = nullptr,
.position = -1,
.count = 0,
};
int32_t current_pos = 0;
const M_ENUM_ENTRY *entry = &((M_ENUM_ENTRY *)option->misc)[0];
while (entry->value != -1) {
if (entry->value == *(int32_t *)option->target) {
result.entry = entry;
result.position = current_pos;
}
entry++;
current_pos++;
result.count++;
}
return result;
}
static char *M_FormatRowValue(const int32_t row_idx)
{
const M_OPTION *const option = &m_Options[row_idx];
@ -128,10 +293,19 @@ static char *M_FormatRowValue(const int32_t row_idx)
case COT_INT32:
return String_Format(
GS(DETAIL_INTEGER_FMT), *(int32_t *)option->target);
case COT_DOUBLE:
return String_Format(GS(DETAIL_FLOAT_FMT), *(double *)option->target);
case COT_FLOAT:
return String_Format(GS(DETAIL_FLOAT_FMT), *(float *)option->target);
case COT_RGB888: {
const uint8_t *const component = M_GetColorComponent(option);
return String_Format("%d", *component);
}
case COT_ENUM: {
const M_ENUM_LOOKUP enum_lookup = M_GetEnumEntry(option);
ASSERT(enum_lookup.entry != nullptr);
return (char *)GameString_Get(enum_lookup.entry->name);
}
default:
break;
}
@ -151,6 +325,20 @@ static bool M_CanChangeValue(const int32_t row_idx, const int32_t dir)
return *(int32_t *)option->target < option->max_value;
}
break;
case COT_DOUBLE:
if (dir < 0) {
return *(double *)option->target > (double)option->min_value / 100;
} else if (dir > 0) {
return *(double *)option->target < (double)option->max_value / 100;
}
break;
case COT_FLOAT:
if (dir < 0) {
return *(float *)option->target > (float)option->min_value / 100;
} else if (dir > 0) {
return *(float *)option->target < (float)option->max_value / 100;
}
break;
case COT_RGB888: {
const uint8_t *const component = M_GetColorComponent(option);
if (dir < 0) {
@ -160,6 +348,16 @@ static bool M_CanChangeValue(const int32_t row_idx, const int32_t dir)
}
break;
}
case COT_ENUM: {
const M_ENUM_LOOKUP enum_lookup = M_GetEnumEntry(option);
ASSERT(enum_lookup.entry != nullptr);
if (dir < 0) {
return enum_lookup.position > 0;
} else if (dir > 0) {
return enum_lookup.position < enum_lookup.count - 1;
}
break;
}
default:
break;
}
@ -186,6 +384,12 @@ static bool M_RequestChangeValue(const int32_t row_idx, const int32_t dir)
case COT_INT32:
*(int32_t *)option->target += delta;
break;
case COT_DOUBLE:
*(double *)option->target += (double)delta / 100;
break;
case COT_FLOAT:
*(float *)option->target += (float)delta / 100;
break;
case COT_RGB888: {
uint8_t *const component = M_GetColorComponent(option);
int32_t component_i = *component;
@ -194,6 +398,13 @@ static bool M_RequestChangeValue(const int32_t row_idx, const int32_t dir)
*component = component_i;
break;
}
case COT_ENUM: {
const M_ENUM_LOOKUP enum_lookup = M_GetEnumEntry(option);
const M_ENUM_ENTRY *const next_entry =
&((M_ENUM_ENTRY *)option->misc)[enum_lookup.position + delta];
*(int32_t *)option->target = next_entry->value;
break;
}
default:
return false;
}
@ -244,7 +455,7 @@ void UI_GraphicSettings(UI_GRAPHIC_SETTINGS_STATE *const s)
UI_Label(GameString_Get(m_Options[i].label_id));
UI_Spacer(20.0f, 0.0f);
UI_BeginResize(70.0f, -1.0f);
UI_BeginResize(110.0f, -1.0f);
UI_BeginStackEx((UI_STACK_SETTINGS) {
.orientation = UI_STACK_HORIZONTAL,
.align = { .h = UI_STACK_H_ALIGN_CENTER },

View file

@ -6,17 +6,6 @@
"window_title_about": "About TR2X Config Tool",
"label_about_details": "Visit the TR2X GitHub page for further information."
},
"Enums": {
"fps": {
"30": "30",
"60": "60"
},
"aspect_mode": {
"any": "Any",
"16:9": "16:9",
"4:3": "4:3"
}
},
"Properties": {
"enable_tr3_sidesteps": {
"Title": "Enhanced sidesteps",
@ -26,10 +15,6 @@
"Title": "Enable more responsive passport",
"Description": "Disables blocking user input when passport flips pages, scheduling the page flips instead."
},
"fps": {
"Title": "FPS",
"Description": "Controls the framerate at which to render the game."
},
"enable_3d_pickups": {
"Title": "3D pickups",
"Description": "Enables 3D models to be rendered in place of the sprites for pickup items."
@ -102,10 +87,6 @@
"Title": "Key item pre-selection",
"Description": "When Lara presses action against a keyhole or puzzle slot, and she has the corresponding item in the inventory, that item will be pre-selected."
},
"aspect_mode": {
"Title": "Aspect mode",
"Description": "Set the aspect ratio of the game output."
},
"screenshot_format": {
"Title": "Screenshot format",
"Description": "Screenshot file format."

View file

@ -6,17 +6,6 @@
"window_title_about": "Informazioni sul Programma di configurazione di TR2X",
"label_about_details": "Per ulteriori informazioni, visita la pagina GitHub di TR2X."
},
"Enums": {
"fps": {
"30": "30",
"60": "60"
},
"aspect_mode": {
"any": "Qualsiasi",
"16:9": "16:9",
"4:3": "4:3"
}
},
"Properties": {
"enable_tr3_sidesteps": {
"Title": "Passi laterali migliorati",
@ -26,10 +15,6 @@
"Title": "Abilita passaporto reattivo",
"Description": "Disabilita il blocco dell'input dell'utente mentre scorrono le pagine del passaporto, programmando invece le pagine da sfogliare."
},
"fps": {
"Title": "FPS",
"Description": "Controlla i fotogrammi al secondo a cui eseguire il gioco."
},
"enable_3d_pickups": {
"Title": "Oggetti 3D",
"Description": "Sostituisce gli oggetti recuperabili 2D con i rispettivi modelli 3D."
@ -102,10 +87,6 @@
"Title": "Preselezione degli oggetti chiave",
"Description": "Quando Lara preme azione contro un buco della serratura o un altro ricettacolo e ha l'oggetto corrispondente nell'inventario, quell'oggetto verrà preselezionato."
},
"aspect_mode": {
"Title": "Modalità rapporto d'aspetto",
"Description": "Imposta il rapporto d'aspetto del gioco."
},
"screenshot_format": {
"Title": "Formato istantanea dello schermo",
"Description": "Formato del file da utilizzare per le istantanee dello schermo."

View file

@ -4,15 +4,6 @@
"jpg",
"png"
],
"fps": [
"30",
"60"
],
"aspect_mode": [
"any",
"16:9",
"4:3"
],
"underwater_music": [
"full",
"quiet",
@ -160,18 +151,6 @@
"DataType": "Bool",
"DefaultValue": true
},
{
"Field": "fps",
"DataType": "Enum",
"EnumKey": "fps",
"DefaultValue": "60"
},
{
"Field": "aspect_mode",
"DataType": "Enum",
"EnumKey": "aspect_mode",
"DefaultValue": "any"
},
{
"Field": "screenshot_format",
"DataType": "Enum",