feat(render): implement Campaign AR and terrain fidelity
This commit is contained in:
parent
99cf26e00c
commit
7a5f96ede5
368 changed files with 50611 additions and 950 deletions
|
|
@ -14,6 +14,117 @@ public enum ParticleRange
|
|||
Extended = 1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Immutable, value-equal user overrides for one selected render pack. Keys
|
||||
/// are stable setting IDs and compare case-insensitively; values remain the
|
||||
/// declaration's invariant string representation until descriptor validation.
|
||||
/// </summary>
|
||||
public sealed class RenderPackSettingOverrides :
|
||||
IReadOnlyDictionary<string, string>,
|
||||
IEquatable<RenderPackSettingOverrides>
|
||||
{
|
||||
private readonly SortedDictionary<string, string> _values;
|
||||
|
||||
public static RenderPackSettingOverrides Empty { get; } = new([]);
|
||||
|
||||
public RenderPackSettingOverrides(
|
||||
IEnumerable<KeyValuePair<string, string>> values)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(values);
|
||||
_values = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach ((string key, string value) in values)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(key);
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
_values[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Count => _values.Count;
|
||||
|
||||
public IEnumerable<string> Keys => _values.Keys;
|
||||
|
||||
public IEnumerable<string> Values => _values.Values;
|
||||
|
||||
public string this[string key] => _values[key];
|
||||
|
||||
public bool ContainsKey(string key) => _values.ContainsKey(key);
|
||||
|
||||
public bool TryGetValue(string key, out string value) =>
|
||||
_values.TryGetValue(key, out value!);
|
||||
|
||||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() =>
|
||||
_values.GetEnumerator();
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() =>
|
||||
GetEnumerator();
|
||||
|
||||
public RenderPackSettingOverrides Set(string settingId, string value)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(settingId);
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
var next = new SortedDictionary<string, string>(
|
||||
_values,
|
||||
StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
[settingId] = value,
|
||||
};
|
||||
return new RenderPackSettingOverrides(next);
|
||||
}
|
||||
|
||||
public bool Equals(RenderPackSettingOverrides? other) =>
|
||||
other is not null
|
||||
&& _values.Count == other._values.Count
|
||||
&& _values.All(pair => other._values.TryGetValue(pair.Key, out string? value)
|
||||
&& string.Equals(pair.Value, value, StringComparison.Ordinal));
|
||||
|
||||
public override bool Equals(object? obj) =>
|
||||
obj is RenderPackSettingOverrides other && Equals(other);
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCode();
|
||||
foreach ((string key, string value) in _values)
|
||||
{
|
||||
hash.Add(key, StringComparer.OrdinalIgnoreCase);
|
||||
hash.Add(value, StringComparer.Ordinal);
|
||||
}
|
||||
return hash.ToHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stable, user-authored selection of one optional render pack. Logical ids
|
||||
/// are persisted instead of menu indexes so discovery order can never select a
|
||||
/// different pack or preset after an install/update. The renderer normalizes a
|
||||
/// missing, malformed, unavailable, or incompatible selection back to
|
||||
/// <see cref="Retail"/> and retains the precise reason for diagnostics.
|
||||
/// </summary>
|
||||
public sealed record RenderPackSelectionSettings(
|
||||
string PackId,
|
||||
string? PackVersion,
|
||||
string PresetId)
|
||||
{
|
||||
public const string RetailPackId = "retail";
|
||||
public const string RetailPresetId = "off";
|
||||
|
||||
public static RenderPackSelectionSettings Retail { get; } = new(
|
||||
RetailPackId,
|
||||
PackVersion: null,
|
||||
RetailPresetId);
|
||||
|
||||
/// <summary>
|
||||
/// User-authored values keyed by the selected pack's stable setting IDs.
|
||||
/// Empty by default so pre-render-pack settings files upgrade without a
|
||||
/// migration write.
|
||||
/// </summary>
|
||||
public RenderPackSettingOverrides SettingOverrides { get; init; } =
|
||||
RenderPackSettingOverrides.Empty;
|
||||
|
||||
public bool IsRetail =>
|
||||
string.Equals(PackId, RetailPackId, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display-related preferences persisted to <c>settings.json</c>.
|
||||
/// Originally documented as "no retail equivalent for FOV / vsync etc" —
|
||||
|
|
@ -77,6 +188,16 @@ public sealed record DisplaySettings(
|
|||
bool BuildingDetailTextures = true,
|
||||
bool MultiPassAlpha = false)
|
||||
{
|
||||
/// <summary>
|
||||
/// Opt-in graphics enhancement selection. This is deliberately separate
|
||||
/// from <see cref="Quality"/>, which remains the authoritative retail
|
||||
/// renderer/streaming quality preset. Keeping the default here means an
|
||||
/// upgraded settings file that predates shader packs deserializes to the
|
||||
/// exact retail path without a migration write.
|
||||
/// </summary>
|
||||
public RenderPackSelectionSettings RenderPack { get; init; } =
|
||||
RenderPackSelectionSettings.Retail;
|
||||
|
||||
/// <summary>Values used on first launch / when settings.json is absent.
|
||||
/// Geometry defaults preserve the pre-L.0 runtime state: Resolution
|
||||
/// matches the WindowOptions startup size (1280×720). FieldOfView is
|
||||
|
|
|
|||
|
|
@ -97,7 +97,10 @@ public sealed class SettingsStore
|
|||
TextureFiltering: ReadInt (disp, "textureFiltering", d.TextureFiltering),
|
||||
LandscapeDrawDistance: ReadInt (disp, "landscapeDrawDistance", d.LandscapeDrawDistance),
|
||||
BuildingDetailTextures: ReadBool (disp, "buildingDetailTextures", d.BuildingDetailTextures),
|
||||
MultiPassAlpha: ReadBool (disp, "multiPassAlpha", d.MultiPassAlpha));
|
||||
MultiPassAlpha: ReadBool (disp, "multiPassAlpha", d.MultiPassAlpha))
|
||||
{
|
||||
RenderPack = ReadRenderPackSelection(disp, d.RenderPack),
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -686,6 +689,7 @@ public sealed class SettingsStore
|
|||
["multiPassAlpha"] = d.MultiPassAlpha,
|
||||
["particleRange"] = d.ParticleRange.ToString(),
|
||||
["quality"] = d.Quality.ToString(),
|
||||
["renderPack"] = BuildRenderPackObject(d.RenderPack),
|
||||
["resolution"] = d.Resolution,
|
||||
["screenBrightness"] = d.ScreenBrightness,
|
||||
["showFps"] = d.ShowFps,
|
||||
|
|
@ -693,6 +697,21 @@ public sealed class SettingsStore
|
|||
["vsync"] = d.VSync,
|
||||
};
|
||||
|
||||
private static SortedDictionary<string, object?> BuildRenderPackObject(
|
||||
RenderPackSelectionSettings selection)
|
||||
{
|
||||
var overrides = new SortedDictionary<string, object>(StringComparer.Ordinal);
|
||||
foreach ((string key, string value) in selection.SettingOverrides)
|
||||
overrides[key] = value;
|
||||
return new SortedDictionary<string, object?>(StringComparer.Ordinal)
|
||||
{
|
||||
["packId"] = selection.PackId,
|
||||
["packVersion"] = selection.PackVersion,
|
||||
["presetId"] = selection.PresetId,
|
||||
["settingOverrides"] = overrides,
|
||||
};
|
||||
}
|
||||
|
||||
private static SortedDictionary<string, object> BuildMiscObject(MiscSettings m)
|
||||
=> new(StringComparer.Ordinal)
|
||||
{
|
||||
|
|
@ -777,6 +796,49 @@ public sealed class SettingsStore
|
|||
File.WriteAllText(_path, sb.ToString());
|
||||
}
|
||||
|
||||
private static RenderPackSelectionSettings ReadRenderPackSelection(
|
||||
JsonElement display,
|
||||
RenderPackSelectionSettings fallback)
|
||||
{
|
||||
if (!display.TryGetProperty("renderPack", out JsonElement value)
|
||||
|| value.ValueKind != JsonValueKind.Object)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
string packId = ReadString(value, "packId", string.Empty);
|
||||
string presetId = ReadString(value, "presetId", string.Empty);
|
||||
if (string.IsNullOrWhiteSpace(packId) || string.IsNullOrWhiteSpace(presetId))
|
||||
return fallback;
|
||||
string? version = null;
|
||||
if (value.TryGetProperty("packVersion", out JsonElement versionElement)
|
||||
&& versionElement.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
version = versionElement.GetString();
|
||||
}
|
||||
|
||||
var overrides = new List<KeyValuePair<string, string>>();
|
||||
if (value.TryGetProperty("settingOverrides", out JsonElement overrideElement))
|
||||
{
|
||||
if (overrideElement.ValueKind != JsonValueKind.Object)
|
||||
return fallback;
|
||||
foreach (JsonProperty property in overrideElement.EnumerateObject())
|
||||
{
|
||||
if (property.Value.ValueKind != JsonValueKind.String
|
||||
|| property.Value.GetString() is not { } settingValue)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
overrides.Add(new KeyValuePair<string, string>(property.Name, settingValue));
|
||||
}
|
||||
}
|
||||
|
||||
return new RenderPackSelectionSettings(packId, version, presetId)
|
||||
{
|
||||
SettingOverrides = new RenderPackSettingOverrides(overrides),
|
||||
};
|
||||
}
|
||||
|
||||
private static string ReadString(JsonElement obj, string name, string fallback)
|
||||
=> obj.TryGetProperty(name, out var el) && el.ValueKind == JsonValueKind.String
|
||||
? (el.GetString() ?? fallback) : fallback;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue