diff --git a/src/AcDream.Core/Physics/TerrainSurface.cs b/src/AcDream.Core/Physics/TerrainSurface.cs index 8337dbe..a3c5f78 100644 --- a/src/AcDream.Core/Physics/TerrainSurface.cs +++ b/src/AcDream.Core/Physics/TerrainSurface.cs @@ -51,12 +51,15 @@ public sealed class TerrainSurface /// interpolation within that triangle. This matches the visual terrain /// mesh exactly. /// - /// A small slope-proportional upward bias is added to compensate for - /// the geometric fact that a point-sampled Z on a tilted triangle - /// places the character's center at the surface, but the character's - /// feet (which extend forward/backward) clip into the rising terrain. - /// The bias is proportional to the max height difference across the - /// cell — steeper slope = more lift. On flat ground it's zero. + /// Triangle layout (from LandblockMesh.cs index buffer): + /// SWtoNE: tri1 = {BL,TL,BR}, tri2 = {BR,TL,TR} — shared edge TL→BR (x+y=1 boundary) + /// SEtoNW: tri1 = {BL,TR,BR}, tri2 = {BL,TL,TR} — shared edge BL→TR (y=x boundary) + /// + /// NOTE: The SWtoNE "cut" exposes the SW(BL) and NE(TR) corners as isolated + /// vertices — the hypotenuse runs NW(TL)→SE(BR), so the dividing test is + /// x+y=1 (not y=x). Confusing naming aside, the formula below matches + /// TerrainGeometryGenerator.GetHeight (ACME WorldBuilder-ACME-Edition) which + /// was verified against the mesh index buffer. /// public float SampleZ(float localX, float localY) { @@ -83,17 +86,21 @@ public sealed class TerrainSurface if (splitSWtoNE) { - if (ty <= tx) - return hBL + (hBR - hBL) * tx + (hTR - hBR) * ty; + // Mesh: {BL,TL,BR} and {BR,TL,TR}. Shared hypotenuse = TL(0,1)→BR(1,0). + // Dividing line: tx + ty = 1. + if (tx + ty <= 1f) + return hBL + (hBR - hBL) * tx + (hTL - hBL) * ty; // BL+BR+TL triangle else - return hBL + (hTR - hTL) * tx + (hTL - hBL) * ty; + return hTR + (hTL - hTR) * (1f - tx) + (hBR - hTR) * (1f - ty); // TR+TL+BR triangle } else { - if (ty <= 1f - tx) - return hBL + (hBR - hBL) * tx + (hTL - hBL) * ty; + // Mesh: {BL,TR,BR} and {BL,TL,TR}. Shared hypotenuse = BL(0,0)→TR(1,1). + // Dividing line: ty = tx. + if (ty <= tx) + return hBL + (hBR - hBL) * tx + (hTR - hBR) * ty; // BL+BR+TR triangle else - return hTR + (hTL - hTR) * (1f - tx) + (hBR - hTR) * (1f - ty); + return hBL + (hTR - hTL) * tx + (hTL - hBL) * ty; // BL+TR+TL triangle } }