feat(physics): Transition system data structures
SpherePath, CollisionInfo, ObjectInfo, TransitionState, PhysicsGlobals. Types match the pseudocode from transition_pseudocode.md, faithful to decompiled CTransition + ACE Transition.cs naming. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
13f56b62a0
commit
9ea8ae5191
1 changed files with 271 additions and 0 deletions
271
src/AcDream.Core/Physics/TransitionTypes.cs
Normal file
271
src/AcDream.Core/Physics/TransitionTypes.cs
Normal file
|
|
@ -0,0 +1,271 @@
|
||||||
|
using System.Numerics;
|
||||||
|
using DatReaderWriter.Types;
|
||||||
|
|
||||||
|
namespace AcDream.Core.Physics;
|
||||||
|
|
||||||
|
public enum TransitionState
|
||||||
|
{
|
||||||
|
Invalid = 0,
|
||||||
|
OK = 1,
|
||||||
|
Collided = 2,
|
||||||
|
Adjusted = 3,
|
||||||
|
Slid = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum InsertType
|
||||||
|
{
|
||||||
|
Transition = 0,
|
||||||
|
Placement = 1,
|
||||||
|
InitialPlacement = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum ObjectInfoState : uint
|
||||||
|
{
|
||||||
|
None = 0x000,
|
||||||
|
Contact = 0x001,
|
||||||
|
OnWalkable = 0x002,
|
||||||
|
IsViewer = 0x004,
|
||||||
|
PathClipped = 0x008,
|
||||||
|
FreeRotate = 0x010,
|
||||||
|
PerfectClip = 0x040,
|
||||||
|
IsImpenetrable = 0x080,
|
||||||
|
IsPlayer = 0x100,
|
||||||
|
EdgeSlide = 0x200,
|
||||||
|
IgnoreCreatures = 0x400,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Per-object flags and properties for the transition system.
|
||||||
|
/// ACE: ObjectInfo. Decompiled: struct at transition + various offsets.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ObjectInfo
|
||||||
|
{
|
||||||
|
public ObjectInfoState State;
|
||||||
|
public float StepUpHeight = 0.01f; // PhysicsGlobals.DefaultStepHeight
|
||||||
|
public float StepDownHeight = 0.04f;
|
||||||
|
public bool Ethereal;
|
||||||
|
public bool StepDown = true;
|
||||||
|
public float Scale = 1.0f;
|
||||||
|
|
||||||
|
// Convenience flag checks
|
||||||
|
public bool Contact => State.HasFlag(ObjectInfoState.Contact);
|
||||||
|
public bool OnWalkable => State.HasFlag(ObjectInfoState.OnWalkable);
|
||||||
|
public bool IsViewer => State.HasFlag(ObjectInfoState.IsViewer);
|
||||||
|
public bool IsPlayer => State.HasFlag(ObjectInfoState.IsPlayer);
|
||||||
|
public bool EdgeSlide => State.HasFlag(ObjectInfoState.EdgeSlide);
|
||||||
|
public bool PathClipped => State.HasFlag(ObjectInfoState.PathClipped);
|
||||||
|
public bool FreeRotate => State.HasFlag(ObjectInfoState.FreeRotate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Accumulated collision results for the current transition.
|
||||||
|
/// ACE: CollisionInfo.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CollisionInfo
|
||||||
|
{
|
||||||
|
public bool ContactPlaneValid;
|
||||||
|
public Plane ContactPlane;
|
||||||
|
public uint ContactPlaneCellId;
|
||||||
|
public bool ContactPlaneIsWater;
|
||||||
|
|
||||||
|
public bool LastKnownContactPlaneValid;
|
||||||
|
public Plane LastKnownContactPlane;
|
||||||
|
public uint LastKnownContactPlaneCellId;
|
||||||
|
public bool LastKnownContactPlaneIsWater;
|
||||||
|
|
||||||
|
public bool SlidingNormalValid;
|
||||||
|
public Vector3 SlidingNormal; // XY only (Z zeroed)
|
||||||
|
|
||||||
|
public bool CollisionNormalValid;
|
||||||
|
public Vector3 CollisionNormal;
|
||||||
|
|
||||||
|
public bool CollidedWithEnvironment;
|
||||||
|
public int FramesStationaryFall;
|
||||||
|
|
||||||
|
public Vector3 AdjustOffset;
|
||||||
|
public List<uint> CollideObjectGuids = new();
|
||||||
|
public uint? LastCollidedObjectGuid;
|
||||||
|
|
||||||
|
public void SetContactPlane(Plane plane, uint cellId, bool isWater = false)
|
||||||
|
{
|
||||||
|
ContactPlaneValid = true;
|
||||||
|
ContactPlane = plane;
|
||||||
|
ContactPlaneCellId = cellId;
|
||||||
|
ContactPlaneIsWater = isWater;
|
||||||
|
|
||||||
|
LastKnownContactPlaneValid = true;
|
||||||
|
LastKnownContactPlane = plane;
|
||||||
|
LastKnownContactPlaneCellId = cellId;
|
||||||
|
LastKnownContactPlaneIsWater = isWater;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSlidingNormal(Vector3 normal)
|
||||||
|
{
|
||||||
|
SlidingNormalValid = true;
|
||||||
|
SlidingNormal = new Vector3(normal.X, normal.Y, 0f);
|
||||||
|
if (SlidingNormal.LengthSquared() > PhysicsGlobals.EpsilonSq)
|
||||||
|
SlidingNormal = Vector3.Normalize(SlidingNormal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCollisionNormal(Vector3 normal)
|
||||||
|
{
|
||||||
|
CollisionNormalValid = true;
|
||||||
|
CollisionNormal = normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Movement path descriptor — tracks sphere positions in multiple
|
||||||
|
/// coordinate frames during collision resolution.
|
||||||
|
/// ACE: SpherePath.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class SpherePath
|
||||||
|
{
|
||||||
|
public int NumSphere = 1;
|
||||||
|
|
||||||
|
// Sphere arrays — index 0 = foot/body, index 1 = head (when NumSphere==2)
|
||||||
|
public readonly Sphere[] LocalSphere = new Sphere[2] { new(), new() };
|
||||||
|
public readonly Sphere[] GlobalSphere = new Sphere[2] { new(), new() };
|
||||||
|
public readonly Sphere[] GlobalCurrCenter = new Sphere[2] { new(), new() };
|
||||||
|
|
||||||
|
// Positions
|
||||||
|
public Vector3 BeginPos;
|
||||||
|
public Vector3 EndPos;
|
||||||
|
public Vector3 CurPos;
|
||||||
|
public Vector3 CheckPos;
|
||||||
|
public Quaternion BeginOrientation = Quaternion.Identity;
|
||||||
|
public Quaternion EndOrientation = Quaternion.Identity;
|
||||||
|
public Quaternion CurOrientation = Quaternion.Identity;
|
||||||
|
public Quaternion CheckOrientation = Quaternion.Identity;
|
||||||
|
|
||||||
|
// Cell tracking
|
||||||
|
public uint CurCellId;
|
||||||
|
public uint CheckCellId;
|
||||||
|
|
||||||
|
// Per-step offset
|
||||||
|
public Vector3 GlobalOffset;
|
||||||
|
|
||||||
|
// Step-up state
|
||||||
|
public bool StepUp;
|
||||||
|
public Vector3 StepUpNormal;
|
||||||
|
public bool Collide;
|
||||||
|
|
||||||
|
// Step-down state
|
||||||
|
public bool StepDown;
|
||||||
|
public float StepDownAmt;
|
||||||
|
public float WalkInterp = 1.0f;
|
||||||
|
|
||||||
|
// Walkable tracking
|
||||||
|
public bool WalkableValid;
|
||||||
|
public Plane WalkablePlane;
|
||||||
|
public float WalkableAllowance = PhysicsGlobals.FloorZ;
|
||||||
|
|
||||||
|
// Backup for restore
|
||||||
|
public Vector3 BackupCheckPos;
|
||||||
|
public uint BackupCheckCellId;
|
||||||
|
|
||||||
|
// Misc flags
|
||||||
|
public bool NegPolyHit;
|
||||||
|
public bool NegStepUp;
|
||||||
|
public Vector3 NegCollisionNormal;
|
||||||
|
public bool CheckWalkable;
|
||||||
|
public InsertType InsertType = InsertType.Transition;
|
||||||
|
|
||||||
|
public void SetCheckPos(Vector3 pos, uint cellId)
|
||||||
|
{
|
||||||
|
CheckPos = pos;
|
||||||
|
CheckCellId = cellId;
|
||||||
|
// Update global spheres to match new check position
|
||||||
|
for (int i = 0; i < NumSphere; i++)
|
||||||
|
{
|
||||||
|
GlobalSphere[i].Origin = LocalSphere[i].Origin + pos;
|
||||||
|
GlobalSphere[i].Radius = LocalSphere[i].Radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddOffsetToCheckPos(Vector3 offset)
|
||||||
|
{
|
||||||
|
CheckPos += offset;
|
||||||
|
for (int i = 0; i < NumSphere; i++)
|
||||||
|
GlobalSphere[i].Origin += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveCheckPos()
|
||||||
|
{
|
||||||
|
BackupCheckPos = CheckPos;
|
||||||
|
BackupCheckCellId = CheckCellId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RestoreCheckPos()
|
||||||
|
{
|
||||||
|
SetCheckPos(BackupCheckPos, BackupCheckCellId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the path for a simple point-to-point movement.
|
||||||
|
/// </summary>
|
||||||
|
public void InitPath(Vector3 begin, Vector3 end, uint cellId,
|
||||||
|
float sphereRadius, float sphereHeight = 0f)
|
||||||
|
{
|
||||||
|
BeginPos = begin;
|
||||||
|
EndPos = end;
|
||||||
|
CurPos = begin;
|
||||||
|
CurCellId = cellId;
|
||||||
|
|
||||||
|
LocalSphere[0].Origin = new Vector3(0, 0, sphereRadius);
|
||||||
|
LocalSphere[0].Radius = sphereRadius;
|
||||||
|
|
||||||
|
if (sphereHeight > 0)
|
||||||
|
{
|
||||||
|
NumSphere = 2;
|
||||||
|
LocalSphere[1].Origin = new Vector3(0, 0, sphereHeight - sphereRadius);
|
||||||
|
LocalSphere[1].Radius = sphereRadius;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NumSphere = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetCheckPos(begin, cellId);
|
||||||
|
|
||||||
|
// Also init CurCenter
|
||||||
|
for (int i = 0; i < NumSphere; i++)
|
||||||
|
{
|
||||||
|
GlobalCurrCenter[i].Origin = LocalSphere[i].Origin + begin;
|
||||||
|
GlobalCurrCenter[i].Radius = LocalSphere[i].Radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Physics constants matching the retail AC client.
|
||||||
|
/// ACE: PhysicsGlobals. Decompiled: various DAT_ addresses.
|
||||||
|
/// </summary>
|
||||||
|
public static class PhysicsGlobals
|
||||||
|
{
|
||||||
|
public const float EPSILON = 0.0002f;
|
||||||
|
public const float EpsilonSq = EPSILON * EPSILON;
|
||||||
|
public const float LandingZ = 0.0871557f;
|
||||||
|
public const float FloorZ = 0.6642f;
|
||||||
|
public const float DefaultStepHeight = 0.01f;
|
||||||
|
public const float Gravity = -9.8f;
|
||||||
|
public const float MaxVelocity = 50.0f;
|
||||||
|
public const float DummySphereRadius = 0.1f;
|
||||||
|
public const int MaxTransitionSteps = 30; // retail uses 30, ACE uses 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The main collision transition orchestrator.
|
||||||
|
/// ACE: Transition. Decompiled: CTransition.
|
||||||
|
/// Stub class — algorithm methods added in Task 6b-6d.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class Transition
|
||||||
|
{
|
||||||
|
public ObjectInfo ObjectInfo = new();
|
||||||
|
public SpherePath SpherePath = new();
|
||||||
|
public CollisionInfo CollisionInfo = new();
|
||||||
|
|
||||||
|
// Will be populated in Task 6b:
|
||||||
|
// public TransitionState FindTransitionalPosition(PhysicsEngine engine, PhysicsDataCache cache) { ... }
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue