feat(vfx): bind effects to live animated poses
This commit is contained in:
parent
96ddfdf175
commit
542dcfc384
41 changed files with 3246 additions and 741 deletions
|
|
@ -25,8 +25,8 @@ public sealed class EquippedChildAttachmentTests
|
|||
};
|
||||
var parentPose = new[]
|
||||
{
|
||||
new MeshRef(1, Matrix4x4.Identity),
|
||||
new MeshRef(2, Matrix4x4.CreateTranslation(10, 0, 0)),
|
||||
Matrix4x4.Identity,
|
||||
Matrix4x4.CreateTranslation(10, 0, 0),
|
||||
};
|
||||
var child = new Setup
|
||||
{
|
||||
|
|
@ -42,12 +42,14 @@ public sealed class EquippedChildAttachmentTests
|
|||
};
|
||||
var template = new[] { new MeshRef(0x01000001u, Matrix4x4.Identity) };
|
||||
|
||||
bool ok = EquippedChildAttachment.TryCompose(
|
||||
bool ok = EquippedChildAttachment.TryComposePose(
|
||||
parent, parentPose, child, ParentLocation.LeftHand,
|
||||
Placement.LeftHand, template, 1.0f, out var result);
|
||||
Placement.LeftHand, template, 1.0f, out EquippedChildPose pose);
|
||||
|
||||
Assert.True(ok);
|
||||
Assert.Equal(15f, Assert.Single(result).PartTransform.Translation.X);
|
||||
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]
|
||||
|
|
@ -88,6 +90,115 @@ public sealed class EquippedChildAttachmentTests
|
|||
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 =
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ public class SetupPartTransformsTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Compute_AppliesDefaultScale_WhenPresent()
|
||||
public void Compute_ExcludesVisualDefaultScale_FromRigidPartFrame()
|
||||
{
|
||||
// DefaultScale = (2,2,2) on part 0. An input (1,1,1) should
|
||||
// come out (2,2,2) after the part transform — confirms the
|
||||
|
|
@ -113,6 +113,37 @@ public class SetupPartTransformsTests
|
|||
|
||||
Assert.Single(transforms);
|
||||
var probe = Vector3.Transform(new Vector3(1f, 1f, 1f), transforms[0]);
|
||||
Assert.Equal(new Vector3(2f, 2f, 2f), probe);
|
||||
Assert.Equal(new Vector3(1f, 1f, 1f), probe);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Compute_ObjectScaleMultipliesOriginButNotOrientationAxes()
|
||||
{
|
||||
var setup = new Setup
|
||||
{
|
||||
Parts = { 0x01000100u },
|
||||
DefaultScale = { new Vector3(7f, 8f, 9f) },
|
||||
PlacementFrames =
|
||||
{
|
||||
[Placement.Resting] = new AnimationFrame(1)
|
||||
{
|
||||
Frames =
|
||||
{
|
||||
new Frame
|
||||
{
|
||||
Origin = new Vector3(2f, 0f, 0f),
|
||||
Orientation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, MathF.PI / 2f),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Matrix4x4 rigid = Assert.Single(SetupPartTransforms.Compute(setup, objectScale: 3f));
|
||||
|
||||
Assert.Equal(new Vector3(6f, 0f, 0f), rigid.Translation);
|
||||
Vector3 rotatedAxis = Vector3.TransformNormal(Vector3.UnitX, rigid);
|
||||
Assert.InRange(rotatedAxis.X, -0.0001f, 0.0001f);
|
||||
Assert.InRange(rotatedAxis.Y, 0.9999f, 1.0001f);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue