ui: draw distance and world streaming settings rows

This commit is contained in:
mchughalex 2026-07-19 18:23:58 +01:00
parent 8007e4238a
commit b4d018c405
2 changed files with 117 additions and 1 deletions

View file

@ -116,6 +116,8 @@ class SimpleSettingsDialog final : public ImGuiDialog {
// Live setting values (hot cvars, applied and saved on change). // Live setting values (hot cvars, applied and saved on change).
bool renderer_native_ = true; bool renderer_native_ = true;
bool ssao_ = true; bool ssao_ = true;
int draw_distance_index_ = 1;
int stream_probe_index_ = 0;
bool mode_indicator_ = true; bool mode_indicator_ = true;
bool fps_counter_ = false; bool fps_counter_ = false;
bool audio_mute_ = false; bool audio_mute_ = false;

View file

@ -80,6 +80,18 @@ constexpr std::array<const char*, 6> kShadowQualityLabels = {
"Off", "Auto", "512 (console)", "1024", "1536", "2048"}; "Off", "Auto", "512 (console)", "1024", "1536", "2048"};
constexpr std::array<int32_t, 6> kShadowQualityTiles = {0, 0, 512, 1024, 1536, 2048}; constexpr std::array<int32_t, 6> kShadowQualityTiles = {0, 0, 512, 1024, 1536, 2048};
// Draw distance: one multiplier applied to both the world detail cull and
// the character LOD switch ranges (paired hot cvars in the app layer).
constexpr std::array<const char*, 4> kDrawDistanceLabels = {"Original", "2x",
"3x", "5x"};
constexpr std::array<double, 4> kDrawDistanceScales = {1.0, 2.0, 3.0, 5.0};
// World streaming: pre-load radius in metres for neighbouring world cells
// (0 = the game's own cell-boundary streaming).
constexpr std::array<const char*, 4> kStreamProbeLabels = {
"Original", "Near (100 m)", "Extended (200 m)", "Far (300 m)"};
constexpr std::array<double, 4> kStreamProbeRadii = {0.0, 100.0, 200.0, 300.0};
// Audio device buffer sizes in sample frames (0 = backend default). // Audio device buffer sizes in sample frames (0 = backend default).
constexpr std::array<const char*, 4> kAudioBufferLabels = { constexpr std::array<const char*, 4> kAudioBufferLabels = {
"Auto", "512 (low latency)", "1024", "2048 (most stable)"}; "Auto", "512 (low latency)", "1024", "2048 (most stable)"};
@ -388,6 +400,42 @@ int ShadowQualityIndexFromCvar() {
rex::cvar::Query<int32_t>("skate3_native_render_scene_shadow_tile")); rex::cvar::Query<int32_t>("skate3_native_render_scene_shadow_tile"));
} }
bool HasDrawDistanceCvars() {
return HasCvar("skate3_draw_distance_scale") &&
HasCvar("skate3_lod_distance_scale");
}
template <size_t N>
int NearestValueIndex(const std::array<double, N>& values, double value) {
int best = 0;
double best_err = std::abs(value - values[0]);
for (size_t i = 1; i < N; ++i) {
const double err = std::abs(value - values[i]);
if (err < best_err) {
best = static_cast<int>(i);
best_err = err;
}
}
return best;
}
int DrawDistanceIndexFromCvar() {
if (!HasDrawDistanceCvars()) {
return 0;
}
return NearestValueIndex(kDrawDistanceScales,
rex::cvar::Query<double>("skate3_draw_distance_scale"));
}
int StreamProbeIndexFromCvar() {
if (!HasCvar("skate3_draw_distance_stream_probe")) {
return 0;
}
return NearestValueIndex(
kStreamProbeRadii,
rex::cvar::Query<double>("skate3_draw_distance_stream_probe"));
}
// The native/emulated renderer choice only means anything while the native // The native/emulated renderer choice only means anything while the native
// hook layer (skate3_native_render, restart-scoped) is active. // hook layer (skate3_native_render, restart-scoped) is active.
bool HasRendererChoice() { bool HasRendererChoice() {
@ -899,6 +947,8 @@ void SimpleSettingsDialog::LoadSettingsFromCvars() {
HasCvar("skate3_native_render_scene") && rex::cvar::Query<bool>("skate3_native_render_scene"); HasCvar("skate3_native_render_scene") && rex::cvar::Query<bool>("skate3_native_render_scene");
ssao_ = HasCvar("skate3_native_render_scene_ssao") && ssao_ = HasCvar("skate3_native_render_scene_ssao") &&
rex::cvar::Query<bool>("skate3_native_render_scene_ssao"); rex::cvar::Query<bool>("skate3_native_render_scene_ssao");
draw_distance_index_ = DrawDistanceIndexFromCvar();
stream_probe_index_ = StreamProbeIndexFromCvar();
mode_indicator_ = HasCvar("skate3_native_render_mode_indicator") && mode_indicator_ = HasCvar("skate3_native_render_mode_indicator") &&
rex::cvar::Query<bool>("skate3_native_render_mode_indicator"); rex::cvar::Query<bool>("skate3_native_render_mode_indicator");
fps_counter_ = HasCvar("show_fps_counter") && rex::cvar::Query<bool>("show_fps_counter"); fps_counter_ = HasCvar("show_fps_counter") && rex::cvar::Query<bool>("show_fps_counter");
@ -1261,7 +1311,7 @@ void SimpleSettingsDialog::BuildRows(std::vector<RowSpec>& rows, int category) {
rows.push_back(std::move(row)); rows.push_back(std::move(row));
} }
if (HasRendererChoice() || HasMsaaCvar() || HasShadowQualityCvars() || if (HasRendererChoice() || HasMsaaCvar() || HasShadowQualityCvars() ||
HasCvar("skate3_native_render_scene_ssao")) { HasCvar("skate3_native_render_scene_ssao") || HasDrawDistanceCvars()) {
header("Quality"); header("Quality");
} }
if (HasRendererChoice()) { if (HasRendererChoice()) {
@ -1355,6 +1405,70 @@ void SimpleSettingsDialog::BuildRows(std::vector<RowSpec>& rows, int category) {
}; };
rows.push_back(std::move(row)); rows.push_back(std::move(row));
} }
if (HasDrawDistanceCvars()) {
RowSpec row;
row.kind = RowSpec::kEnum;
row.label = "Draw Distance";
row.desc =
"How far away small world objects, foliage and character detail "
"stay visible, as a multiple of the original console distances. "
"Higher settings draw more of the world and cost some "
"performance. Applies immediately.";
for (const char* label : kDrawDistanceLabels) {
row.options.push_back(label);
}
row.index = &draw_distance_index_;
row.on_enum_change = [this](int value) {
value = std::clamp(value, 0,
static_cast<int>(kDrawDistanceScales.size()) - 1);
const std::string scale = std::to_string(kDrawDistanceScales[value]);
rex::cvar::SetFlagByName("skate3_draw_distance_scale", scale);
rex::cvar::SetFlagByName("skate3_lod_distance_scale", scale);
SaveSimpleSettingsConfig(config_path_);
};
row.reset = [this] {
draw_distance_index_ = NearestValueIndex(
kDrawDistanceScales,
CvarDefaultDouble("skate3_draw_distance_scale", 2.0));
const std::string scale =
std::to_string(kDrawDistanceScales[draw_distance_index_]);
rex::cvar::SetFlagByName("skate3_draw_distance_scale", scale);
rex::cvar::SetFlagByName("skate3_lod_distance_scale", scale);
SaveSimpleSettingsConfig(config_path_);
};
rows.push_back(std::move(row));
}
if (HasCvar("skate3_draw_distance_stream_probe")) {
RowSpec row;
row.kind = RowSpec::kEnum;
row.label = "World Streaming";
row.desc =
"Load neighbouring world areas before they come into view, so "
"buildings and trees stream in far away instead of appearing "
"nearby. Costs a little memory; Original is the console "
"behavior. Applies immediately.";
for (const char* label : kStreamProbeLabels) {
row.options.push_back(label);
}
row.index = &stream_probe_index_;
row.on_enum_change = [this](int value) {
value = std::clamp(value, 0,
static_cast<int>(kStreamProbeRadii.size()) - 1);
rex::cvar::SetFlagByName("skate3_draw_distance_stream_probe",
std::to_string(kStreamProbeRadii[value]));
SaveSimpleSettingsConfig(config_path_);
};
row.reset = [this] {
stream_probe_index_ = NearestValueIndex(
kStreamProbeRadii,
CvarDefaultDouble("skate3_draw_distance_stream_probe", 0.0));
rex::cvar::SetFlagByName(
"skate3_draw_distance_stream_probe",
std::to_string(kStreamProbeRadii[stream_probe_index_]));
SaveSimpleSettingsConfig(config_path_);
};
rows.push_back(std::move(row));
}
if (HasFieldOfViewCvar()) { if (HasFieldOfViewCvar()) {
header("Gameplay"); header("Gameplay");
RowSpec row; RowSpec row;