From 82e857cf21a27cc541ea91e78a3121c7720aa8ba Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 10 Apr 2026 21:51:34 +0200 Subject: [PATCH] fix(app): interior stabs are landblock-local, not cell-local MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 2d's initial composition was cellOrigin + cellRot*stabLocal, assuming EnvCell.StaticObjects carried cell-local frames and that EnvCell.Position was the cell-to-landblock transform. User reported all interior objects "far up in the air" after the initial Phase 2d. Diagnostic confirmed the real shape: EnvCell 0xA9B40100.Position.Origin = (84.1, 131.5, 66.0) and the first Stab inside it had Frame.Origin = (92.1, 131.5, 68.0). Both are in landblock-local X/Y/Z space — the stab is 8 units east and 2 units up from the cell's registered origin but expressed in the SAME coordinate space, not as an offset. Adding the cell origin on top double-counted ~155 units in Z and put the statue at worldPos Y=263 Z=233+, completely out of range. EnvCell.Position appears to tell the physics engine which landblock region owns the cell (for collision/portal lookups) rather than acting as a cell-to-landblock transform for contained objects. The stabs are already in the same coordinate system as LandBlockInfo.Objects stabs. Fix: drop the cell origin + cell rotation from the composition. World position is now just stab.Frame.Origin + lbOffset, mirroring the regular Stab handling exactly. Smoke verified: 475 interior objects still hydrate, process runs clean. Visual verification pending. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/AcDream.App/Rendering/GameWindow.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 10196b6..beb110f 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -381,16 +381,14 @@ public sealed class GameWindow : IDisposable if (meshRefs.Count == 0) continue; - // Compose: the stab's position is cell-local, and the cell's Position - // is landblock-local. Rotate the stab-local position by the cell's - // orientation before adding the cell origin. - var stabLocalPos = stab.Frame.Origin; - var cellOrigin = envCell.Position.Origin; - var cellRot = envCell.Position.Orientation; - var rotatedStab = System.Numerics.Vector3.Transform(stabLocalPos, cellRot); - var landblockLocalPos = cellOrigin + rotatedStab; - var worldPos = landblockLocalPos + lbOffset; - var worldRot = cellRot * stab.Frame.Orientation; + // Stabs inside EnvCells are already in landblock-local coordinates + // (same coordinate space as LandBlockInfo.Objects stabs) — NOT + // cell-local. The EnvCell.Position field tells the physics engine + // which cell owns the object, but it doesn't translate coordinates. + // Adding cellOrigin was a wrong assumption that left interior objects + // floating ~150 units in the air. + var worldPos = stab.Frame.Origin + lbOffset; + var worldRot = stab.Frame.Orientation; var hydrated = new AcDream.Core.World.WorldEntity {