checkpoint: preserve cathedral look-in investigation state
This commit is contained in:
parent
c287db8651
commit
572de1ec30
11 changed files with 757 additions and 197 deletions
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -67,6 +67,18 @@ public sealed class WalkFrameDriverTests
|
|||
sorted.Sort();
|
||||
log.Add($"PARTICLES:{string.Join(",", sorted.ConvertAll(o => o.ToString("x")))}");
|
||||
}
|
||||
|
||||
public void DrawLookInDynamics(
|
||||
uint cellId,
|
||||
int routeIndex,
|
||||
IReadOnlySet<uint> staticParticleOwnerIds)
|
||||
{
|
||||
var sorted = new List<uint>(staticParticleOwnerIds);
|
||||
sorted.Sort();
|
||||
log.Add(
|
||||
$"LOOKIN:{cellId:x8}:{routeIndex}:"
|
||||
+ string.Join(",", sorted.ConvertAll(o => o.ToString("x"))));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class RecordingTrace(List<string> log) : IWalkFrameDriverTrace
|
||||
|
|
@ -417,10 +429,28 @@ public sealed class WalkFrameDriverTests
|
|||
new[]
|
||||
{
|
||||
"ALPHA:12.50", "PUNCH:4@v0", "SHELL:00000104",
|
||||
"FLUSH:1:LookInStatic", "FLUSH:1:BuildingShell", "PARTICLES:c9",
|
||||
"FLUSH:1:LookInStatic", "LOOKIN:00000104:0:ca",
|
||||
"FLUSH:1:BuildingShell", "PARTICLES:c9",
|
||||
},
|
||||
log);
|
||||
|
||||
Assert.Equal([0x104u], driver.LookInCellTurns);
|
||||
Assert.Equal([0x104u], driver.LookInCells);
|
||||
WalkPortalView capturedView = ctx.Cells[0x104].PortalViews[0];
|
||||
WalkViewPoly capturedPoly = Assert.Single(capturedView.View.Polys);
|
||||
Vector2 capturedCenter = Vector2.Zero;
|
||||
for (int edge = 0; edge < capturedPoly.VertexCount; edge++)
|
||||
{
|
||||
capturedCenter += capturedView.View.Vertices[
|
||||
capturedPoly.VertexIndex + edge].Point;
|
||||
}
|
||||
capturedCenter /= capturedPoly.VertexCount;
|
||||
Vector3 insideCone = ctx.Rays.RayThrough(capturedCenter.X, capturedCenter.Y);
|
||||
Assert.True(driver.SphereVisibleInLookInTurn(
|
||||
0, in insideCone, 0.1f));
|
||||
Assert.False(driver.SphereVisibleInLookInTurn(
|
||||
0, new Vector3(10_000f, 0f, 10f), 0.1f));
|
||||
|
||||
// The punch polygon reached the leaf renderer in WORLD space: the
|
||||
// building-local Quad(-2f) vertex (-0.5,-0.5,-2) translates by
|
||||
// (10,0,0) under the caller-supplied building world transform.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Rendering.Walk;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using DatReaderWriter.Types;
|
||||
|
||||
namespace AcDream.App.Tests.Rendering.Wb;
|
||||
|
||||
public sealed class WbDrawDispatcherLookInConeTests
|
||||
{
|
||||
[Fact]
|
||||
public void DrawingSphereAdmissionUsesTheIndividualPartTransformAndLargestScale()
|
||||
{
|
||||
var cone = new RecordingLookInViews();
|
||||
var sphere = new Sphere
|
||||
{
|
||||
Origin = new Vector3(1f, 2f, 3f),
|
||||
Radius = 0.5f,
|
||||
};
|
||||
Matrix4x4 transform =
|
||||
Matrix4x4.CreateScale(2f, 3f, 4f)
|
||||
* Matrix4x4.CreateTranslation(10f, 20f, 30f);
|
||||
|
||||
bool visible = WbDrawDispatcher.LookInDrawingSphereVisible(
|
||||
cone,
|
||||
routeIndex: 7,
|
||||
sphere,
|
||||
transform,
|
||||
out Vector3 center,
|
||||
out float radius);
|
||||
|
||||
Assert.True(visible);
|
||||
Assert.Equal(7, cone.RouteIndex);
|
||||
Assert.Equal(new Vector3(12f, 26f, 42f), center);
|
||||
Assert.Equal(center, cone.Center);
|
||||
Assert.Equal(2f, radius);
|
||||
Assert.Equal(radius, cone.Radius);
|
||||
}
|
||||
|
||||
private sealed class RecordingLookInViews : IWalkLookInViewSource
|
||||
{
|
||||
public IReadOnlyList<uint> LookInCellTurns { get; } = [0xF4180112u];
|
||||
|
||||
public int RouteIndex { get; private set; }
|
||||
|
||||
public Vector3 Center { get; private set; }
|
||||
|
||||
public float Radius { get; private set; }
|
||||
|
||||
public bool SphereVisibleInLookInTurn(
|
||||
int routeIndex,
|
||||
in Vector3 center,
|
||||
float radius)
|
||||
{
|
||||
RouteIndex = routeIndex;
|
||||
Center = center;
|
||||
Radius = radius;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue