fix(terrain): align per-cell triangle geometry with ACE's ConstructPolygons convention
Our LandblockMesh, terrain.vert corner tables, and TerrainSurface.SampleZ
used the OPPOSITE diagonal for each CellSplitDirection enum value from
what ACE (and the decompiled retail client at FUN_00532a50) picks for the
same sign bit. Same formula, same sign-bit mapping, inverted geometry.
Symptom: remote players rendered at server-broadcast Z hovered or clipped
by up to ~1m on sloped cells. Flat cells masked the bug because all four
corner heights were equal so any triangle pair returned the same Z. Live
diagnostic confirmed +0.79m hover on cell (7,5) at lb(AA,B4) — a ~20°
slope — while flat neighbors agreed to floating-point noise.
Three coordinated edits so CPU mesh + GPU corner lookup + CPU sampler all
agree on the retail geometry:
- LandblockMesh: SWtoNE branch now emits {BL,BR,TR}+{BL,TR,TL} (y=x cut),
SEtoNW emits {BL,BR,TL}+{BR,TR,TL} (x+y=1 cut).
- terrain.vert: corner-index tables updated to match.
- TerrainSurface.SampleZ: swapped the two branches' interpolation.
After the fix, 19 live DIAG samples across flat + two slope transitions
all land within 0.01m of server Z. Staircase pattern during remote motion
on slopes is a separate bug (no per-frame collision resolution) and will
be addressed via the transition/FindValidPosition port.
Cross-verified against: ACE LandblockStruct.ConstructPolygons lines 221-
244, decompiled retail FUN_00532a50 (chunk_00530000.c:2235), ClientReference
IsSWtoNECut (tests/AcDream.Core.Tests/Terrain/ClientReference.cs).
Updated test SplitDirection_TerrainSurface_AgreesWith_TerrainBlending
with corrected expectations (Z values swap between the two branches).
All 717 tests green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
beffdf477e
commit
56975f8919
5 changed files with 96 additions and 71 deletions
|
|
@ -1970,7 +1970,7 @@ public sealed class GameWindow : IDisposable
|
|||
entity.Position = worldPos;
|
||||
entity.Rotation = rot;
|
||||
|
||||
// Track remote-entity motion for stop detection. Only record the
|
||||
// Track remote-entity motion for stop detection. Only record the
|
||||
// timestamp when position moved MEANINGFULLY (> 0.05m). Updates
|
||||
// that report the same position keep the old Time, so the
|
||||
// TickAnimations check can see when motion last changed.
|
||||
|
|
|
|||
|
|
@ -76,26 +76,30 @@ void main() {
|
|||
|
||||
// Derive which of the 4 cell corners this vertex represents from
|
||||
// gl_VertexID % 6. The CPU-side LandblockMesh emits vertices in a
|
||||
// specific order for each split direction; the table below must stay
|
||||
// specific order for each split direction; the tables below must stay
|
||||
// in lockstep with LandblockMesh.Build's SWtoNE/SEtoNW branches.
|
||||
// 2026-04-21 fix: geometry re-derived to match ACE's ConstructPolygons
|
||||
// convention. SWtoNE (cut BL→TR, y=x diagonal) now maps to the {BL,BR,TR}
|
||||
// + {BL,TR,TL} triangle pair; SEtoNW (cut BR→TL, x+y=1 diagonal) maps to
|
||||
// {BL,BR,TL} + {BR,TR,TL}.
|
||||
int vIdx = gl_VertexID % 6;
|
||||
int corner = 0;
|
||||
if (splitDir == 0u) {
|
||||
// SWtoNE order: BL, TL, BR, BR, TL, TR → corners 0, 3, 1, 1, 3, 2
|
||||
// SWtoNE order: BL, BR, TR, BL, TR, TL → corners 0, 1, 2, 0, 2, 3
|
||||
if (vIdx == 0) corner = 0;
|
||||
else if (vIdx == 1) corner = 3;
|
||||
else if (vIdx == 2) corner = 1;
|
||||
else if (vIdx == 3) corner = 1;
|
||||
else if (vIdx == 4) corner = 3;
|
||||
else corner = 2;
|
||||
} else {
|
||||
// SEtoNW order: BL, TR, BR, BL, TL, TR → corners 0, 2, 1, 0, 3, 2
|
||||
if (vIdx == 0) corner = 0;
|
||||
else if (vIdx == 1) corner = 2;
|
||||
else if (vIdx == 2) corner = 1;
|
||||
else if (vIdx == 1) corner = 1;
|
||||
else if (vIdx == 2) corner = 2;
|
||||
else if (vIdx == 3) corner = 0;
|
||||
else if (vIdx == 4) corner = 3;
|
||||
else corner = 2;
|
||||
else if (vIdx == 4) corner = 2;
|
||||
else corner = 3;
|
||||
} else {
|
||||
// SEtoNW order: BL, BR, TL, BR, TR, TL → corners 0, 1, 3, 1, 2, 3
|
||||
if (vIdx == 0) corner = 0;
|
||||
else if (vIdx == 1) corner = 1;
|
||||
else if (vIdx == 2) corner = 3;
|
||||
else if (vIdx == 3) corner = 1;
|
||||
else if (vIdx == 4) corner = 2;
|
||||
else corner = 3;
|
||||
}
|
||||
|
||||
vec2 baseUV;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue