From 7c516edd7b836877cb5a1e07b34731ba9aa23f91 Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 19 May 2026 21:58:53 +0200 Subject: [PATCH] fix(physics): document adjustedCenter discard + restore wall-poly test Code review feedback on Task 3 commit 91b29d1: - TryFindIndoorWalkablePlane: comment explaining why FindWalkableSphere's adjustedCenter out param is intentionally discarded (ValidateWalkable recomputes contact geometry from plane + foot position, consistent with the outdoor terrain path). - IndoorWalkablePlaneTests: new TryFindIndoorWalkablePlane_WallPolyInBsp_ReturnsFalse restores integration-level coverage that the renamed NoBsp_ReturnsFalse lost. Verifies WalkableAllowance gate rejects a wall polygon in the cell BSP. Steep-poly rejection is also covered at the BSPQuery layer by FindWalkableSphere_SteepPoly_RejectedByWalkableAllowance. No behavior change. Build clean; all related tests pass; same 8 pre-existing failures. Spec: docs/superpowers/specs/2026-05-19-indoor-walkable-plane-bsp-port-design.md Co-Authored-By: Claude Sonnet 4.6 --- src/AcDream.Core/Physics/TransitionTypes.cs | 6 +++ .../Physics/IndoorWalkablePlaneTests.cs | 51 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/AcDream.Core/Physics/TransitionTypes.cs b/src/AcDream.Core/Physics/TransitionTypes.cs index 7364ec4..50dc718 100644 --- a/src/AcDream.Core/Physics/TransitionTypes.cs +++ b/src/AcDream.Core/Physics/TransitionTypes.cs @@ -1247,6 +1247,12 @@ public sealed class Transition this.SpherePath.WalkableAllowance = savedWalkableAllowance; } + // adjustedCenter (sphere slid onto polygon plane) is intentionally + // discarded — ValidateWalkable recomputes contact geometry from the + // world-space plane + foot position, consistent with the outdoor terrain + // path (SampleTerrainWalkable returns only plane + vertices, no adjusted + // sphere). The local is held only to satisfy the out param. + if (!found || hitPoly is null) return false; // Transform hit polygon's plane + vertices to world space. Math is diff --git a/tests/AcDream.Core.Tests/Physics/IndoorWalkablePlaneTests.cs b/tests/AcDream.Core.Tests/Physics/IndoorWalkablePlaneTests.cs index 466c5d0..75f136e 100644 --- a/tests/AcDream.Core.Tests/Physics/IndoorWalkablePlaneTests.cs +++ b/tests/AcDream.Core.Tests/Physics/IndoorWalkablePlaneTests.cs @@ -167,6 +167,57 @@ public class IndoorWalkablePlaneTests Assert.False(found); } + [Fact] + public void TryFindIndoorWalkablePlane_WallPolyInBsp_ReturnsFalse() + { + // A polygon with a horizontal normal (Z = 0) is a wall, not a floor. + // walkable_hits_sphere rejects it: dp = dot(UnitZ, (0,1,0)) = 0 <= FloorZ. + // Regression coverage for the previous NoWalkablePolys_ReturnsFalse intent + // (the renamed NoBsp_ReturnsFalse only covers the null-BSP early-return). + Vector3[] wallVerts = + { + new Vector3(0f, 0f, 0f), + new Vector3(1f, 0f, 0f), + new Vector3(1f, 0f, 1f), + new Vector3(0f, 0f, 1f), + }; + var resolved = new Dictionary + { + [0] = new ResolvedPolygon + { + Vertices = wallVerts, + Plane = new Plane(new Vector3(0f, 1f, 0f), 0f), // wall facing +Y + NumPoints = 4, + SidesType = CullMode.None, + }, + }; + + var center = new Vector3(0.5f, 0f, 0.5f); + var bsp = BuildLeafBsp(new ushort[] { 0 }, center, 2f); + + var cell = new CellPhysics + { + BSP = bsp, + WorldTransform = Matrix4x4.Identity, + InverseWorldTransform = Matrix4x4.Identity, + Resolved = resolved, + }; + + var transition = new Transition(); + transition.SpherePath.WalkInterp = 1.0f; + + // Foot sphere positioned to overlap the wall's plane (|Y - 0| = 0 < radius 0.48). + bool found = transition.TryFindIndoorWalkablePlane( + cell, + localFootCenter: new Vector3(0.5f, 0f, 0.5f), + sphereRadius: 0.48f, + out _, + out _, + out _); + + Assert.False(found); + } + [Fact] public void TryFindIndoorWalkablePlane_EmptyResolved_ReturnsFalse() {