perf(physics): reuse reset-complete transition scratch
Mirror retail's ten-deep LIFO transition lifetime, retain all query scratch with complete reset contracts, and remove Tier-0 enum boxing without changing collision decisions. Fresh and retained engines are bit-identical across the expanded oracle, while measured transition profiles now allocate 0 bytes per resolve.
This commit is contained in:
parent
2effba5127
commit
16d182c2f0
19 changed files with 3037 additions and 1228 deletions
|
|
@ -25,21 +25,21 @@ namespace AcDream.Core.Physics;
|
|||
[Flags]
|
||||
public enum PhysicsStateFlags : uint
|
||||
{
|
||||
None = 0x00000000,
|
||||
Static = 0x00000001,
|
||||
Ethereal = 0x00000004,
|
||||
ReportCollisions = 0x00000008,
|
||||
IgnoreCollisions = 0x00000010,
|
||||
NoDraw = 0x00000020,
|
||||
Missile = 0x00000040,
|
||||
Pushable = 0x00000080,
|
||||
AlignPath = 0x00000100,
|
||||
PathClipped = 0x00000200,
|
||||
Gravity = 0x00000400,
|
||||
Lighting = 0x00000800,
|
||||
ParticleEmitter = 0x00001000,
|
||||
Hidden = 0x00004000,
|
||||
ScriptedCollision = 0x00008000,
|
||||
None = 0x00000000,
|
||||
Static = 0x00000001,
|
||||
Ethereal = 0x00000004,
|
||||
ReportCollisions = 0x00000008,
|
||||
IgnoreCollisions = 0x00000010,
|
||||
NoDraw = 0x00000020,
|
||||
Missile = 0x00000040,
|
||||
Pushable = 0x00000080,
|
||||
AlignPath = 0x00000100,
|
||||
PathClipped = 0x00000200,
|
||||
Gravity = 0x00000400,
|
||||
Lighting = 0x00000800,
|
||||
ParticleEmitter = 0x00001000,
|
||||
Hidden = 0x00004000,
|
||||
ScriptedCollision = 0x00008000,
|
||||
/// <summary>
|
||||
/// A6.P7 (2026-05-25): retail HAS_PHYSICS_BSP_PS bit
|
||||
/// (acclient.h:2833). When set, the entity exposes a per-Setup
|
||||
|
|
@ -52,7 +52,7 @@ public enum PhysicsStateFlags : uint
|
|||
/// state 0x10008 (STATIC | REPORT_COLLISIONS | HAS_PHYSICS_BSP).
|
||||
/// ACE name: <c>PhysicsState.HasPhysicsBSP</c>.
|
||||
/// </summary>
|
||||
HasPhysicsBsp = 0x00010000,
|
||||
HasPhysicsBsp = 0x00010000,
|
||||
/// <summary>
|
||||
/// L.3a (2026-04-30): retail INELASTIC_PS bit (acclient.h:2834).
|
||||
/// When set, wall-collisions zero the velocity instead of reflecting.
|
||||
|
|
@ -60,14 +60,14 @@ public enum PhysicsStateFlags : uint
|
|||
/// impact rather than bounce. The player NEVER has this flag set —
|
||||
/// player wall-hits use the reflection path with elasticity ~0.05.
|
||||
/// </summary>
|
||||
Inelastic = 0x00020000,
|
||||
HasDefaultAnim = 0x00040000,
|
||||
HasDefaultScript = 0x00080000,
|
||||
Cloaked = 0x00100000,
|
||||
Inelastic = 0x00020000,
|
||||
HasDefaultAnim = 0x00040000,
|
||||
HasDefaultScript = 0x00080000,
|
||||
Cloaked = 0x00100000,
|
||||
ReportAsEnvironment = 0x00200000,
|
||||
EdgeSlide = 0x00400000,
|
||||
Sledding = 0x00800000,
|
||||
Frozen = 0x01000000,
|
||||
EdgeSlide = 0x00400000,
|
||||
Sledding = 0x00800000,
|
||||
Frozen = 0x01000000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -77,17 +77,17 @@ public enum PhysicsStateFlags : uint
|
|||
[Flags]
|
||||
public enum TransientStateFlags : uint
|
||||
{
|
||||
None = 0,
|
||||
Contact = 0x00000001, // bit 0 — touching any surface
|
||||
None = 0,
|
||||
Contact = 0x00000001, // bit 0 — touching any surface
|
||||
OnWalkable = 0x00000002, // bit 1 — standing on a walkable surface
|
||||
Sliding = 0x00000004, // bit 2 — carry sliding normal into next transition
|
||||
Sliding = 0x00000004, // bit 2 — carry sliding normal into next transition
|
||||
// retail frames_stationary_fall carried across frames: transition() seeds fsf from
|
||||
// these bits before the sweep (pc:280940-947); handle_all_collisions re-encodes fsf
|
||||
// into them at the end of the frame (pc:282743/282749/282753).
|
||||
StationaryFall = 0x00000010, // bit 4 — fsf == 1
|
||||
StationaryStop = 0x00000020, // bit 5 — fsf == 2
|
||||
StationaryFall = 0x00000010, // bit 4 — fsf == 1
|
||||
StationaryStop = 0x00000020, // bit 5 — fsf == 2
|
||||
StationaryStuck = 0x00000040, // bit 6 — fsf == 3
|
||||
Active = 0x00000080, // bit 7 — object needs per-frame update
|
||||
Active = 0x00000080, // bit 7 — object needs per-frame update
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -99,17 +99,17 @@ public sealed class PhysicsBody
|
|||
{
|
||||
// ── constants ──────────────────────────────────────────────────────────
|
||||
// From PhysicsGlobals.cs / confirmed by DAT_007c78a4 reference in decompiled code.
|
||||
public const float MaxVelocity = 50.0f;
|
||||
public const float MaxVelocity = 50.0f;
|
||||
public const float MaxVelocitySquared = MaxVelocity * MaxVelocity;
|
||||
public const float Gravity = -9.8f; // DAT_0082223c in FUN_00511420
|
||||
public const float SmallVelocity = 0.25f;
|
||||
public const float Gravity = -9.8f; // DAT_0082223c in FUN_00511420
|
||||
public const float SmallVelocity = 0.25f;
|
||||
public const float SmallVelocitySquared = SmallVelocity * SmallVelocity;
|
||||
public const float DefaultFriction = 0.95f;
|
||||
public const float MinQuantum = 1.0f / 30.0f; // ~0.0333 s
|
||||
public const float DefaultFriction = 0.95f;
|
||||
public const float MinQuantum = 1.0f / 30.0f; // ~0.0333 s
|
||||
// Matching-client disassembly resolves the named lift's stripped global to
|
||||
// 0.2 seconds. update_object consumes larger gaps as repeated 0.2 s quanta.
|
||||
public const float MaxQuantum = 0.2f;
|
||||
public const float HugeQuantum = 2.0f; // discard stale dt
|
||||
public const float MaxQuantum = 0.2f;
|
||||
public const float HugeQuantum = 2.0f; // discard stale dt
|
||||
|
||||
// ── struct fields ──────────────────────────────────────────────────────
|
||||
// Offsets from acclient_function_map.md §PhysicsObj Struct Layout.
|
||||
|
|
@ -336,6 +336,28 @@ public sealed class PhysicsBody
|
|||
/// <summary>Most recent walkable polygon vertices (world-space).</summary>
|
||||
public Vector3[]? WalkableVertices { get; set; }
|
||||
|
||||
// Transition publication retains one exact-length backing array. The
|
||||
// public property remains assignable for snapshot restoration and tests;
|
||||
// engine writeback uses this separate storage so clearing the logical
|
||||
// walkable reference does not force a new array on the next grounded
|
||||
// resolve.
|
||||
private Vector3[]? _walkableVertexStorage;
|
||||
|
||||
internal void SetWalkableVerticesExact(ReadOnlySpan<Vector3> source)
|
||||
{
|
||||
if (_walkableVertexStorage is null
|
||||
|| _walkableVertexStorage.Length != source.Length)
|
||||
{
|
||||
_walkableVertexStorage = new Vector3[source.Length];
|
||||
}
|
||||
|
||||
source.CopyTo(_walkableVertexStorage);
|
||||
WalkableVertices = _walkableVertexStorage;
|
||||
}
|
||||
|
||||
internal Vector3[]? RetainedWalkableVertexStorage
|
||||
=> _walkableVertexStorage;
|
||||
|
||||
/// <summary>Up vector used by the most recent walkable polygon probe.</summary>
|
||||
public Vector3 WalkableUp { get; set; } = Vector3.UnitZ;
|
||||
|
||||
|
|
@ -389,10 +411,10 @@ public sealed class PhysicsBody
|
|||
|
||||
// ── convenience helpers ────────────────────────────────────────────────
|
||||
|
||||
public bool HasGravity => State.HasFlag(PhysicsStateFlags.Gravity);
|
||||
public bool OnWalkable => TransientState.HasFlag(TransientStateFlags.OnWalkable);
|
||||
public bool IsActive => TransientState.HasFlag(TransientStateFlags.Active);
|
||||
public bool InContact => TransientState.HasFlag(TransientStateFlags.Contact);
|
||||
public bool HasGravity => (State & PhysicsStateFlags.Gravity) != 0;
|
||||
public bool OnWalkable => (TransientState & TransientStateFlags.OnWalkable) != 0;
|
||||
public bool IsActive => (TransientState & TransientStateFlags.Active) != 0;
|
||||
public bool InContact => (TransientState & TransientStateFlags.Contact) != 0;
|
||||
|
||||
// ── FUN_00511420 ───────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -411,16 +433,16 @@ public sealed class PhysicsBody
|
|||
/// </summary>
|
||||
public void calc_acceleration()
|
||||
{
|
||||
if (TransientState.HasFlag(TransientStateFlags.Contact) &&
|
||||
TransientState.HasFlag(TransientStateFlags.OnWalkable) &&
|
||||
!State.HasFlag(PhysicsStateFlags.Sledding))
|
||||
if ((TransientState & TransientStateFlags.Contact) != 0 &&
|
||||
(TransientState & TransientStateFlags.OnWalkable) != 0 &&
|
||||
(State & PhysicsStateFlags.Sledding) == 0)
|
||||
{
|
||||
Acceleration = Vector3.Zero;
|
||||
Omega = Vector3.Zero;
|
||||
return;
|
||||
}
|
||||
|
||||
if (State.HasFlag(PhysicsStateFlags.Gravity))
|
||||
if ((State & PhysicsStateFlags.Gravity) != 0)
|
||||
Acceleration = new Vector3(0f, 0f, Gravity);
|
||||
else
|
||||
Acceleration = Vector3.Zero;
|
||||
|
|
@ -540,7 +562,7 @@ public sealed class PhysicsBody
|
|||
/// </summary>
|
||||
public void calc_friction(float dt, float velocityMag2)
|
||||
{
|
||||
if (!TransientState.HasFlag(TransientStateFlags.OnWalkable))
|
||||
if ((TransientState & TransientStateFlags.OnWalkable) == 0)
|
||||
return;
|
||||
|
||||
float dot = Vector3.Dot(GroundNormal, Velocity);
|
||||
|
|
@ -553,7 +575,7 @@ public sealed class PhysicsBody
|
|||
float friction = Friction;
|
||||
|
||||
// Sledding modifies friction thresholds (from ACE cross-check).
|
||||
if (State.HasFlag(PhysicsStateFlags.Sledding))
|
||||
if ((State & PhysicsStateFlags.Sledding) != 0)
|
||||
{
|
||||
if (velocityMag2 < 1.5625f) // 1.25² — slow sled
|
||||
friction = 1.0f;
|
||||
|
|
@ -590,7 +612,7 @@ public sealed class PhysicsBody
|
|||
if (velocityMag2 <= 0f)
|
||||
{
|
||||
// No movement manager equivalent here; just clear Active if grounded.
|
||||
if (TransientState.HasFlag(TransientStateFlags.OnWalkable))
|
||||
if ((TransientState & TransientStateFlags.OnWalkable) != 0)
|
||||
TransientState &= ~TransientStateFlags.Active;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue