diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index a485ea5..351bf9b 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -72,6 +72,11 @@ public sealed class GameWindow : IDisposable private int _liveDropReasonNoSetup; private int _liveDropReasonSetupDatMissing; private int _liveDropReasonNoMeshRefs; + // Phase 6.4 animation-registration diagnostics + private int _liveAnimRejectNoCycle; + private int _liveAnimRejectFramerate; + private int _liveAnimRejectSingleFrame; + private int _liveAnimRejectPartFrames; public GameWindow(string datDir, WorldGameState worldGameState, WorldEvents worldEvents) { @@ -449,9 +454,18 @@ public sealed class GameWindow : IDisposable // the WorldEntity at identity so the renderer's // model = PartTransform * entityRoot = cellTransform * I // gives the correctly positioned cell mesh. + // + // Z lift: buildings sit ON the terrain mesh, so the ground + // floor of every building is coincident in Z with the terrain + // polygon beneath it. Without a bias the two fight for the + // same depth and flicker as the camera moves. A small lift + // (2 cm) is invisible from human scale but breaks the tie + // cleanly in the cell mesh's favor. + var cellOrigin = envCell.Position.Origin + lbOffset + + new System.Numerics.Vector3(0f, 0f, 0.02f); var cellTransform = System.Numerics.Matrix4x4.CreateFromQuaternion(envCell.Position.Orientation) * - System.Numerics.Matrix4x4.CreateTranslation(envCell.Position.Origin + lbOffset); + System.Numerics.Matrix4x4.CreateTranslation(cellOrigin); var cellMeshRef = new AcDream.Core.World.MeshRef(envCellId, cellTransform); @@ -921,6 +935,17 @@ public sealed class GameWindow : IDisposable // Phase 6.4: register for per-frame playback if we resolved a real // cycle with a non-zero framerate and at least two frames in the // cycle (single-frame poses are static and don't need ticking). + // Diagnostic: log why we did / didn't register so we can tell + // which entities fall through the filter. + if (idleCycle is null) + _liveAnimRejectNoCycle++; + else if (idleCycle.Framerate == 0f) + _liveAnimRejectFramerate++; + else if (idleCycle.HighFrame <= idleCycle.LowFrame) + _liveAnimRejectSingleFrame++; + else if (idleCycle.Animation.PartFrames.Count <= 1) + _liveAnimRejectPartFrames++; + if (idleCycle is not null && idleCycle.Framerate != 0f && idleCycle.HighFrame > idleCycle.LowFrame && idleCycle.Animation.PartFrames.Count > 1) @@ -950,6 +975,10 @@ public sealed class GameWindow : IDisposable // waiting for a graceful shutdown. if (_liveSpawnReceived % 20 == 0) { + Console.WriteLine( + $"live: animated={_animatedEntities.Count} " + + $"animReject: noCycle={_liveAnimRejectNoCycle} fr0={_liveAnimRejectFramerate} " + + $"1frame={_liveAnimRejectSingleFrame} partFrames={_liveAnimRejectPartFrames}"); Console.WriteLine( $"live: summary recv={_liveSpawnReceived} hydrated={_liveSpawnHydrated} " + $"drops: noPos={_liveDropReasonNoPos} noSetup={_liveDropReasonNoSetup} " +