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

@ -2,6 +2,7 @@ using System.Numerics;
using System.Reflection;
using AcDream.App.Composition;
using AcDream.App.Rendering;
using AcDream.App.Rendering.Packs;
using AcDream.App.Rendering.Selection;
using AcDream.App.Rendering.Vfx;
using AcDream.App.Streaming;
@ -15,6 +16,94 @@ namespace AcDream.App.Tests.Rendering;
public sealed class WorldSceneRendererTests
{
[Fact]
public void EnhancedPreparation_BuildsCanonicalWorldOnceBeforeExecution()
{
var rig = new Rig(portalVisible: false, waitingForLogin: false, clipRoot: null);
PreparedWorldSceneFrame prepared = rig.Renderer.PrepareEnhanced(default);
WorldRenderFrameOutcome result = rig.Renderer.RenderPreparedEnhanced(
default,
in prepared);
Assert.True(result.NormalWorldDrawn);
Assert.True(prepared.ShouldRender);
Assert.Equal(1, rig.Calls.Count(value => value == "frame:build"));
Assert.True(rig.Calls.IndexOf("frame:build") < rig.Calls.IndexOf("selection:begin"));
Assert.Equal(prepared.World.Camera.Frustum, rig.Selection.PreparedViewFrustum);
Assert.Equal(rig.Entities.ResidentWindow, prepared.World.ResidentStreamingWindow);
Assert.Equal((0, 0), rig.Entities.LastResidentWindowCenter);
}
[Fact]
public void EnhancedPreparation_AttachesTheSelectedAuthoredCelestialSource()
{
var rig = new Rig(portalVisible: false, waitingForLogin: false, clipRoot: null);
rig.DayGroup.SkyObjects =
[
new SkyObjectData
{
GfxObjId = AuthoredCelestialShadowSourceResolver.SunGfxObjId,
AuthoredSortCenter = Vector3.UnitX,
BeginTime = 0f,
EndTime = 0f,
BeginAngle = 90f,
EndAngle = 90f,
},
];
PreparedWorldSceneFrame prepared = rig.Renderer.PrepareEnhanced(default);
Assert.True(prepared.ShouldRender);
Assert.Equal(
AuthoredCelestialShadowSourceKind.Sun,
prepared.World.CelestialShadowSource.Kind);
Assert.Equal(0, prepared.World.CelestialShadowSource.ObjectIndex);
Assert.Equal(
AuthoredCelestialShadowSourceResolver.SunGfxObjId,
prepared.World.CelestialShadowSource.GfxObjId);
Assert.InRange(
Vector3.Distance(
Vector3.UnitZ,
prepared.World.CelestialShadowSource.SurfaceToLightDirection),
0f,
1e-5f);
rig.Renderer.CancelPreparedEnhanced(in prepared);
}
[Fact]
public void EnhancedPreparation_SkippedWorldStillPublishesEmptySelectionFrame()
{
var rig = new Rig(portalVisible: true, waitingForLogin: false, clipRoot: null);
PreparedWorldSceneFrame prepared = rig.Renderer.PrepareEnhanced(default);
WorldRenderFrameOutcome result = rig.Renderer.RenderPreparedEnhanced(
default,
in prepared);
Assert.False(prepared.ShouldRender);
Assert.Equal(default, result);
Assert.Equal(["selection:begin", "selection:complete"], rig.Calls);
}
[Fact]
public void CancelledEnhancedPrepass_DoesNotPoisonTheNextFrame()
{
var rig = new Rig(portalVisible: false, waitingForLogin: false, clipRoot: null);
PreparedWorldSceneFrame failed = rig.Renderer.PrepareEnhanced(default);
// Models an exception in the shadow prepass before a world pass opens.
rig.Renderer.CancelPreparedEnhanced(in failed);
PreparedWorldSceneFrame recovered = rig.Renderer.PrepareEnhanced(default);
WorldRenderFrameOutcome result = rig.Renderer.RenderPreparedEnhanced(
default,
in recovered);
Assert.True(result.NormalWorldDrawn);
Assert.Equal(2, rig.Calls.Count(value => value == "frame:build"));
}
[Fact]
public void PortalViewport_PublishesEmptySelectionFrameAndSkipsWorldOwners()
{
@ -445,20 +534,21 @@ public sealed class WorldSceneRendererTests
clipRoot,
playerSeenOutside ?? clipRoot is not null);
Frames = new FrameBuilder(Calls, frame);
var selection = new SelectionFrame(Calls);
Selection = new SelectionFrame(Calls);
var alpha = new AlphaFrame(Calls);
var visibility = new ParticleVisibility(Calls);
PView = new PViewRenderer(Calls);
Passes = new PassExecutor(Calls);
var diagnostics = new Diagnostics(Calls);
Entities = new EntitySource();
Renderer = new WorldSceneRenderer(
foundation,
login,
sky,
Frames,
new EntitySource(),
selection,
Entities,
Selection,
alpha,
visibility,
PView,
@ -479,6 +569,10 @@ public sealed class WorldSceneRendererTests
public FrameBuilder Frames { get; }
public EntitySource Entities { get; }
public SelectionFrame Selection { get; }
public PViewRenderer PView { get; }
public PassExecutor Passes { get; }
@ -535,6 +629,16 @@ public sealed class WorldSceneRendererTests
private sealed class EntitySource : IWorldSceneEntitySource
{
public ResidentStreamingWindowFact ResidentWindow { get; } = new(
Revision: 77,
CenterX: 0,
CenterY: 0,
CompleteRadiusLandblocks: 2,
PublishedLandblockCount: 25,
HasPublishedCenter: true);
public (int X, int Y)? LastResidentWindowCenter { get; private set; }
public IReadOnlyList<(uint LandblockId, Vector3 AabbMin, Vector3 AabbMax,
IReadOnlyList<WorldEntity> Entities,
IReadOnlyDictionary<uint, WorldEntity>? AnimatedById)> LandblockEntries =>
@ -543,11 +647,25 @@ public sealed class WorldSceneRendererTests
public IReadOnlyList<(uint LandblockId, Vector3 AabbMin, Vector3 AabbMax)> LandblockBounds =>
Array.Empty<(uint, Vector3, Vector3)>();
public ResidentStreamingWindowFact CaptureResidentStreamingWindow(
int centerX,
int centerY)
{
LastResidentWindowCenter = (centerX, centerY);
return ResidentWindow;
}
}
private sealed class SelectionFrame(List<string> calls) : IWorldSceneSelectionFrame
{
public void BeginFrame() => calls.Add("selection:begin");
public FrustumPlanes? PreparedViewFrustum { get; private set; }
public void BeginFrame(FrustumPlanes? preparedViewFrustum = null)
{
PreparedViewFrustum = preparedViewFrustum;
calls.Add("selection:begin");
}
public void CompleteFrame() => calls.Add("selection:complete");