checkpoint: preserve cathedral look-in investigation state

This commit is contained in:
Erik 2026-08-30 23:29:28 +02:00
parent c287db8651
commit 572de1ec30
11 changed files with 757 additions and 197 deletions

View file

@ -3,12 +3,23 @@ using AcDream.App.Rendering;
using AcDream.App.Rendering.Scene;
using AcDream.App.Rendering.Scene.Arch;
using AcDream.App.Rendering.Wb;
using AcDream.App.Rendering.Walk;
using AcDream.Core.World;
namespace AcDream.App.Tests.Rendering;
public sealed class RenderScenePViewFrameProductTests
{
private sealed class RejectAllLookInViews(uint cellId) : IWalkLookInViewSource
{
public IReadOnlyList<uint> LookInCellTurns { get; } = [cellId];
public bool SphereVisibleInLookInTurn(
int routeIndex,
in Vector3 center,
float radius) => false;
}
private const uint Landblock = 0xA9B4FFFF;
private const uint Cell = 0xA9B40170;
private static readonly RenderSceneGeneration Generation =
@ -159,6 +170,7 @@ public sealed class RenderScenePViewFrameProductTests
clip,
viewcone,
[],
null,
[Cell],
EmptyCellSource.Instance,
[],
@ -168,6 +180,134 @@ public sealed class RenderScenePViewFrameProductTests
}
}
[Fact]
public void Builder_KeysLookInDynamicsToWalkTurnsAndExcludesThemFromDynamicLast()
{
const uint lookInCell = 0xA9B4_0171;
using var scene = new ArchRenderScene(Generation);
RenderProjectionRecord rootDynamic = Record(
0x0100_0000_0000_0021,
RenderProjectionClass.LiveDynamicRoot,
parentCell: Cell);
RenderProjectionRecord lookInDynamic = Record(
0x0100_0000_0000_0022,
RenderProjectionClass.LiveDynamicRoot,
parentCell: lookInCell);
scene.Apply(
[
RenderProjectionDelta.Register(Generation, 1, rootDynamic),
RenderProjectionDelta.Register(Generation, 2, lookInDynamic),
]);
PortalVisibilityFrame portal = Portal(Cell);
ClipFrameAssembly clip = FullScreenClip(Cell);
ViewconeCuller viewcone = ViewconeCuller.Build(clip, Matrix4x4.Identity);
RenderSceneQuery query = scene.OpenQuery();
var input = new RenderScenePViewBuildInput(
query,
new RenderSceneDigest(query.Generation, query.Counts, default),
portal,
clip,
viewcone,
LookInCellTurns: [lookInCell],
WalkLookInViews: null,
DrawableCells: [Cell, lookInCell],
Cells: EmptyCellSource.Instance,
AnimatedEntityIds: [],
RootIsOutdoor: false,
RootFloodCells: new HashSet<uint> { Cell });
var exchange = new RenderFrameExchange();
var builder = new RenderScenePViewFrameBuilder();
builder.Build(exchange, frameSequence: 1, in input);
RenderFrameView frame = exchange.BorrowLatest(Generation, 1);
try
{
RenderFrameCandidateRange lookInRange = Assert.Single(
frame.RouteRanges.ToArray(),
range => range.Route == RenderFrameCandidateRoute.LookInObject);
Assert.Equal(0, lookInRange.RouteIndex);
Assert.Equal(lookInCell, lookInRange.CellId);
Assert.Equal(
[lookInDynamic.Id],
frame.RouteCandidates
.Slice(lookInRange.Offset, lookInRange.Count)
.ToArray()
.Select(static record => record.Id));
RenderFrameCandidateRange dynamicLast = Assert.Single(
frame.RouteRanges.ToArray(),
range => range.Route == RenderFrameCandidateRoute.DynamicLast);
RenderProjectionId[] lastIds = frame.RouteCandidates
.Slice(dynamicLast.Offset, dynamicLast.Count)
.ToArray()
.Select(static record => record.Id)
.ToArray();
Assert.Contains(rootDynamic.Id, lastIds);
Assert.DoesNotContain(lookInDynamic.Id, lastIds);
}
finally
{
exchange.Release(in frame);
}
}
[Fact]
public void Builder_CarriesExactWalkConeForPerPartDispatcherAdmission()
{
const uint lookInCell = 0xA9B4_0171;
using var scene = new ArchRenderScene(Generation);
RenderProjectionRecord lookInDynamic = Record(
0x0100_0000_0000_0022,
RenderProjectionClass.LiveDynamicRoot,
parentCell: lookInCell);
scene.Apply([RenderProjectionDelta.Register(Generation, 1, lookInDynamic)]);
PortalVisibilityFrame portal = Portal(Cell);
ClipFrameAssembly clip = FullScreenClip(Cell);
ViewconeCuller viewcone = ViewconeCuller.Build(clip, Matrix4x4.Identity);
RenderSceneQuery query = scene.OpenQuery();
var input = new RenderScenePViewBuildInput(
query,
new RenderSceneDigest(query.Generation, query.Counts, default),
portal,
clip,
viewcone,
LookInCellTurns: [lookInCell],
WalkLookInViews: new RejectAllLookInViews(lookInCell),
DrawableCells: [Cell, lookInCell],
Cells: EmptyCellSource.Instance,
AnimatedEntityIds: [],
RootIsOutdoor: false,
RootFloodCells: new HashSet<uint> { Cell });
var exchange = new RenderFrameExchange();
var builder = new RenderScenePViewFrameBuilder();
builder.Build(exchange, frameSequence: 1, in input);
RenderFrameView frame = exchange.BorrowLatest(Generation, 1);
try
{
RenderFrameCandidateRange range = Assert.Single(
frame.RouteRanges.ToArray(),
candidate => candidate.Route == RenderFrameCandidateRoute.LookInObject);
Assert.Equal(
[lookInDynamic.Id],
frame.RouteCandidates
.Slice(range.Offset, range.Count)
.ToArray()
.Select(static record => record.Id));
Assert.NotNull(frame.WalkLookInViews);
Assert.False(frame.WalkLookInViews!.SphereVisibleInLookInTurn(
routeIndex: 0,
center: Vector3.Zero,
radius: 1f));
}
finally
{
exchange.Release(in frame);
}
}
[Fact]
public void Controller_MatchesCurrentPViewThenNamesAWithdrawnCandidate()
{