Round 1 (2bd353234) was three-lens reviewed: retail-faithful PASS,
production PASS, gate-honesty FAIL. Every finding landed on the
terrace-edge KnownFailure row plus one pin gap. This is the last round.
R2-1 - gate honesty. WalkTraceConformanceTests.AlphaDepthTranscript.cs's
own KnownFailure doc comment claimed "the PC sequence matches exactly";
it never did - Assert.Equal(expected.PmEvents, actualPm) threw first, so
the PC Assert.Equal line never ran. RunAlphaDepthTranscriptGate now
computes BOTH diffs (SequenceEqual, not Assert.Equal) before asserting
either, and Assert.Fail's message prints both sequences together
whenever either diverges - a written claim about one sequence is now
only ever one this method actually evaluated. Re-running the OLD
(unseeded, cold PortalsDrawnCount=0) code path through this new combined
assertion shows terrace-edge's real divergence was WORSE than round 1
believed: both PM and PC diverged (PC expected counter=2, actual
counter=0) - round 1's "PC matches" claim was not just untested, it was
false.
R2-2 - the harness fix. Retail's portalsDrawnCount (wo(008719b4)) is a
PERSISTENT session global; each capture's own FIRST PM or PC line
(cathedral-arrival 0, cathedral-leak 0, foundry-deep 1,
holtburg-doorway-still 2, terrace-edge 2, cathedral-stair-arch 8) already
carries the pre-capture value - terrace-edge's own sample sits in the
"F 1" preamble text a cdb session prints before the first parsed frame
marker (line 1209, ahead of "F 1" at line 1298), so no amount of
replaying the CAPTURED frames from a cold start can ever reach it (this
fixture's own turns never touch the counter - every PC line reads
ov=0). New WalkAlphaDepthTrace.LoadInitialCounter(root, fixtureName)
scans a fixture's raw lines, ignoring frame boundaries, for the first
PM/PC match and returns its counter. RunAlphaDepthTranscriptGate seeds
WalkFrameDriver.PortalsDrawnCount (already an internal, test-assembly-
settable field) from this value, runs ONE Collect+Replay pass, and
deletes the two-pass "priming" replay outright. Result: terrace-edge
turns GREEN with no KnownFailure tag; every other pose stays exact
(a fixture with real steady-state seal activity computes the identical
value a priming pass used to converge to, since a steady state is by
definition unchanged by one more pass). No pose diverges after seeding,
so the packet's "write the divergence into section 9" fallback never
triggers - nothing needed there this round.
R2-3 - the SOME-vs-SAME-plane discriminator. T2's existing five cases
never separated retail's real quantifier ("every vertex lies on the SAME
one of the four +/-12 planes") from a weaker "every vertex lies on SOME
plane" (per-vertex OR across the four planes, ANDed across vertices) -
the existing split-plane case's third vertex sits on NO plane at all, so
both readings admit it for the same reason. New case in
WalkVisibilityMathTests.cs
(Boundary_guard_admits_a_polygon_whose_every_vertex_is_on_SOME_plane_but_not_the_SAME_one)
and a fourth sink.OnPunchGeometry call in WalkFrameDriverTests.cs's
OnPunchGeometry_RejectsOnlyWhenEveryVertexSharesOnePlane_ButPunchesAnyOtherShape
pin close the gap: [(12,0,3),(0,12,3),(12,5,3)] - every vertex touches
SOME plane (vertex1/3 on x=+12, vertex2 on y=+12) but no SINGLE plane
holds all three (x=+12 cleared by vertex2's x=0, y=+12 cleared by
vertex1's y=0) - retail's real four-accumulator guard admits it; the
weaker per-vertex-OR form rejects it.
R2-4 - the sixth pose. Cherry-picked 4f12cd06b (walk+alphadepth capture)
and 3a6aa7eef (pmverts capture) from claude/campaign-w-retail-frame-walk
onto 2bd353234 - both applied clean, no conflicts (README.md merged
without a manual row). New
AlphaDepthTranscript_CathedralStairArch_MatchesRetailFrame2 Fact: root
0xF4180114, seven outside views, sixteen mode=1 building punches (all
counterBefore=0008 - far-Z punches never touch the counter) of which
twelve are guard-rejected on local y=12.000, then eight mode=0 exit
seals with counterBefore running 0..7 against the root's own ov=7
read-then-zero, settling this fixture's own steady-state counter at 8 -
exactly the value LoadInitialCounter reads back out of its own preamble.
PASS.
R2-5 - comment truth. (a) RetailPViewPassExecutor.cs:
DrawPortalDepthWrite's and DrawExitPortalMask's summaries corrected -
neither the retired ">=3 vertices" pre-filter nor "always matches what
reached the GPU" describe F2's actual count-before-clip behavior any
more. (b) The F2 pin's doc comment
(RetailPViewPassExecutorTests.DrawExitPortalMask_CountsAnUnclippableTwoVertexPolygon_ButDrawsNothing)
named TWO alternative mutations as if both failed; verified only one
does (see mutations below) - the doc comment now says so explicitly and
keeps only the pre-filter mutation. (c) WalkVisibilityMath.cs's doc
comment stated DAT-scan figures as bare facts; independently re-verified
this round with a freshly-written scan (own code, walking the same
production EnvCell.CellPortals/CellStruct.Polygons shape
WalkCellFactory itself uses, over the full 0x0000-0xFFFF landblock-prefix
range) - every cited figure reproduced bit-exact (3,405 landblocks with
cells; 1,854,237 portal polygons; 2,889 any-vertex; 2,163 all-on-plane,
all exit, 0 interior; 16,939 exit portals total, 1,837,298 interior) -
so the doc comment now states the counting rule (id range, what's
counted, how unresolvable polygons/cells are skipped) alongside every
figure instead of leaving them unqualified; landblocksWithCells=3,405
also independently matches CellStructSurfaceConstructionInstalledDatTests'
own OH2 walk of the identical range (that test's own golden-count
comment). The scratch scan itself is not part of this diff (deleted
after use, same as F1's own datScan). (d) This commit body does not
cite "register tests" as evidence - no test reads the register.
Register truth is the human read: no row changes, AD-119 (the C2
blend-vs-write-mask row) unchanged.
Mutations run this round (all verified against production code, then
reverted):
- R2-1/R2-2 (disable seeding, driver.PortalsDrawnCount = 0 instead of
initialCounter): AlphaDepthTranscript_TerraceEdge_MatchesRetailFrame2
fails - "terrace-edge.alphadepth: PM DIVERGES - expected
[(mode=1,counterBefore=2), (mode=1,counterBefore=2)], actual
[(mode=1,counterBefore=0), (mode=1,counterBefore=0)]; PC DIVERGES -
expected [(ov=0,counter=2,fc=0), (ov=0,counter=2,fc=0)], actual
[(ov=0,counter=0,fc=0), (ov=0,counter=0,fc=0)]" - proving both the
seeding fix (R2-2) and the honest dual-sequence message (R2-1) at
once.
- R2-3 (replace the four per-plane accumulators with a single
per-vertex "on ANY plane" test ANDed across vertices):
Boundary_guard_admits_a_polygon_whose_every_vertex_is_on_SOME_plane_but_not_the_SAME_one
fails, Assert.False() Expected:False Actual:True;
OnPunchGeometry_RejectsOnlyWhenEveryVertexSharesOnePlane_ButPunchesAnyOtherShape
fails, Assert.Equal() Expected:3 Actual:2.
- R2-5b, mutation ONE (restore the old `localVertices.Length < 3`
pre-filter ahead of the guard):
DrawExitPortalMask_CountsAnUnclippableTwoVertexPolygon_ButDrawsNothing
fails, Assert.Equal() Expected:1 Actual:0 - kept as the pin's cited
mutation.
- R2-5b, mutation TWO (move `submitted++` to AFTER
`_portalDepthMask.DrawDepthFan(...)` instead of before): same test
STILL PASSES - DrawDepthFan has no effect on the local `submitted`
counter, so the final returned value is identical regardless of
which side of that call the increment sits on. Confirmed unobservable
exactly as R2-5b required; the doc comment no longer names this as a
failing mutation.
Gates: dotnet build tests/AcDream.App.Tests -c Release -> 0 warnings/0
errors. dotnet build src/AcDream.App -c Release -> 0 warnings/0 errors.
Hermetic lane (Lane!=InstalledDat&Lane!=PreparedPackage&Lane!=Live&
Lane!=Manual&Lane!=Timing&Lane!=Windows&Lane!=Linux&Lane!=SystemFont&
Purpose!=Diagnostic&Status!=KnownFailure): 6830/6830 passed (+1 over
round 1's 6829, the new T2 case). InstalledDat lane
(ACDREAM_RUN_INSTALLED_DAT_TESTS=1, --filter Lane=InstalledDat): 254
total (+1 over round 1's 253, the new sixth Fact), 249 passed, 4 failed -
exactly the pre-existing four (LayoutImporterMediaBearingChildSweepTests
and LayoutImporterInvisibleSweepTests, both #383;
TowerAscentReplayTests' KnownFailure; Oh_doorway_still_first_frame_diff,
#458) plus the pre-existing WalkLookInGateSweepTests skip - terrace-edge
no longer fails, no new divergence anywhere else. All six
AlphaDepthTranscript_*_MatchesRetailFrame2 Facts: 6/6 passed standalone.
Shader classes (VulkanShaderDescriptorContractTests +
VulkanShaderManifestTests + RenderPackSpirvValidatorTests +
SkyVertexLayoutTests): 35/35. RetailPViewPassExecutorTests +
WalkFrameDriverTests (full classes, regression check on the R2-3/R2-5b
edits): 57/57.
Commits: two cherry-picks (4f12cd06b, 3a6aa7eef) from
claude/campaign-w-retail-frame-walk, both clean, plus this one fix
commit on top of 2bd353234.
Register: no new AD row; AD-119 unchanged (human read, not a test -
see R2-5d above).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
324 lines
14 KiB
C#
324 lines
14 KiB
C#
using System.Numerics;
|
|
using AcDream.App.Rendering.Walk;
|
|
|
|
namespace AcDream.App.Tests.Rendering.Walk;
|
|
|
|
public sealed class WalkVisibilityMathTests
|
|
{
|
|
// ---- get_pt_limit @0x0054b840 ----
|
|
|
|
[Fact]
|
|
public void Up_normal_plane_encodes_inside_above_as_negative_height()
|
|
{
|
|
// Plane z >= 5 at the origin column: N=(0,0,1), d=-5 → h=5, inside above.
|
|
var plane = new WalkPlane(new Vector3(0, 0, 1), -5f);
|
|
Assert.Equal(-5f, WalkVisibilityMath.GetPointLimit(0, 0, plane));
|
|
}
|
|
|
|
[Fact]
|
|
public void Up_normal_plane_at_or_above_sky_height_is_outside()
|
|
{
|
|
var plane = new WalkPlane(new Vector3(0, 0, 1), -1000f);
|
|
Assert.Equal(
|
|
WalkVisibilityMath.OutsideColumn,
|
|
WalkVisibilityMath.GetPointLimit(0, 0, plane));
|
|
}
|
|
|
|
[Fact]
|
|
public void Up_normal_plane_with_nonpositive_height_is_wholly_inside()
|
|
{
|
|
var plane = new WalkPlane(new Vector3(0, 0, 1), 3f); // z >= -3
|
|
Assert.Equal(
|
|
WalkVisibilityMath.InsideColumn,
|
|
WalkVisibilityMath.GetPointLimit(0, 0, plane));
|
|
}
|
|
|
|
[Fact]
|
|
public void Down_normal_plane_encodes_inside_below_as_positive_height()
|
|
{
|
|
// Plane z <= 7: N=(0,0,-1), d=7 → h=7, inside below.
|
|
var plane = new WalkPlane(new Vector3(0, 0, -1), 7f);
|
|
Assert.Equal(7f, WalkVisibilityMath.GetPointLimit(0, 0, plane));
|
|
}
|
|
|
|
[Fact]
|
|
public void Down_normal_plane_with_nonpositive_height_is_outside()
|
|
{
|
|
var plane = new WalkPlane(new Vector3(0, 0, -1), -2f); // z <= -2: nothing above ground
|
|
Assert.Equal(
|
|
WalkVisibilityMath.OutsideColumn,
|
|
WalkVisibilityMath.GetPointLimit(0, 0, plane));
|
|
}
|
|
|
|
[Fact]
|
|
public void Vertical_plane_uses_the_side_of_the_ground_point()
|
|
{
|
|
var plane = new WalkPlane(new Vector3(1, 0, 0), -10f); // x >= 10
|
|
Assert.Equal(
|
|
WalkVisibilityMath.OutsideColumn,
|
|
WalkVisibilityMath.GetPointLimit(5f, 0, plane));
|
|
Assert.Equal(
|
|
WalkVisibilityMath.InsideColumn,
|
|
WalkVisibilityMath.GetPointLimit(15f, 0, plane));
|
|
// ON the plane (within epsilon) counts inside.
|
|
Assert.Equal(
|
|
WalkVisibilityMath.InsideColumn,
|
|
WalkVisibilityMath.GetPointLimit(10f, 0, plane));
|
|
}
|
|
|
|
// ---- corner_plane_check @0x0054b930 ----
|
|
|
|
[Theory]
|
|
[InlineData(1001f, 0f, 10f, WalkBoundingType.Outside)] // sentinel outside
|
|
[InlineData(0f, 0f, 10f, WalkBoundingType.EntirelyInside)] // sentinel inside
|
|
[InlineData(-5f, 6f, 10f, WalkBoundingType.EntirelyInside)] // inside above 5; slab [6,10] wholly above
|
|
[InlineData(-5f, 2f, 10f, WalkBoundingType.PartiallyInside)] // slab straddles 5
|
|
[InlineData(-5f, 2f, 4f, WalkBoundingType.Outside)] // slab wholly below 5
|
|
[InlineData(7f, 2f, 6f, WalkBoundingType.EntirelyInside)] // inside below 7; slab wholly below
|
|
[InlineData(7f, 2f, 10f, WalkBoundingType.PartiallyInside)] // slab straddles 7
|
|
[InlineData(7f, 8f, 10f, WalkBoundingType.Outside)] // slab wholly above 7
|
|
public void Corner_check_classifies_the_slab(
|
|
float bound, float minZ, float maxZ, WalkBoundingType expected)
|
|
=> Assert.Equal(expected, WalkVisibilityMath.CornerPlaneCheck(bound, minZ, maxZ));
|
|
|
|
[Fact]
|
|
public void Corner_check_boundary_touch_is_out_on_the_out_side_and_in_on_the_in_side()
|
|
{
|
|
// Retail equality edges (flood appendix report 4): maxZ == h with
|
|
// inside-above rejects; minZ == h with inside-above accepts entirely.
|
|
Assert.Equal(
|
|
WalkBoundingType.Outside,
|
|
WalkVisibilityMath.CornerPlaneCheck(-5f, 2f, 5f));
|
|
Assert.Equal(
|
|
WalkBoundingType.EntirelyInside,
|
|
WalkVisibilityMath.CornerPlaneCheck(-5f, 5f, 10f));
|
|
// Inside-below: minZ == h rejects; maxZ == h accepts entirely.
|
|
Assert.Equal(
|
|
WalkBoundingType.Outside,
|
|
WalkVisibilityMath.CornerPlaneCheck(5f, 5f, 10f));
|
|
Assert.Equal(
|
|
WalkBoundingType.EntirelyInside,
|
|
WalkVisibilityMath.CornerPlaneCheck(5f, 2f, 5f));
|
|
}
|
|
|
|
// ---- block_plane_check @0x0054d060 ----
|
|
|
|
[Fact]
|
|
public void Plane_check_requires_unanimity_for_outside_and_entirely_inside()
|
|
{
|
|
Assert.Equal(
|
|
WalkBoundingType.Outside,
|
|
WalkVisibilityMath.BlockPlaneCheck(1001f, 1001f, 1001f, 1001f, 0f, 10f));
|
|
Assert.Equal(
|
|
WalkBoundingType.EntirelyInside,
|
|
WalkVisibilityMath.BlockPlaneCheck(0f, 0f, 0f, 0f, 0f, 10f));
|
|
// 3-of-4 outside is still PARTIAL (the block may straddle the plane).
|
|
Assert.Equal(
|
|
WalkBoundingType.PartiallyInside,
|
|
WalkVisibilityMath.BlockPlaneCheck(1001f, 1001f, 1001f, 0f, 0f, 10f));
|
|
Assert.Equal(
|
|
WalkBoundingType.PartiallyInside,
|
|
WalkVisibilityMath.BlockPlaneCheck(0f, 0f, 0f, 1001f, 0f, 10f));
|
|
}
|
|
|
|
// ---- block_check @0x0054dc50 ----
|
|
|
|
[Fact]
|
|
public void Block_check_culls_on_any_single_fully_outside_plane()
|
|
{
|
|
// Plane 0 (CY) inside everywhere; plane 1 outside at all four corners.
|
|
float[] c = [0f, 1001f];
|
|
Assert.Equal(
|
|
WalkBoundingType.Outside,
|
|
WalkVisibilityMath.BlockCheck(c, c, c, c, planeCount: 1, maxZ: 10f, minZ: 0f));
|
|
}
|
|
|
|
[Fact]
|
|
public void Block_check_demotion_is_sticky_across_planes()
|
|
{
|
|
// CY entirely inside; plane 1 partial at one corner; plane 2 entirely
|
|
// inside — the partial must survive to the final result.
|
|
float[] cornerA = [0f, -5f, 0f]; // straddles h=5 for slab [2,10]
|
|
float[] cornerB = [0f, 0f, 0f];
|
|
Assert.Equal(
|
|
WalkBoundingType.PartiallyInside,
|
|
WalkVisibilityMath.BlockCheck(
|
|
cornerA, cornerB, cornerB, cornerB, planeCount: 2, maxZ: 10f, minZ: 2f));
|
|
}
|
|
|
|
[Fact]
|
|
public void Block_check_is_entirely_inside_only_with_unanimity_on_every_plane()
|
|
{
|
|
float[] c = [0f, 0f, 0f];
|
|
Assert.Equal(
|
|
WalkBoundingType.EntirelyInside,
|
|
WalkVisibilityMath.BlockCheck(c, c, c, c, planeCount: 2, maxZ: 10f, minZ: 0f));
|
|
}
|
|
|
|
// ---- FillClipHeights (get_clip_height @0x0054cff0) ----
|
|
|
|
[Fact]
|
|
public void Clip_heights_write_the_cy_plane_then_every_edge_plane()
|
|
{
|
|
var cy = new WalkPlane(new Vector3(0, 0, 1), -5f);
|
|
WalkPlane[] edges =
|
|
[
|
|
new(new Vector3(0, 0, -1), 20f),
|
|
new(new Vector3(1, 0, 0), -100f),
|
|
];
|
|
Span<float> bounds = stackalloc float[3];
|
|
|
|
WalkVisibilityMath.FillClipHeights(0f, 0f, cy, edges, bounds);
|
|
|
|
Assert.Equal(-5f, bounds[0]);
|
|
Assert.Equal(20f, bounds[1]);
|
|
Assert.Equal(WalkVisibilityMath.OutsideColumn, bounds[2]);
|
|
}
|
|
|
|
// ---- viewconeCheck @0x0054c250 ----
|
|
|
|
private static readonly WalkPlane Cy = new(new Vector3(0, 1, 0), 0f); // forward = +Y, eye at origin
|
|
|
|
[Fact]
|
|
public void Sphere_fully_behind_the_near_plane_is_outside()
|
|
=> Assert.Equal(
|
|
WalkBoundingType.Outside,
|
|
WalkVisibilityMath.ViewconeCheck(new Vector3(0, -5, 0), 1f, Cy, []));
|
|
|
|
[Fact]
|
|
public void Cull_is_strict_and_partial_is_inclusive_at_the_boundary()
|
|
{
|
|
// d == -r exactly: NOT culled (strict d < -r), and partial (d <= r).
|
|
Assert.Equal(
|
|
WalkBoundingType.PartiallyInside,
|
|
WalkVisibilityMath.ViewconeCheck(new Vector3(0, -1, 0), 1f, Cy, []));
|
|
// d == +r exactly: tangent from inside counts PARTIAL, not entirely.
|
|
Assert.Equal(
|
|
WalkBoundingType.PartiallyInside,
|
|
WalkVisibilityMath.ViewconeCheck(new Vector3(0, 1, 0), 1f, Cy, []));
|
|
Assert.Equal(
|
|
WalkBoundingType.EntirelyInside,
|
|
WalkVisibilityMath.ViewconeCheck(new Vector3(0, 1.01f, 0), 1f, Cy, []));
|
|
}
|
|
|
|
[Fact]
|
|
public void Edge_planes_cull_and_demote_like_the_cy_plane()
|
|
{
|
|
WalkPlane[] edges = [new(new Vector3(1, 0, 0), 0f)]; // inside is x >= 0
|
|
Assert.Equal(
|
|
WalkBoundingType.Outside,
|
|
WalkVisibilityMath.ViewconeCheck(new Vector3(-3, 5, 0), 1f, Cy, edges));
|
|
Assert.Equal(
|
|
WalkBoundingType.PartiallyInside,
|
|
WalkVisibilityMath.ViewconeCheck(new Vector3(0.5f, 5, 0), 1f, Cy, edges));
|
|
Assert.Equal(
|
|
WalkBoundingType.EntirelyInside,
|
|
WalkVisibilityMath.ViewconeCheck(new Vector3(3, 5, 0), 1f, Cy, edges));
|
|
}
|
|
|
|
// ---- DrawPortalPolyInternal @0x0059bc90's degenerate-input guard
|
|
// (S4-c1 C1, T2 — quantifier corrected at fix round 1 F1): the polygon
|
|
// is rejected only when EVERY local vertex lies on the SAME one of the
|
|
// four planes x=+12, x=-12, y=+12, y=-12. A polygon with merely ONE
|
|
// vertex on a bound is an ordinary polygon and is kept. ----
|
|
|
|
[Fact]
|
|
public void Boundary_guard_rejects_a_polygon_whose_every_vertex_sits_on_the_same_plusX_plane()
|
|
{
|
|
// MUTATION: delete the guard (always return false) — this case,
|
|
// and every other all-on-plane case below, fails to reject.
|
|
Vector3[] polygon = [new Vector3(12f, -3f, 3f), new Vector3(12f, 0f, 3f), new Vector3(12f, 5f, 3f)];
|
|
Assert.True(WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard(polygon));
|
|
}
|
|
|
|
[Fact]
|
|
public void Boundary_guard_rejects_a_polygon_whose_every_vertex_sits_on_the_same_minusY_plane()
|
|
{
|
|
// The y=-12 plane specifically (§7 F1's fourth case) — not just x.
|
|
Vector3[] polygon = [new Vector3(-3f, -12f, 3f), new Vector3(0f, -12f, 3f), new Vector3(5f, -12f, 3f)];
|
|
Assert.True(WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard(polygon));
|
|
}
|
|
|
|
[Fact]
|
|
public void Boundary_guard_admits_a_polygon_with_only_one_vertex_on_plusX12_the_rest_inside()
|
|
{
|
|
// The round-1 defect: the round-0 "any vertex" reading would have
|
|
// rejected this. Retail draws it (punched/sealed, counted) — only
|
|
// the DEGENERATE all-on-one-plane polygon is dropped.
|
|
// MUTATION: restore the any-vertex form (any vertex == +/-12
|
|
// rejects) — this case starts failing (wrongly rejected) even
|
|
// though the guard still correctly rejects the all-on-plane cases.
|
|
Vector3[] polygon = [new Vector3(0f, 0f, 3f), new Vector3(12f, 0f, 3f), new Vector3(5f, 5f, 3f)];
|
|
Assert.False(WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard(polygon));
|
|
}
|
|
|
|
[Fact]
|
|
public void Boundary_guard_admits_a_polygon_whose_every_vertex_is_just_inside_11_999()
|
|
{
|
|
Vector3[] polygon = [new Vector3(11.999f, 0f, 3f), new Vector3(11.999f, 5f, 3f), new Vector3(11.999f, -5f, 3f)];
|
|
Assert.False(WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard(polygon));
|
|
}
|
|
|
|
[Fact]
|
|
public void Boundary_guard_admits_a_polygon_split_across_plusX12_and_plusY12_no_common_plane()
|
|
{
|
|
// Two different vertices sit on two DIFFERENT bounding planes; no
|
|
// single plane holds every vertex, so nothing rejects it.
|
|
// MUTATION: OR the four per-vertex hits instead of the four
|
|
// per-plane accumulators (i.e. revert to "any vertex on any plane")
|
|
// — this case starts failing (wrongly rejected).
|
|
Vector3[] polygon = [new Vector3(12f, 0f, 3f), new Vector3(0f, 12f, 3f), new Vector3(-5f, -5f, 3f)];
|
|
Assert.False(WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard(polygon));
|
|
}
|
|
|
|
[Fact]
|
|
public void Boundary_guard_admits_a_polygon_whose_every_vertex_is_on_SOME_plane_but_not_the_SAME_one()
|
|
{
|
|
// S4-c1 fix round 2, R2-3: the existing split-plane case above
|
|
// (12,0,3)/(0,12,3)/(-5,-5,3) does NOT discriminate retail's real
|
|
// quantifier ("every vertex on the SAME plane") from the weaker
|
|
// "every vertex on SOME plane" (per-vertex OR across the four
|
|
// planes, ANDed across vertices) — its third vertex sits on NO
|
|
// plane at all, so both readings admit it for the same reason. This
|
|
// case closes that gap: EVERY vertex here touches a plane —
|
|
// (12,0,3) and (12,5,3) sit on x=+12, (0,12,3) sits on y=+12 — yet
|
|
// no SINGLE plane holds all three: x=+12 is cleared by vertex 2
|
|
// (x=0), y=+12 is cleared by vertex 1 (y=0). Retail's four
|
|
// independent per-plane accumulators admit it (neither survives to
|
|
// the end of the loop).
|
|
// MUTATION: replace the four per-plane accumulators with a single
|
|
// per-vertex "is this vertex on ANY of the four planes" test ANDed
|
|
// across vertices — this case starts failing (wrongly rejected):
|
|
// every vertex here IS on some plane, so the per-vertex-OR form
|
|
// rejects it.
|
|
Vector3[] polygon = [new Vector3(12f, 0f, 3f), new Vector3(0f, 12f, 3f), new Vector3(12f, 5f, 3f)];
|
|
Assert.False(WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard(polygon));
|
|
}
|
|
|
|
[Fact]
|
|
public void Boundary_guard_ignores_the_vertical_z_component()
|
|
{
|
|
// Retail's guard reads only the two horizontal FIELDS the decomp
|
|
// names (arg1->vertices[i] as the x source, ecx_1[1] as the y
|
|
// source) — a vertex whose HEIGHT happens to be +/-12 is an entirely
|
|
// ordinary local coordinate and must not trip the guard.
|
|
Vector3[] polygon = [new Vector3(0, 0, 12f), new Vector3(1, 1, -12f), new Vector3(2, 0, 0)];
|
|
Assert.False(WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard(polygon));
|
|
}
|
|
|
|
[Fact]
|
|
public void Boundary_guard_rejects_the_empty_polygon_because_every_plane_predicate_survives_vacuously()
|
|
{
|
|
// S4-c1 fix round 1 F1 consequence, not an explicit §7 case: retail
|
|
// wraps the whole predicate+count block in `if (num_pts > 0)`
|
|
// (0x0059bcc9) — for num_pts==0 NOTHING runs: no count, no clip, no
|
|
// draw. The four-accumulator port reproduces that "nothing happens"
|
|
// effect for free: with no vertex to clear any of them, all four
|
|
// per-plane predicates stay at their initial `true`, so the guard
|
|
// rejects (bails before transform/clip/count) — the same net effect
|
|
// as retail's outer num_pts>0 gate. Real dat portal polygons are
|
|
// never empty (the doc comment's DAT scan enumerates only >=3-vertex
|
|
// polygons); this is a defensive corner, not a live path.
|
|
Assert.True(WalkVisibilityMath.IsRejectedByPortalPolygonBoundaryGuard([]));
|
|
}
|
|
}
|