219 lines
6.7 KiB
C#
219 lines
6.7 KiB
C#
using System.Numerics;
|
|
using AcDream.Core.Meshing;
|
|
using AcDream.Core.World;
|
|
using DatReaderWriter.DBObjs;
|
|
using DatReaderWriter.Enums;
|
|
using DatReaderWriter.Types;
|
|
|
|
namespace AcDream.Core.Tests.Meshing;
|
|
|
|
public sealed class EquippedChildAttachmentTests
|
|
{
|
|
[Fact]
|
|
public void TryCompose_CombinesChildPlacementHoldingFrameAndAnimatedHandPart()
|
|
{
|
|
var parent = new Setup
|
|
{
|
|
HoldingLocations =
|
|
{
|
|
[ParentLocation.LeftHand] = new LocationType
|
|
{
|
|
PartId = 1,
|
|
Frame = FrameAt(2, 0, 0),
|
|
},
|
|
},
|
|
};
|
|
var parentPose = new[]
|
|
{
|
|
Matrix4x4.Identity,
|
|
Matrix4x4.CreateTranslation(10, 0, 0),
|
|
};
|
|
var child = new Setup
|
|
{
|
|
Parts = { 0x01000001u },
|
|
DefaultScale = { Vector3.One },
|
|
PlacementFrames =
|
|
{
|
|
[Placement.LeftHand] = new AnimationFrame(1)
|
|
{
|
|
Frames = { FrameAt(3, 0, 0) },
|
|
},
|
|
},
|
|
};
|
|
var template = new[] { new MeshRef(0x01000001u, Matrix4x4.Identity) };
|
|
|
|
bool ok = EquippedChildAttachment.TryComposePose(
|
|
parent, parentPose, child, ParentLocation.LeftHand,
|
|
Placement.LeftHand, template, 1.0f, out EquippedChildPose pose);
|
|
|
|
Assert.True(ok);
|
|
Assert.Equal(12f, pose.RootLocal.Translation.X);
|
|
Assert.Equal(3f, Assert.Single(pose.PartLocal).Translation.X);
|
|
Assert.Equal(15f, Assert.Single(pose.AttachedParts).PartTransform.Translation.X);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryCompose_MissingRequestedPlacementFallsBackToDefault()
|
|
{
|
|
var parent = ParentWithRightHand(partId: -1, FrameAt(4, 0, 0));
|
|
var child = new Setup
|
|
{
|
|
Parts = { 0x01000001u },
|
|
PlacementFrames =
|
|
{
|
|
[Placement.Default] = new AnimationFrame(1)
|
|
{
|
|
Frames = { FrameAt(6, 0, 0) },
|
|
},
|
|
},
|
|
};
|
|
|
|
bool ok = EquippedChildAttachment.TryCompose(
|
|
parent, [], child, ParentLocation.RightHand,
|
|
Placement.RightHandCombat,
|
|
[new MeshRef(0x01000001u, Matrix4x4.Identity)],
|
|
1.0f,
|
|
out var result);
|
|
|
|
Assert.True(ok);
|
|
Assert.Equal(10f, Assert.Single(result).PartTransform.Translation.X);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryCompose_MissingHoldingLocationRejectsAttachment()
|
|
{
|
|
bool ok = EquippedChildAttachment.TryCompose(
|
|
new Setup(), [], new Setup(), ParentLocation.RightHand,
|
|
Placement.RightHandCombat, [], 1.0f, out var result);
|
|
|
|
Assert.False(ok);
|
|
Assert.Empty(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryComposePoseInto_UnavailableValidPartRejectsWithoutRootFallback()
|
|
{
|
|
var parent = ParentWithRightHand(partId: 1, FrameAt(4, 0, 0));
|
|
var child = ChildWithOnePart();
|
|
|
|
bool ok = EquippedChildAttachment.TryComposePoseInto(
|
|
parent,
|
|
[Matrix4x4.Identity, Matrix4x4.CreateTranslation(10, 0, 0)],
|
|
[true, false],
|
|
child,
|
|
ParentLocation.RightHand,
|
|
Placement.Default,
|
|
[new MeshRef(0x01000001u, Matrix4x4.Identity)],
|
|
1f,
|
|
null,
|
|
null,
|
|
out _);
|
|
|
|
Assert.False(ok);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryComposePoseInto_OutOfRangePartUsesRetailRootAndReusesBuffers()
|
|
{
|
|
var parent = ParentWithRightHand(partId: 99, FrameAt(4, 0, 0));
|
|
var child = ChildWithOnePart();
|
|
var poseBuffer = new Matrix4x4[1];
|
|
var meshBuffer = new MeshRef[1];
|
|
|
|
bool ok = EquippedChildAttachment.TryComposePoseInto(
|
|
parent,
|
|
[Matrix4x4.CreateTranslation(10, 0, 0)],
|
|
[true],
|
|
child,
|
|
ParentLocation.RightHand,
|
|
Placement.Default,
|
|
[new MeshRef(0x01000001u, Matrix4x4.Identity)],
|
|
1f,
|
|
poseBuffer,
|
|
meshBuffer,
|
|
out EquippedChildPose pose);
|
|
|
|
Assert.True(ok);
|
|
Assert.Same(poseBuffer, pose.PartLocal);
|
|
Assert.Same(meshBuffer, pose.AttachedParts);
|
|
Assert.Equal(4f, pose.RootLocal.Translation.X);
|
|
}
|
|
|
|
[Fact]
|
|
public void NestedChildRootsComposeThroughImmediateParentWorldPose()
|
|
{
|
|
Matrix4x4 topLevelWorld = Matrix4x4.CreateTranslation(100, 0, 0);
|
|
Matrix4x4 childRootLocal = Matrix4x4.CreateTranslation(10, 0, 0);
|
|
Matrix4x4 grandchildRootLocal = Matrix4x4.CreateTranslation(2, 0, 0);
|
|
|
|
Matrix4x4 childWorld = childRootLocal * topLevelWorld;
|
|
Matrix4x4 grandchildWorld = grandchildRootLocal * childWorld;
|
|
|
|
Assert.Equal(112f, grandchildWorld.Translation.X);
|
|
}
|
|
|
|
[Fact]
|
|
public void ChildRigidPoseExcludesDefaultScaleAndScalesOnlyOrigin()
|
|
{
|
|
var parent = ParentWithRightHand(partId: -1, FrameAt(4, 0, 0));
|
|
var child = new Setup
|
|
{
|
|
Parts = { 0x01000001u },
|
|
DefaultScale = { new Vector3(5f, 6f, 7f) },
|
|
PlacementFrames =
|
|
{
|
|
[Placement.Default] = new AnimationFrame(1)
|
|
{
|
|
Frames = { FrameAt(3, 0, 0) },
|
|
},
|
|
},
|
|
};
|
|
|
|
Assert.True(EquippedChildAttachment.TryComposePose(
|
|
parent,
|
|
Array.Empty<Matrix4x4>(),
|
|
child,
|
|
ParentLocation.RightHand,
|
|
Placement.Default,
|
|
[new MeshRef(0x01000001u, Matrix4x4.Identity)],
|
|
childScale: 2f,
|
|
out EquippedChildPose pose));
|
|
|
|
Matrix4x4 rigid = Assert.Single(pose.PartLocal);
|
|
Assert.Equal(new Vector3(6f, 0f, 0f), rigid.Translation);
|
|
Assert.Equal(Vector3.One, new Vector3(rigid.M11, rigid.M22, rigid.M33));
|
|
Matrix4x4 visual = Assert.Single(pose.AttachedParts).PartTransform;
|
|
Assert.Equal(new Vector3(10f, 12f, 14f), new Vector3(visual.M11, visual.M22, visual.M33));
|
|
}
|
|
|
|
private static Setup ChildWithOnePart() => new()
|
|
{
|
|
Parts = { 0x01000001u },
|
|
DefaultScale = { Vector3.One },
|
|
PlacementFrames =
|
|
{
|
|
[Placement.Default] = new AnimationFrame(1)
|
|
{
|
|
Frames = { FrameAt(1, 0, 0) },
|
|
},
|
|
},
|
|
};
|
|
|
|
private static Setup ParentWithRightHand(int partId, Frame frame) => new()
|
|
{
|
|
HoldingLocations =
|
|
{
|
|
[ParentLocation.RightHand] = new LocationType
|
|
{
|
|
PartId = partId,
|
|
Frame = frame,
|
|
},
|
|
},
|
|
};
|
|
|
|
private static Frame FrameAt(float x, float y, float z) => new()
|
|
{
|
|
Origin = new Vector3(x, y, z),
|
|
Orientation = Quaternion.Identity,
|
|
};
|
|
}
|