feat(physics): port retail complete object frame pipeline

Restore the named-retail object update order across local, remote, static, projectile, animation, shadow, teleport, and effect lifetimes. Separate authoritative root commits from spatial rebucketing, preserve per-owner hook/FIFO ordering, and remove update-path allocations with exact lifecycle and residency gates.

Add deterministic conformance, adversarial lifetime, GUID-reuse, pending-cell, quaternion, timestamp, and allocation coverage. Release build is warning-free and all 6,446 tests pass with five intentional skips; retail, architecture, and adversarial reviews are clean.

Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-20 09:10:31 +02:00
parent 31a0889f08
commit f961d70023
77 changed files with 12513 additions and 1871 deletions

View file

@ -1,6 +1,7 @@
using System;
using System.Numerics;
using AcDream.Core.Physics;
using AcDream.Core.Physics.Motion;
using Xunit;
namespace AcDream.Core.Tests.Physics;
@ -27,6 +28,164 @@ public sealed class InterpolationManagerTests
private static InterpolationManager Make() => new InterpolationManager();
[Fact]
public void AdjustOffset_CompleteFrame_ReplacesOriginAndNonCommutingOrientation()
{
var manager = Make();
Quaternion current = Quaternion.CreateFromAxisAngle(Vector3.UnitX, 1.1f);
Quaternion target = Quaternion.CreateFromAxisAngle(Vector3.UnitY, -0.9f);
manager.Enqueue(
new Vector3(10f, 0f, 0f),
target,
isMovingTo: false,
currentBodyPosition: Vector3.Zero);
var offset = new MotionDeltaFrame
{
Origin = new Vector3(7f, 8f, 9f),
Orientation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, 0.4f),
};
bool overwritten = manager.AdjustOffset(
dt: 0.1,
currentBodyPosition: Vector3.Zero,
currentBodyOrientation: current,
maxSpeedFromMinterp: 4f,
offset);
Assert.True(overwritten);
Vector3 expectedLocalOrigin = MoveToMath.GlobalToLocalVec(
current,
new Vector3(0.8f, 0f, 0f));
Assert.Equal(expectedLocalOrigin.X, offset.Origin.X, 5);
Assert.Equal(expectedLocalOrigin.Y, offset.Origin.Y, 5);
Assert.Equal(expectedLocalOrigin.Z, offset.Origin.Z, 5);
Quaternion expectedOrientation = Quaternion.Normalize(
Quaternion.Inverse(current) * target);
Assert.InRange(
MathF.Abs(Quaternion.Dot(
expectedOrientation,
Quaternion.Normalize(offset.Orientation))),
0.99999f,
1.00001f);
}
[Fact]
public void AdjustOffset_MoveToNode_KeepsCurrentHeading()
{
var manager = Make();
manager.Enqueue(
new Vector3(10f, 0f, 0f),
Quaternion.CreateFromAxisAngle(Vector3.UnitY, -0.9f),
isMovingTo: true,
currentBodyPosition: Vector3.Zero);
var offset = new MotionDeltaFrame
{
Orientation = Quaternion.CreateFromAxisAngle(Vector3.UnitX, 0.6f),
};
Assert.True(manager.AdjustOffset(
0.1,
Vector3.Zero,
Quaternion.CreateFromAxisAngle(Vector3.UnitZ, 0.2f),
4f,
offset));
Assert.Equal(Quaternion.Identity, offset.Orientation);
}
[Fact]
public void AdjustOffset_UsesManagerWideLatestKeepHeadingFlag()
{
var manager = Make();
Quaternion current = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, 0.2f);
manager.Enqueue(
new Vector3(10f, 0f, 0f),
Quaternion.CreateFromAxisAngle(Vector3.UnitY, 0.7f),
isMovingTo: false,
currentBodyPosition: Vector3.Zero,
currentBodyOrientation: current);
manager.Enqueue(
new Vector3(20f, 0f, 0f),
Quaternion.CreateFromAxisAngle(Vector3.UnitX, -0.6f),
isMovingTo: true,
currentBodyPosition: Vector3.Zero,
currentBodyOrientation: current);
var offset = new MotionDeltaFrame();
Assert.True(manager.AdjustOffset(
0.1,
Vector3.Zero,
current,
4f,
offset));
// keep_heading is an InterpolationManager field in retail, not a
// property of each queued node. The latest near enqueue therefore
// controls the current head as well.
Assert.Equal(Quaternion.Identity, offset.Orientation);
}
[Fact]
public void AdjustOffset_WithoutContact_LeavesFrameAndQueueUntouched()
{
var manager = Make();
manager.Enqueue(
FarTarget,
Quaternion.CreateFromAxisAngle(Vector3.UnitY, 0.7f),
isMovingTo: false,
currentBodyPosition: BodyOrigin);
var offset = new MotionDeltaFrame
{
Origin = new Vector3(1f, 2f, 3f),
Orientation = Quaternion.CreateFromAxisAngle(Vector3.UnitX, 0.4f),
};
Vector3 originalOrigin = offset.Origin;
Quaternion originalOrientation = offset.Orientation;
bool overwritten = manager.AdjustOffset(
0.1,
BodyOrigin,
Quaternion.Identity,
4f,
offset,
inContact: false);
Assert.False(overwritten);
Assert.Equal(originalOrigin, offset.Origin);
Assert.Equal(originalOrientation, offset.Orientation);
Assert.True(manager.IsActive);
}
[Fact]
public void Enqueue_AlreadyClose_InstallsOnlyTargetHeading()
{
var manager = Make();
Quaternion target = Quaternion.Normalize(
Quaternion.CreateFromAxisAngle(Vector3.UnitX, 0.8f)
* Quaternion.CreateFromAxisAngle(Vector3.UnitZ, -0.6f));
Quaternion? immediate = manager.Enqueue(
new Vector3(0.01f, 0f, 0f),
target,
isMovingTo: false,
currentBodyPosition: Vector3.Zero);
Assert.NotNull(immediate);
Quaternion expected = MoveToMath.SetHeading(
target,
MoveToMath.GetHeading(target));
Assert.InRange(
MathF.Abs(Quaternion.Dot(
Quaternion.Normalize(expected),
Quaternion.Normalize(immediate!.Value))),
0.99999f,
1.00001f);
Assert.True(MathF.Abs(Quaternion.Dot(
Quaternion.Normalize(target),
Quaternion.Normalize(immediate.Value))) < 0.999f);
Assert.False(manager.IsActive);
}
// =========================================================================
// Queue mechanics
// =========================================================================