fix(physics): validate retail cell containment roots

This commit is contained in:
Erik 2026-07-31 14:48:26 +02:00
parent 7716c2ee89
commit 3e0f3b6206
23 changed files with 429 additions and 197 deletions

View file

@ -7,9 +7,9 @@ namespace AcDream.Core.World.Cells;
/// <summary>
/// The unified cell graph: the active, authoritative id-&gt;cell resolver and registry.
/// Populated unconditionally from
/// <see cref="AcDream.Core.Physics.PhysicsDataCache.CacheCellStruct"/> (including
/// authored cells with null physics or containment roots) and consumed across
/// Populated from validated
/// <see cref="AcDream.Core.Physics.PhysicsDataCache.CacheCellStruct"/> payloads
/// (a physics root is optional; a containment root is required) and consumed across
/// the engine: <see cref="GetVisible"/> resolves any cell id, <see cref="CurrCell"/> is
/// the player render/lighting root, <see cref="FindVisibleChildCell"/> resolves the
/// 3rd-person camera cell, and <see cref="TryGetTerrainOrigin"/> supplies the block-local

View file

@ -11,9 +11,9 @@ namespace AcDream.Core.World.Cells;
public sealed class EnvCell : ObjCell
{
/// <summary>
/// Cell-containment BSP (retail CellStruct.CellBSP). A present tree with a
/// null root is universally inside; an absent test/tooling payload uses the
/// legacy AABB fallback.
/// Cell-containment BSP (retail CellStruct.CellBSP). Production publication
/// requires a non-null root; prepared production uses
/// <see cref="FlatContainmentBsp"/> instead.
/// </summary>
public CellBSPTree? ContainmentBsp { get; }
@ -38,14 +38,18 @@ public sealed class EnvCell : ObjCell
public override bool PointInCell(Vector3 worldPoint)
{
// Retail CEnvCell::point_in_cell @ 0x0052C300 returns false before
// touching the CellStruct when this->portals is null. Installed data
// contains real zero-portal cells, so this guard is behavior-bearing.
if (Portals.Count == 0)
return false;
var local = Vector3.Transform(worldPoint, InverseWorldTransform);
if (FlatContainmentBsp is not null)
if (FlatContainmentBsp is { RootIndex: >= 0 })
return FlatBspQuery.PointInsideCellBsp(FlatContainmentBsp, local);
if (ContainmentBsp is not null)
if (ContainmentBsp?.Root is not null)
return BSPQuery.PointInsideCellBsp(ContainmentBsp.Root, local); // BSPQuery.cs:1034
return local.X >= LocalBoundsMin.X && local.X <= LocalBoundsMax.X
&& local.Y >= LocalBoundsMin.Y && local.Y <= LocalBoundsMax.Y
&& local.Z >= LocalBoundsMin.Z && local.Z <= LocalBoundsMax.Z;
return false;
}
/// <summary>