acdream/tests/AcDream.Core.Tests/Rendering/RenderingDiagnosticsTests.cs
Erik 2b7f5a16c6 fix(render): branch inside/outside on is_player_outside, not the camera cell (PARTIAL)
Retail SmartBox::RenderNormalMode (0x453aa0:92665) branches DrawInside vs the
outdoor LScape::draw on is_player_outside (the PLAYER's cell, 0x451e80), then
roots DrawInside at the VIEWER cell. acdream keyed the whole branch off the
camera cell (clipRoot = visibility.CameraCell), so a 3rd-person chase camera
lagging in a doorway AFTER the player stepped outside took the DrawInside path
rooted at the threshold cell, where the exit-portal flood degenerates: grey
world + entities-through-walls. Now ShouldRenderIndoor(playerCellId,
viewerCellResolved) gates the branch on the player; the DrawInside root stays
the viewer cell (handoff invariant preserved).

SCOPE / HONESTY: this REDUCES the player-OUTSIDE doorway grey (visual-confirmed
reduced a lot) but does NOT fix the deeper symptom: when the player is in one
interior cell (cellar 0174) and the camera is in another (room 0171), the flood
roots at the camera cell and does NOT seal the player's cell, so the cellar
floor / interior walls drop to grey. That is the KNOWN R1-completion problem
(2026-06-05 Residual A handoff + 2026-06-02 design doc section 3: a
SHELL-SEALING / wrong-flood-root bug), not this branch.

Tests: Core 1331p / 4f (documented) / 1s, App 187p, build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 17:02:09 +02:00

135 lines
5.6 KiB
C#

using AcDream.Core.Rendering;
using Xunit;
namespace AcDream.Core.Tests.Rendering;
public sealed class RenderingDiagnosticsTests
{
// Each flag-mutating test snapshots the IndoorAll state on entry and
// restores it via try/finally. RenderingDiagnostics is a process-wide
// static (env-var-initialized); without restoration a mutated state
// leaks into other tests + into parallel test runs. Mirrors the
// PhysicsDiagnosticsTests pattern at line 30-49.
[Fact]
public void IndoorAll_True_TurnsAllFlagsOn()
{
bool initial = RenderingDiagnostics.IndoorAll;
try
{
// Reset all flags off first to make the test deterministic
// regardless of env-var state on the test runner.
RenderingDiagnostics.ProbeIndoorWalkEnabled = false;
RenderingDiagnostics.ProbeIndoorLookupEnabled = false;
RenderingDiagnostics.ProbeIndoorUploadEnabled = false;
RenderingDiagnostics.ProbeIndoorXformEnabled = false;
RenderingDiagnostics.ProbeIndoorCullEnabled = false;
RenderingDiagnostics.IndoorAll = true;
Assert.True(RenderingDiagnostics.ProbeIndoorWalkEnabled);
Assert.True(RenderingDiagnostics.ProbeIndoorLookupEnabled);
Assert.True(RenderingDiagnostics.ProbeIndoorUploadEnabled);
Assert.True(RenderingDiagnostics.ProbeIndoorXformEnabled);
Assert.True(RenderingDiagnostics.ProbeIndoorCullEnabled);
Assert.True(RenderingDiagnostics.IndoorAll);
}
finally
{
RenderingDiagnostics.IndoorAll = initial;
}
}
[Fact]
public void IndoorAll_False_TurnsAllFlagsOff()
{
bool initial = RenderingDiagnostics.IndoorAll;
try
{
RenderingDiagnostics.IndoorAll = true; // start from all-on
RenderingDiagnostics.IndoorAll = false;
Assert.False(RenderingDiagnostics.ProbeIndoorWalkEnabled);
Assert.False(RenderingDiagnostics.ProbeIndoorLookupEnabled);
Assert.False(RenderingDiagnostics.ProbeIndoorUploadEnabled);
Assert.False(RenderingDiagnostics.ProbeIndoorXformEnabled);
Assert.False(RenderingDiagnostics.ProbeIndoorCullEnabled);
Assert.False(RenderingDiagnostics.IndoorAll);
}
finally
{
RenderingDiagnostics.IndoorAll = initial;
}
}
[Fact]
public void IndoorAll_OneOff_ReadsAsFalse()
{
bool initial = RenderingDiagnostics.IndoorAll;
try
{
RenderingDiagnostics.IndoorAll = true;
RenderingDiagnostics.ProbeIndoorCullEnabled = false; // flip one off
Assert.False(RenderingDiagnostics.IndoorAll);
}
finally
{
RenderingDiagnostics.IndoorAll = initial;
}
}
[Theory]
[InlineData(0x00000029ul, false)] // outdoor cell 0x29 in 8x8 grid
[InlineData(0xA9B40029ul, false)] // outdoor cell with landblock prefix
[InlineData(0x00000100ul, true)] // indoor cell minimum
[InlineData(0x00000105ul, true)] // typical Holtburg Inn interior
[InlineData(0xA9B40105ul, true)] // indoor with landblock prefix
[InlineData(0xA9B401FFul, true)] // indoor near top of range
public void IsEnvCellId_DistinguishesOutdoorVsIndoorByLow16Bits(ulong id, bool expected)
{
Assert.Equal(expected, RenderingDiagnostics.IsEnvCellId(id));
}
// ── Render inside/outside branch (retail RenderNormalMode is_player_outside) ──
// The top-level render branch decides DrawInside vs DrawOutside. Retail
// (SmartBox::RenderNormalMode 0x453aa0:92665) keys it on is_player_outside (the
// PLAYER's cell, 0x451e80), NOT the camera cell. acdream previously branched on the
// camera cell, so a chase camera lagging in a doorway while the player was already
// outside took the DrawInside path and degenerated to a grey world + entities showing
// through walls. These pin the player-keyed branch (DrawInside root stays the viewer cell).
[Fact]
public void ShouldRenderIndoor_PlayerOutside_CameraInside_ReturnsFalse()
{
// THE doorway-grey regression: the player stepped onto a landcell (0x...0031) but the
// chase camera still resolves an interior EnvCell. Branch on the PLAYER → outdoor.
Assert.False(RenderingDiagnostics.ShouldRenderIndoor(playerCellId: 0xA9B40031u, viewerCellResolved: true));
}
[Fact]
public void ShouldRenderIndoor_PlayerInside_CameraInside_ReturnsTrue()
{
Assert.True(RenderingDiagnostics.ShouldRenderIndoor(playerCellId: 0xA9B40171u, viewerCellResolved: true));
}
[Fact]
public void ShouldRenderIndoor_PlayerInside_NoViewerCell_ReturnsFalse()
{
// Opposite lag (camera pulled outside while the player is inside): no viewer cell to
// root DrawInside at → outdoor. Defensive; matches prior null-CameraCell behavior.
Assert.False(RenderingDiagnostics.ShouldRenderIndoor(playerCellId: 0xA9B40171u, viewerCellResolved: false));
}
[Fact]
public void ShouldRenderIndoor_PlayerOutside_CameraOutside_ReturnsFalse()
{
Assert.False(RenderingDiagnostics.ShouldRenderIndoor(playerCellId: 0xA9B40031u, viewerCellResolved: false));
}
[Fact]
public void ShouldRenderIndoor_UnknownPlayerCell_TreatedAsOutside_ReturnsFalse()
{
// playerCellId == 0 (unresolved) → treat as outside (safe default: outdoor render).
Assert.False(RenderingDiagnostics.ShouldRenderIndoor(playerCellId: 0u, viewerCellResolved: true));
}
}