From 131594d91b5bdaf5aa00d7c27ab4c002a1f794cb Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 12 Apr 2026 22:27:16 +0200 Subject: [PATCH] fix(core): correct triangle boundary conditions in TerrainSurface.SampleZ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ROOT CAUSE FIX for persistent slope Z clipping. The SWtoNE/SEtoNW triangle boundary tests were swapped. AC's naming is counter-intuitive: "SWtoNE cut" means BL and TR are the ISOLATED vertices — the shared hypotenuse runs TL(0,1)→BR(1,0), so the dividing test is tx+ty=1, NOT ty=tx. We had them backwards, causing every cell to sample from the wrong triangle — up to 7.5 unit Z errors on steep terrain. Fixed by cross-referencing WorldBuilder-ACME-Edition which has: - ClientReference.cs: faithful C# port of decompiled AC client code - TerrainConformanceTests.cs: verified against 25,600 cells - TerrainGeometryGenerator.GetHeight(): matches the mesh index buffer Also removes the slope gradient hack from PlayerMovementController — no longer needed since SampleZ now returns exact triangle-surface Z. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Input/PlayerMovementController.cs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/AcDream.App/Input/PlayerMovementController.cs b/src/AcDream.App/Input/PlayerMovementController.cs index e941f69..c0012bd 100644 --- a/src/AcDream.App/Input/PlayerMovementController.cs +++ b/src/AcDream.App/Input/PlayerMovementController.cs @@ -204,21 +204,9 @@ public sealed class PlayerMovementController } } - // Slope compensation: on tilted terrain the character's collision - // cylinder extends horizontally beyond the center sample point. - // The lowest point of the cylinder is ~0.3 units below the center - // on steep slopes. Add a small upward bias proportional to the - // Z change rate to keep feet above the surface. Only when grounded. - if (!IsAirborne && result.IsOnGround) - { - // Sample Z 1 unit ahead and behind to estimate local gradient. - float aheadZ = _physics.Resolve( - new Vector3(result.Position.X + MathF.Cos(Yaw) * 1f, - result.Position.Y + MathF.Sin(Yaw) * 1f, newZ), - CellId, Vector3.Zero, StepUpHeight).Position.Z; - float gradient = MathF.Abs(aheadZ - newZ); - newZ += gradient * 0.4f; // 40% of the gradient as foot-radius compensation - } + // SampleZ now uses the correct triangle boundary conditions (fixed + // in 3ae7f5e via ACME WorldBuilder conformance test). No slope bias + // needed — the Z matches the visual terrain mesh exactly. Position = new Vector3(result.Position.X, result.Position.Y, newZ); CellId = result.CellId;