feat(core): UCG Stage 1 — ObjCell base + CellPortal
Introduces AcDream.Core.World.Cells namespace with the two foundational types for the Unified Cell Graph. CellPortal is a readonly struct unifying the three legacy portal representations; ObjCell is the abstract base for all traversable cells with the retail id-magnitude IsEnv discriminator (CObjCell::GetVisible, pseudo_c:308215). Zero consumers; zero behavior change. 5/5 tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bd0244f203
commit
9cb15710be
3 changed files with 111 additions and 0 deletions
32
src/AcDream.Core/World/Cells/CellPortal.cs
Normal file
32
src/AcDream.Core/World/Cells/CellPortal.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
namespace AcDream.Core.World.Cells;
|
||||
|
||||
/// <summary>
|
||||
/// Unified cell-to-cell portal edge. Superset of the three legacy portal types
|
||||
/// (render <c>CellPortalInfo</c>, physics <c>PortalInfo</c>, <c>PortalPlane</c>).
|
||||
/// Retail anchor: CCellPortal (acclient.h:32300).
|
||||
/// </summary>
|
||||
public readonly struct CellPortal
|
||||
{
|
||||
public uint OtherCellId { get; }
|
||||
public ushort OtherPortalId { get; }
|
||||
public ushort PolygonId { get; }
|
||||
public ushort Flags { get; }
|
||||
/// <summary>Matches the physics <c>PortalInfo.PortalSide</c> convention (PortalInfo.cs:44).</summary>
|
||||
public bool PortalSide => (Flags & 0x2) == 0;
|
||||
/// <summary>Cell-local portal polygon vertices. Carried now; consumed by PView at Stage 3.</summary>
|
||||
public IReadOnlyList<Vector3> PolygonLocal { get; }
|
||||
|
||||
public CellPortal(uint otherCellId, ushort otherPortalId, ushort polygonId, ushort flags,
|
||||
IReadOnlyList<Vector3>? polygonLocal = null)
|
||||
{
|
||||
OtherCellId = otherCellId;
|
||||
OtherPortalId = otherPortalId;
|
||||
PolygonId = polygonId;
|
||||
Flags = flags;
|
||||
PolygonLocal = polygonLocal ?? Array.Empty<Vector3>();
|
||||
}
|
||||
}
|
||||
43
src/AcDream.Core/World/Cells/ObjCell.cs
Normal file
43
src/AcDream.Core/World/Cells/ObjCell.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
namespace AcDream.Core.World.Cells;
|
||||
|
||||
/// <summary>
|
||||
/// Base for every cell the player can stand in. Retail anchor: CObjCell
|
||||
/// (acclient.h:30915). The id magnitude is the type discriminator
|
||||
/// (<see cref="IsEnv"/>): low-16 >= 0x100 => indoor <see cref="EnvCell"/>,
|
||||
/// else outdoor <see cref="LandCell"/>.
|
||||
/// </summary>
|
||||
public abstract class ObjCell
|
||||
{
|
||||
public uint Id { get; }
|
||||
public Matrix4x4 WorldTransform { get; }
|
||||
public Matrix4x4 InverseWorldTransform { get; }
|
||||
public Vector3 LocalBoundsMin { get; }
|
||||
public Vector3 LocalBoundsMax { get; }
|
||||
public IReadOnlyList<CellPortal> Portals { get; }
|
||||
public IReadOnlyList<uint> StabList { get; }
|
||||
public bool SeenOutside { get; }
|
||||
|
||||
/// <summary>Retail magnitude dispatch (CObjCell::GetVisible, pseudo_c:308215).</summary>
|
||||
public bool IsEnv => (Id & 0xFFFFu) >= 0x100u;
|
||||
|
||||
protected ObjCell(uint id, Matrix4x4 worldTransform, Matrix4x4 inverseWorldTransform,
|
||||
Vector3 localBoundsMin, Vector3 localBoundsMax,
|
||||
IReadOnlyList<CellPortal> portals, IReadOnlyList<uint> stabList,
|
||||
bool seenOutside)
|
||||
{
|
||||
Id = id;
|
||||
WorldTransform = worldTransform;
|
||||
InverseWorldTransform = inverseWorldTransform;
|
||||
LocalBoundsMin = localBoundsMin;
|
||||
LocalBoundsMax = localBoundsMax;
|
||||
Portals = portals;
|
||||
StabList = stabList;
|
||||
SeenOutside = seenOutside;
|
||||
}
|
||||
|
||||
/// <summary>Retail CObjCell::point_in_cell (vtable +0x84). Is a world point inside this cell?</summary>
|
||||
public abstract bool PointInCell(Vector3 worldPoint);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue