fix(test): A6.P3 slice 1 T5 — redesign test to actually catch synthesis regression
Code-review feedback on commit 5f7722a: the test was gamed —
placing the sphere exactly on the floor (worldPosZ = floorZ) made it
pass regardless of whether synthesis was present. With sphere center at
Z=0.48 (= floorZ + SphereRadius), PolygonHitsSpherePrecise's distance
guard fires immediately (|dist|=0.48 > rad=0.478) and
TryFindIndoorWalkablePlane returns false even WITH synthesis code. The
test would have passed even if the strip were reverted.
Redesign: restore worldPosZ = floorZ - 0.05f (sphere center at Z=0.43).
Now |dist|=0.43 < rad=0.478 → the guard passes → TryFindIndoorWalkablePlane
finds the floor polygon → synthesis would fire → CP writes every frame.
Path 5 (Contact branch) is not a concern: the loop moves only in X so
movement = (0.001, 0, 0), Dot(movement, floor_normal=(0,0,1)) = 0 ≥ 0 →
PosHitsSphere front-face cull rejects the floor hit even with sphere
center below the floor. Path 5 returns OK with zero CP writes. Contact
flag is left set to keep the test on the realistic grounded-mover path.
Validated locally by temporarily re-introducing the synthesis call —
test fails with 60 writes (1 per frame) pre-strip, passes with 0
additional writes post-strip. Now a real regression sentinel.
1148 pass + 8 pre-existing fail baseline maintained.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5f7722a3a4
commit
39fc0372a3
1 changed files with 28 additions and 20 deletions
|
|
@ -230,30 +230,38 @@ public class IndoorContactPlaneRetentionTests
|
||||||
public void IndoorFlatFloorWalking_60Frames_ProducesAtMost5ExtraCpWrites()
|
public void IndoorFlatFloorWalking_60Frames_ProducesAtMost5ExtraCpWrites()
|
||||||
{
|
{
|
||||||
// ── Arrange ───────────────────────────────────────────────────────────
|
// ── Arrange ───────────────────────────────────────────────────────────
|
||||||
// Post-fix grounded model:
|
// Geometry: sphere center 5 cm BELOW the floor so that the synthesis
|
||||||
|
// path's distance guard passes and TryFindIndoorWalkablePlane would
|
||||||
|
// actually find the floor polygon if synthesis were re-introduced.
|
||||||
//
|
//
|
||||||
// SpherePath.InitPath sets LocalSphere[0].Origin = (0,0,sphereRadius).
|
// SpherePath.InitPath sets LocalSphere[0].Origin = (0,0,SphereRadius).
|
||||||
// After SetCheckPos(worldPos), the global sphere CENTER is at
|
// After SetCheckPos(worldPos), the global sphere CENTER is at
|
||||||
// worldPos + (0,0,SphereRadius) = (0,0,SphereRadius).
|
// worldPos + (0,0,SphereRadius) = (worldPosX, 0, -0.05 + 0.48 = 0.43).
|
||||||
//
|
//
|
||||||
// A correctly-grounded sphere has its bottom exactly at the floor:
|
// PolygonHitsSpherePrecise distance guard (BSPQuery.cs line ~117):
|
||||||
// sphereBottom = sphereCenter.Z - SphereRadius
|
// dist = Dot(normal=(0,0,1), center=(x,0,0.43)) + D(=0) = 0.43
|
||||||
// = worldPosZ + SphereRadius - SphereRadius
|
// rad = SphereRadius - EPSILON = 0.48 - 0.002 = 0.478
|
||||||
// = worldPosZ
|
// |dist| = 0.43 < 0.478 → guard passes → polygon is tested → FOUND.
|
||||||
|
// So TryFindIndoorWalkablePlane WOULD call ValidateWalkable → CP write
|
||||||
|
// if the synthesis call were present. With the strip in place it is never
|
||||||
|
// called → ≤5 additional writes → PASS.
|
||||||
//
|
//
|
||||||
// With worldPosZ = 0 (= floorZ), the bottom just touches the floor.
|
// With the post-5f7722a setup (worldPosZ = floorZ = 0):
|
||||||
// SphereIntersectsPolyInternal uses a strict penetration check, so a
|
// dist = 0.48, rad = 0.478, |dist| = 0.48 > 0.478 → guard FIRES →
|
||||||
// sphere touching-but-not-penetrating does NOT count as a hit.
|
// TryFindIndoorWalkablePlane returns false even WITH synthesis code.
|
||||||
// Path 5 (Contact grounded) returns OK with no CP write.
|
// That setup was not a regression sentinel; this one is.
|
||||||
//
|
//
|
||||||
// Pre-fix: the sphere was positioned 5 cm BELOW the floor so that
|
// Path 5 (BSP Contact branch, BSPQuery.cs ~line 1732):
|
||||||
// TryFindIndoorWalkablePlane → ValidateWalkable would fire every frame.
|
// The loop advances only in X, so movement = (0.001, 0, 0).
|
||||||
// Post-fix: synthesis is gone; the sphere must be at its natural
|
// PosHitsSphere culls hits where Dot(movement, normal) >= 0.
|
||||||
// grounded position (bottom at floorZ) so that BSP Path 5 finds no
|
// Dot((0.001,0,0), (0,0,1)) = 0 >= 0 → floor polygon is ALWAYS
|
||||||
// penetration and returns OK immediately — zero additional CP writes.
|
// rejected by the front-face cull, even though the sphere center is
|
||||||
|
// below the floor. Path 5 exits OK with no CP write regardless of
|
||||||
|
// whether the Contact flag is set. Leaving Contact set keeps this
|
||||||
|
// test on the realistic grounded-mover path.
|
||||||
const float floorZ = 0f;
|
const float floorZ = 0f;
|
||||||
const float worldPosZ = floorZ; // sphere bottom exactly at floor
|
const float worldPosZ = floorZ - 0.05f; // 5 cm below floor
|
||||||
const float sphereCenterZ = worldPosZ + SphereRadius; // = 0.48
|
const float sphereCenterZ = worldPosZ + SphereRadius; // = 0.43
|
||||||
|
|
||||||
var floorPlane = new Plane(Vector3.UnitZ, -floorZ); // N·p + D = 0 → D = 0
|
var floorPlane = new Plane(Vector3.UnitZ, -floorZ); // N·p + D = 0 → D = 0
|
||||||
var worldPos = new Vector3(0f, 0f, worldPosZ);
|
var worldPos = new Vector3(0f, 0f, worldPosZ);
|
||||||
|
|
@ -278,7 +286,7 @@ public class IndoorContactPlaneRetentionTests
|
||||||
// indoor branch would call it each physics frame.
|
// indoor branch would call it each physics frame.
|
||||||
for (int frame = 0; frame < SimulatedFrames; frame++)
|
for (int frame = 0; frame < SimulatedFrames; frame++)
|
||||||
{
|
{
|
||||||
// Advance position by 1 mm forward — same Z (grounded on floor).
|
// Advance position by 1 mm forward — same Z (sphere center 5 cm below floor).
|
||||||
var newPos = new Vector3(frame * 0.001f, 0f, worldPosZ);
|
var newPos = new Vector3(frame * 0.001f, 0f, worldPosZ);
|
||||||
t.SpherePath.SetCheckPos(newPos, IndoorCellId);
|
t.SpherePath.SetCheckPos(newPos, IndoorCellId);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue