fix(app): interior stabs are landblock-local, not cell-local

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) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-10 21:51:34 +02:00
parent abcfb55418
commit 82e857cf21

View file

@ -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
{