using AcDream.Core.Physics;
using AcDream.Core.Physics.Motion;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Types;
namespace AcDream.App.Physics;
///
/// Resolves the motion table's authored default-state part pose used by a
/// live object's collision PartArray. Retail keeps the collision parts in the
/// same current pose as the visual PartArray; an unresolved table falls back
/// to Setup placement frames in .
/// The retained default-pose snapshot approximation is tracked as AP-84 in
/// the retail divergence register until live per-frame collision poses land.
///
internal sealed class LiveEntityDefaultPoseResolver
{
private readonly Func _loadMotionTable;
private readonly IAnimationLoader _animationLoader;
private readonly bool _dumpMotion;
public LiveEntityDefaultPoseResolver(
Func loadMotionTable,
IAnimationLoader animationLoader,
bool dumpMotion)
{
_loadMotionTable = loadMotionTable
?? throw new ArgumentNullException(nameof(loadMotionTable));
_animationLoader = animationLoader
?? throw new ArgumentNullException(nameof(animationLoader));
_dumpMotion = dumpMotion;
}
public IReadOnlyList? Resolve(uint motionTableId, int partCount)
{
if (motionTableId == 0u || partCount == 0)
return null;
MotionTable? motionTable = _loadMotionTable(motionTableId);
if (motionTable is null)
return null;
IReadOnlyList? pose = MotionTablePose.DefaultStatePartFrames(
motionTable,
_animationLoader.LoadAnimation);
if (_dumpMotion)
{
string description = pose is null
? "null->placement-fallback"
: FormattableString.Invariant(
$"part0=({pose[0].Origin.X:F2},{pose[0].Origin.Y:F2},{pose[0].Origin.Z:F2})");
Console.WriteLine(FormattableString.Invariant(
$"[shape-pose] mt=0x{motionTableId:X8} parts={partCount} {description}"));
}
return pose;
}
}