test(render): Stage 3 T3.4 — CellGraphRootTests (6 tests)
New test file: tests/AcDream.Core.Tests/Rendering/CellGraphRootTests.cs. Tests 1-3: render-branch predicates (rootSeenOutside, playerInsideCell, renderSky): RootSelection_OutdoorRoot_NullCurrCell_SeenOutsideDefaultsToTrue RootSelection_BuildingInterior_SeenOutside_SkyRenderedAndSunKept RootSelection_Dungeon_NoSeenOutside_SkyNotRenderedAndSunZeroed Tests 4-6: CellGraph.FindVisibleChildCell: FindVisibleChildCell_PlayerCellContains_ReturnsPlayerCell FindVisibleChildCell_StabListContains_ReturnsNeighbour FindVisibleChildCell_NeitherContains_ReturnsNull All 6 pass. Core suite: 12 pre-existing failures (same baseline), 1276 passing. App suite: 160/160 pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
38a52a7dac
commit
573c5559a0
1 changed files with 208 additions and 0 deletions
208
tests/AcDream.Core.Tests/Rendering/CellGraphRootTests.cs
Normal file
208
tests/AcDream.Core.Tests/Rendering/CellGraphRootTests.cs
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
// CellGraphRootTests.cs — Stage 3 (2026-06-02): unit tests for render-root
|
||||
// selection logic and CellGraph.FindVisibleChildCell.
|
||||
//
|
||||
// Tests 1–3: render-branch predicates. These exercise the formulas in
|
||||
// GameWindow.OnRender directly (Stage 3):
|
||||
// rootSeenOutside = physicsRoot?.SeenOutside ?? true
|
||||
// playerInsideCell = cameraInsideCell && !rootSeenOutside
|
||||
// renderSky = !cameraInsideCell || rootSeenOutside
|
||||
//
|
||||
// Retail anchors:
|
||||
// CellManager::ChangePosition @ 0x004559B0 (pseudo_c:94649) — landscape kept
|
||||
// live iff seen_outside; sun zeroed when inside a sealed interior.
|
||||
// SmartBox::RenderNormalMode @ 0x00453aa0 (pseudo_c:92635) — sky gate.
|
||||
//
|
||||
// Tests 4–6: CellGraph.FindVisibleChildCell (retail find_visible_child_cell
|
||||
// @ 0x0052dc50, pseudo_c:311397).
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using AcDream.Core.World.Cells;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Tests.Rendering;
|
||||
|
||||
public class CellGraphRootTests
|
||||
{
|
||||
// ------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Synthetic EnvCell with an identity transform and axis-aligned bounds so
|
||||
/// PointInCell returns true for points inside [min, max].
|
||||
/// seenOutside = false → sealed dungeon; true → building interior/exterior.
|
||||
/// </summary>
|
||||
private static EnvCell MakeEnvCell(uint id, Vector3 min, Vector3 max, bool seenOutside = false)
|
||||
=> new EnvCell(
|
||||
id,
|
||||
Matrix4x4.Identity,
|
||||
Matrix4x4.Identity,
|
||||
min, max,
|
||||
portals: new List<CellPortal>(),
|
||||
stabList: new List<uint>(),
|
||||
seenOutside: seenOutside,
|
||||
containmentBsp: null);
|
||||
|
||||
/// <summary>
|
||||
/// EnvCell with an explicit stab list (used by FindVisibleChildCell tests).
|
||||
/// </summary>
|
||||
private static EnvCell MakeEnvCellWithStab(uint id, Vector3 min, Vector3 max,
|
||||
IReadOnlyList<uint> stabList, bool seenOutside = false)
|
||||
=> new EnvCell(
|
||||
id,
|
||||
Matrix4x4.Identity,
|
||||
Matrix4x4.Identity,
|
||||
min, max,
|
||||
portals: new List<CellPortal>(),
|
||||
stabList: stabList,
|
||||
seenOutside: seenOutside,
|
||||
containmentBsp: null);
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Predicate helpers — mirror the formulas in GameWindow.OnRender (Stage 3)
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
private static bool RootSeenOutside(EnvCell? physicsRoot) => physicsRoot?.SeenOutside ?? true;
|
||||
|
||||
// Retail CellManager::ChangePosition (0x004559B0): sun/sun-ambient zeroed
|
||||
// when inside a sealed interior (cameraInsideCell=true, seen_outside=false).
|
||||
// Building interiors with seen_outside keep the sun; outdoor root always keeps it.
|
||||
private static bool PlayerInsideCell(bool cameraInsideCell, bool rootSeenOutside)
|
||||
=> cameraInsideCell && !rootSeenOutside;
|
||||
|
||||
// Stage 3 sky gate: visible unless inside a sealed dungeon.
|
||||
private static bool RenderSky(bool cameraInsideCell, bool rootSeenOutside)
|
||||
=> !cameraInsideCell || rootSeenOutside;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Test 1: outdoor root (null CurrCell) — seen_outside=true, sky rendered,
|
||||
// player NOT considered inside a cell.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void RootSelection_OutdoorRoot_NullCurrCell_SeenOutsideDefaultsToTrue()
|
||||
{
|
||||
// When physicsRoot==null (pre-spawn, or outdoor landcell) the ?? true
|
||||
// gives rootSeenOutside=true; cameraInsideCell=false (no cell).
|
||||
bool rootSeenOutside = RootSeenOutside(null);
|
||||
bool cameraInsideCell = false; // no indoor root → ComputeVisibilityFromRoot returns null
|
||||
|
||||
bool playerInsideCell = PlayerInsideCell(cameraInsideCell, rootSeenOutside);
|
||||
bool renderSky = RenderSky(cameraInsideCell, rootSeenOutside);
|
||||
|
||||
Assert.True(rootSeenOutside, "outdoor null root → rootSeenOutside=true");
|
||||
Assert.False(playerInsideCell, "outdoor → playerInsideCell=false (sun kept live)");
|
||||
Assert.True(renderSky, "outdoor → sky rendered");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Test 2: building interior (seen_outside=true) — sky rendered (interim
|
||||
// full-screen until Stage 4 clips it), sun kept live.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void RootSelection_BuildingInterior_SeenOutside_SkyRenderedAndSunKept()
|
||||
{
|
||||
// A cottage cell: seen_outside=true (exit portal exists).
|
||||
var physicsRoot = MakeEnvCell(0xA9B40170u, Vector3.Zero, new Vector3(10, 10, 10),
|
||||
seenOutside: true);
|
||||
|
||||
bool rootSeenOutside = RootSeenOutside(physicsRoot);
|
||||
bool cameraInsideCell = true; // inside a cell → ComputeVisibilityFromRoot non-null
|
||||
|
||||
bool playerInsideCell = PlayerInsideCell(cameraInsideCell, rootSeenOutside);
|
||||
bool renderSky = RenderSky(cameraInsideCell, rootSeenOutside);
|
||||
|
||||
Assert.True(rootSeenOutside, "building interior seen_outside=true");
|
||||
Assert.False(playerInsideCell, "seen_outside=true → sun kept live");
|
||||
Assert.True(renderSky, "seen_outside=true → sky rendered (Stage 4 will clip to doorway)");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Test 3: sealed dungeon (seen_outside=false) — sky suppressed, sun zeroed.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void RootSelection_Dungeon_NoSeenOutside_SkyNotRenderedAndSunZeroed()
|
||||
{
|
||||
// A dungeon cell: seen_outside=false (no exit portal reachable).
|
||||
var physicsRoot = MakeEnvCell(0x01D90100u, Vector3.Zero, new Vector3(10, 10, 10),
|
||||
seenOutside: false);
|
||||
|
||||
bool rootSeenOutside = RootSeenOutside(physicsRoot);
|
||||
bool cameraInsideCell = true; // inside a cell → non-null visibility result
|
||||
|
||||
bool playerInsideCell = PlayerInsideCell(cameraInsideCell, rootSeenOutside);
|
||||
bool renderSky = RenderSky(cameraInsideCell, rootSeenOutside);
|
||||
|
||||
Assert.False(rootSeenOutside, "dungeon seen_outside=false");
|
||||
Assert.True(playerInsideCell, "sealed dungeon → playerInsideCell=true (sun zeroed)");
|
||||
Assert.False(renderSky, "sealed dungeon → sky suppressed");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Test 4: FindVisibleChildCell — query inside the root cell → returns root.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void FindVisibleChildCell_PlayerCellContains_ReturnsPlayerCell()
|
||||
{
|
||||
var graph = new CellGraph();
|
||||
var root = MakeEnvCell(0xA9B40170u, Vector3.Zero, new Vector3(10, 10, 10));
|
||||
graph.Add(root);
|
||||
|
||||
var point = new Vector3(5, 5, 5); // inside root's bounds
|
||||
var result = graph.FindVisibleChildCell(root.Id, point);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(root.Id, result!.Id);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Test 5: FindVisibleChildCell — query inside a stab-list neighbour → returns that cell.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void FindVisibleChildCell_StabListContains_ReturnsNeighbour()
|
||||
{
|
||||
var graph = new CellGraph();
|
||||
|
||||
// Root at [0,10]. Neighbour B at [20,30]. Root's stab list includes B.
|
||||
uint rootId = 0xA9B40170u;
|
||||
uint neighId = 0xA9B40171u;
|
||||
|
||||
var neigh = MakeEnvCell(neighId, new Vector3(20, 0, 0), new Vector3(30, 10, 10));
|
||||
var root = MakeEnvCellWithStab(rootId, Vector3.Zero, new Vector3(10, 10, 10),
|
||||
stabList: new List<uint> { neighId });
|
||||
|
||||
graph.Add(root);
|
||||
graph.Add(neigh);
|
||||
|
||||
var point = new Vector3(25, 5, 5); // inside neighbour, outside root
|
||||
var result = graph.FindVisibleChildCell(rootId, point);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(neighId, result!.Id);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Test 6: FindVisibleChildCell — query outside all cells → null.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
[Fact]
|
||||
public void FindVisibleChildCell_NeitherContains_ReturnsNull()
|
||||
{
|
||||
var graph = new CellGraph();
|
||||
uint rootId = 0xA9B40170u;
|
||||
|
||||
var root = MakeEnvCellWithStab(rootId, Vector3.Zero, new Vector3(10, 10, 10),
|
||||
stabList: new List<uint>());
|
||||
graph.Add(root);
|
||||
|
||||
var pointFarAway = new Vector3(999, 999, 999); // outside all cells
|
||||
var result = graph.FindVisibleChildCell(rootId, pointFarAway);
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue