feat(render): implement Campaign AR and terrain fidelity

This commit is contained in:
Erik 2026-08-22 13:13:29 +02:00
parent 99cf26e00c
commit 7a5f96ede5
368 changed files with 50611 additions and 950 deletions

View file

@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.Numerics;
using AcDream.Core.Content;
using AcDream.Core.World;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Enums;
using DatReaderWriter.Lib.IO;
using DatReaderWriter.Types;
using Xunit;
@ -126,6 +128,55 @@ public sealed class SkyDescLoaderTests
Assert.Equal(0x01004C44u, obj.GfxObjId);
Assert.Equal(0x3300042Cu, obj.PesObjectId);
Assert.True(obj.IsPostScene);
Assert.Equal(Vector3.Zero, obj.AuthoredSortCenter);
}
[Fact]
public void LoadFromRegion_WithDatSourceCarriesDefaultAndReplacementSortCenters()
{
const uint defaultId = 0x01001348u;
const uint replacementId = 0x01001F6Au;
Vector3 defaultCenter = new(1050f, 0f, 0f);
Vector3 replacementCenter = new(2066.82f, 552.99f, 0f);
Region region = MakeRegion(dirBright: 1f, rBgrOrder: 255);
DayGroup group = region.SkyInfo!.DayGroups[0];
group.SkyObjects.Add(new SkyObject
{
DefaultGfxObjectId = defaultId,
});
group.SkyTime[0].SkyObjReplace.Add(new SkyObjectReplace
{
ObjectIndex = 0,
GfxObjId = replacementId,
});
var dats = new FakeDatObjectSource();
dats.Add(defaultId, new GfxObj { SortCenter = defaultCenter });
dats.Add(replacementId, new GfxObj { SortCenter = replacementCenter });
LoadedSkyDesc loaded = Assert.IsType<LoadedSkyDesc>(
SkyDescLoader.LoadFromRegion(region, dats));
Assert.Equal(defaultCenter,
Assert.Single(loaded.DayGroups[0].SkyObjects).AuthoredSortCenter);
Assert.Equal(replacementCenter,
Assert.Single(loaded.DayGroups[0].SkyTimes[0].Replaces).AuthoredSortCenter);
}
[Fact]
public void LoadFromRegion_FailedOptionalSortCenterLookupCannotFailSkyLoading()
{
Region region = MakeRegion(dirBright: 1f, rBgrOrder: 255);
region.SkyInfo!.DayGroups[0].SkyObjects.Add(new SkyObject
{
DefaultGfxObjectId = 0x01001348u,
});
LoadedSkyDesc loaded = Assert.IsType<LoadedSkyDesc>(
SkyDescLoader.LoadFromRegion(region, new ThrowingDatObjectSource()));
Assert.Equal(
Vector3.Zero,
Assert.Single(loaded.DayGroups[0].SkyObjects).AuthoredSortCenter);
}
[Fact]
@ -201,4 +252,31 @@ public sealed class SkyDescLoaderTests
// At begin → begin angle.
Assert.Equal(0f, obj.CurrentAngle(0.25f), precision: 2);
}
private sealed class FakeDatObjectSource : IDatObjectSource
{
private readonly Dictionary<uint, IDBObj> _objects = [];
internal void Add(uint id, IDBObj value) => _objects[id] = value;
public T Get<T>(uint fileId) where T : IDBObj =>
_objects.TryGetValue(fileId, out IDBObj? value) && value is T typed
? typed
: default!;
public bool TryGet<T>(uint fileId, out T value) where T : IDBObj
{
value = Get<T>(fileId);
return value is not null;
}
}
private sealed class ThrowingDatObjectSource : IDatObjectSource
{
public T Get<T>(uint fileId) where T : IDBObj =>
throw new InvalidOperationException("synthetic DAT lookup failure");
public bool TryGet<T>(uint fileId, out T value) where T : IDBObj =>
throw new InvalidOperationException("synthetic DAT lookup failure");
}
}