ui: enhanced sun-shadow settings rows

This commit is contained in:
mchughalex 2026-07-19 18:57:39 +01:00
parent f8cbe94367
commit fbd4e06a34
2 changed files with 157 additions and 1 deletions

View file

@ -102,6 +102,7 @@ class SimpleSettingsDialog final : public ImGuiDialog {
int aspect_ratio_index_ = 0;
int msaa_index_ = 2;
int shadow_quality_index_ = 2;
int static_shadow_res_index_ = 2;
int monitor_index_ = 0;
int audio_buffer_index_ = 0;
int language_index_ = 0;
@ -116,6 +117,9 @@ class SimpleSettingsDialog final : public ImGuiDialog {
// Live setting values (hot cvars, applied and saved on change).
bool renderer_native_ = true;
bool ssao_ = true;
bool static_shadows_ = true;
float static_shadow_strength_ = 1.0f;
bool shadow_pcss_ = true;
int draw_distance_index_ = 1;
int stream_probe_index_ = 0;
bool mode_indicator_ = true;

View file

@ -53,11 +53,15 @@ constexpr std::array<std::string_view, 7> kCoreSimpleSettingsCvars = {
// Optional cvars persisted when the host defines them (HasCvar-gated: app
// cvars like the native-renderer knobs don't exist in every embedder, and
// backend/platform cvars don't exist in every build).
constexpr std::array<std::string_view, 14> kOptionalSimpleSettingsCvars = {
constexpr std::array<std::string_view, 18> kOptionalSimpleSettingsCvars = {
"skate3_native_render_scene",
"skate3_native_render_scene_msaa",
"skate3_native_render_scene_shadows",
"skate3_native_render_scene_shadow_tile",
"skate3_native_render_scene_shadow_static_casters",
"skate3_native_render_scene_shadow_static_strength",
"skate3_native_render_scene_shadow_static_size",
"skate3_native_render_scene_shadow_pcss",
"skate3_native_render_scene_ssao",
"skate3_native_render_mode_indicator",
"show_fps_counter",
@ -80,6 +84,12 @@ constexpr std::array<const char*, 6> kShadowQualityLabels = {
"Off", "Auto", "512 (console)", "1024", "1536", "2048"};
constexpr std::array<int32_t, 6> kShadowQualityTiles = {0, 0, 512, 1024, 1536, 2048};
// Enhanced (static sun) shadow map resolution per cascade tile.
constexpr std::array<const char*, 4> kStaticShadowResLabels = {"1024", "2048",
"4096", "8192"};
constexpr std::array<int32_t, 4> kStaticShadowResSizes = {1024, 2048, 4096,
8192};
// 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",
@ -400,6 +410,29 @@ int ShadowQualityIndexFromCvar() {
rex::cvar::Query<int32_t>("skate3_native_render_scene_shadow_tile"));
}
bool HasStaticShadowCvars() {
return HasCvar("skate3_native_render_scene_shadow_static_casters") &&
HasCvar("skate3_native_render_scene_shadow_static_size");
}
int StaticShadowResIndexFrom(int32_t size) {
int best = 0;
for (int i = 0; i < static_cast<int>(kStaticShadowResSizes.size()); ++i) {
if (size >= kStaticShadowResSizes[i]) {
best = i;
}
}
return best;
}
int StaticShadowResIndexFromCvar() {
if (!HasStaticShadowCvars()) {
return 2;
}
return StaticShadowResIndexFrom(
rex::cvar::Query<int32_t>("skate3_native_render_scene_shadow_static_size"));
}
bool HasDrawDistanceCvars() {
return HasCvar("skate3_draw_distance_scale") &&
HasCvar("skate3_lod_distance_scale");
@ -934,6 +967,7 @@ void SimpleSettingsDialog::LoadSettingsFromCvars() {
HasCvar("skate3_ultrawide") && rex::cvar::Query<bool>("skate3_ultrawide") ? 1 : 0;
msaa_index_ = MsaaIndexFromCvar();
shadow_quality_index_ = ShadowQualityIndexFromCvar();
static_shadow_res_index_ = StaticShadowResIndexFromCvar();
monitor_index_ = MonitorIndexFromCvar();
audio_buffer_index_ = AudioBufferIndexFromCvar();
language_index_ = LanguageIndexFromCvar();
@ -947,6 +981,17 @@ void SimpleSettingsDialog::LoadSettingsFromCvars() {
HasCvar("skate3_native_render_scene") && rex::cvar::Query<bool>("skate3_native_render_scene");
ssao_ = HasCvar("skate3_native_render_scene_ssao") &&
rex::cvar::Query<bool>("skate3_native_render_scene_ssao");
static_shadows_ =
HasCvar("skate3_native_render_scene_shadow_static_casters") &&
rex::cvar::Query<bool>("skate3_native_render_scene_shadow_static_casters");
static_shadow_strength_ =
HasCvar("skate3_native_render_scene_shadow_static_strength")
? float(std::clamp(rex::cvar::Query<double>(
"skate3_native_render_scene_shadow_static_strength"),
0.0, 1.0))
: 1.0f;
shadow_pcss_ = HasCvar("skate3_native_render_scene_shadow_pcss") &&
rex::cvar::Query<bool>("skate3_native_render_scene_shadow_pcss");
draw_distance_index_ = DrawDistanceIndexFromCvar();
stream_probe_index_ = StreamProbeIndexFromCvar();
mode_indicator_ = HasCvar("skate3_native_render_mode_indicator") &&
@ -969,6 +1014,8 @@ bool SimpleSettingsDialog::HasSettingsChanges() const {
(aspect_ratio_index_ != 0) != rex::cvar::Query<bool>("skate3_ultrawide")) ||
(HasMsaaCvar() && msaa_index_ != MsaaIndexFromCvar()) ||
(HasShadowQualityCvars() && shadow_quality_index_ != ShadowQualityIndexFromCvar()) ||
(HasStaticShadowCvars() &&
static_shadow_res_index_ != StaticShadowResIndexFromCvar()) ||
(HasCvar("monitor") && monitor_index_ != MonitorIndexFromCvar()) ||
(HasCvar("audio_device_sample_frames") &&
audio_buffer_index_ != AudioBufferIndexFromCvar()) ||
@ -1097,6 +1144,14 @@ void SimpleSettingsDialog::SaveVideo() {
std::to_string(kShadowQualityTiles[shadow_quality_index_]));
}
}
if (HasStaticShadowCvars()) {
static_shadow_res_index_ = std::clamp(
static_shadow_res_index_, 0,
static_cast<int>(kStaticShadowResSizes.size()) - 1);
rex::cvar::SetFlagByName(
"skate3_native_render_scene_shadow_static_size",
std::to_string(kStaticShadowResSizes[static_shadow_res_index_]));
}
if (HasCvar("monitor")) {
monitor_index_ = std::clamp(monitor_index_, 0, static_cast<int>(kMonitorLabels.size()) - 1);
rex::cvar::SetFlagByName("monitor", std::to_string(monitor_index_));
@ -1384,6 +1439,103 @@ void SimpleSettingsDialog::BuildRows(std::vector<RowSpec>& rows, int category) {
};
rows.push_back(std::move(row));
}
if (HasCvar("skate3_native_render_scene_shadow_static_casters")) {
RowSpec row;
row.kind = RowSpec::kEnum;
row.label = "Enhanced Shadows";
row.desc =
"Real-time sun shadows cast by buildings, trees and props from a "
"dedicated sun-aligned shadow map; the original game only bakes "
"these into the world. Also shades the volumetric sun shafts. "
"Applies immediately.";
row.options = {"Off", "On"};
row.flag = &static_shadows_;
row.on_enum_change = [this](int value) {
SetBoolCvar("skate3_native_render_scene_shadow_static_casters",
value != 0);
SaveSimpleSettingsConfig(config_path_);
};
row.reset = [this] {
static_shadows_ = CvarDefaultBool(
"skate3_native_render_scene_shadow_static_casters", true);
SetBoolCvar("skate3_native_render_scene_shadow_static_casters",
static_shadows_);
SaveSimpleSettingsConfig(config_path_);
};
rows.push_back(std::move(row));
}
if (HasStaticShadowCvars()) {
RowSpec row;
row.kind = RowSpec::kEnum;
row.label = "Enhanced Shadow Resolution";
row.desc =
"Resolution per cascade of the enhanced shadow map. Higher is "
"sharper at distance but uses more video memory (roughly 50 MB "
"at 2048, 200 MB at 4096, 800 MB at 8192).";
for (const char* label : kStaticShadowResLabels) {
row.options.push_back(label);
}
row.index = &static_shadow_res_index_;
row.reset = [this] {
static_shadow_res_index_ = StaticShadowResIndexFrom(int32_t(
CvarDefaultDouble("skate3_native_render_scene_shadow_static_size",
4096.0)));
};
rows.push_back(std::move(row));
}
if (HasCvar("skate3_native_render_scene_shadow_static_strength")) {
RowSpec row;
row.kind = RowSpec::kSlider;
row.label = "Enhanced Shadow Strength";
row.desc =
"How dark the enhanced shadows get (1 matches the game's dynamic "
"shadows). Lower values blend them with the baked lighting where "
"the two disagree. Applies immediately.";
row.value = &static_shadow_strength_;
row.min = 0.0f;
row.max = 1.0f;
row.step = 0.05f;
row.fmt = "%.2f";
row.on_value_change = [this] {
static_shadow_strength_ =
std::clamp(static_shadow_strength_, 0.0f, 1.0f);
rex::cvar::SetFlagByName(
"skate3_native_render_scene_shadow_static_strength",
std::to_string(static_shadow_strength_));
};
row.reset = [this] {
static_shadow_strength_ = float(CvarDefaultDouble(
"skate3_native_render_scene_shadow_static_strength", 1.0));
rex::cvar::SetFlagByName(
"skate3_native_render_scene_shadow_static_strength",
std::to_string(static_shadow_strength_));
SaveSimpleSettingsConfig(config_path_);
};
rows.push_back(std::move(row));
}
if (HasCvar("skate3_native_render_scene_shadow_pcss")) {
RowSpec row;
row.kind = RowSpec::kEnum;
row.label = "Soft Shadows";
row.desc =
"Contact-hardening soft shadows: crisp where a shadow meets its "
"caster, progressively softer with distance, following the sun's "
"angular size. Off uses fixed-width filtering. Applies "
"immediately.";
row.options = {"Off", "On"};
row.flag = &shadow_pcss_;
row.on_enum_change = [this](int value) {
SetBoolCvar("skate3_native_render_scene_shadow_pcss", value != 0);
SaveSimpleSettingsConfig(config_path_);
};
row.reset = [this] {
shadow_pcss_ =
CvarDefaultBool("skate3_native_render_scene_shadow_pcss", true);
SetBoolCvar("skate3_native_render_scene_shadow_pcss", shadow_pcss_);
SaveSimpleSettingsConfig(config_path_);
};
rows.push_back(std::move(row));
}
if (HasCvar("skate3_native_render_scene_ssao")) {
RowSpec row;
row.kind = RowSpec::kEnum;