feat(core): add SetupMesh.Flatten for single-level part hierarchy
This commit is contained in:
parent
473a06c534
commit
8f5b498be6
2 changed files with 155 additions and 0 deletions
45
src/AcDream.Core/Meshing/SetupMesh.cs
Normal file
45
src/AcDream.Core/Meshing/SetupMesh.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.World;
|
||||
using DatReaderWriter.DBObjs;
|
||||
using DatReaderWriter.Enums;
|
||||
using DatReaderWriter.Types;
|
||||
|
||||
namespace AcDream.Core.Meshing;
|
||||
|
||||
public static class SetupMesh
|
||||
{
|
||||
/// <summary>
|
||||
/// Flatten a Setup into a list of (GfxObjId, PartTransform) refs.
|
||||
/// Uses the default placement frame and DefaultScale per part.
|
||||
/// Does NOT walk ParentIndex — each part's transform is local to the setup root.
|
||||
/// This is simplification for Phase 2; complex hierarchical rigs are Phase 3.
|
||||
/// </summary>
|
||||
public static IReadOnlyList<MeshRef> Flatten(Setup setup)
|
||||
{
|
||||
AnimationFrame? defaultAnim = null;
|
||||
if (setup.PlacementFrames.TryGetValue(Placement.Default, out var af))
|
||||
defaultAnim = af;
|
||||
|
||||
var result = new List<MeshRef>(setup.Parts.Count);
|
||||
for (int i = 0; i < setup.Parts.Count; i++)
|
||||
{
|
||||
uint gfxObjId = (uint)setup.Parts[i];
|
||||
|
||||
Frame frame;
|
||||
if (defaultAnim is not null && i < defaultAnim.Frames.Count)
|
||||
frame = defaultAnim.Frames[i];
|
||||
else
|
||||
frame = new Frame { Origin = Vector3.Zero, Orientation = Quaternion.Identity };
|
||||
|
||||
Vector3 scale = i < setup.DefaultScale.Count ? setup.DefaultScale[i] : Vector3.One;
|
||||
|
||||
var transform =
|
||||
Matrix4x4.CreateScale(scale) *
|
||||
Matrix4x4.CreateFromQuaternion(frame.Orientation) *
|
||||
Matrix4x4.CreateTranslation(frame.Origin);
|
||||
|
||||
result.Add(new MeshRef(gfxObjId, transform));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
110
tests/AcDream.Core.Tests/Meshing/SetupMeshTests.cs
Normal file
110
tests/AcDream.Core.Tests/Meshing/SetupMeshTests.cs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Meshing;
|
||||
using DatReaderWriter.DBObjs;
|
||||
using DatReaderWriter.Enums;
|
||||
using DatReaderWriter.Types;
|
||||
|
||||
namespace AcDream.Core.Tests.Meshing;
|
||||
|
||||
public class SetupMeshTests
|
||||
{
|
||||
[Fact]
|
||||
public void Flatten_SinglePartSetup_YieldsOneMeshRef()
|
||||
{
|
||||
var setup = new Setup
|
||||
{
|
||||
Parts = { 0x01000100u },
|
||||
DefaultScale = { Vector3.One },
|
||||
PlacementFrames =
|
||||
{
|
||||
[Placement.Default] = new AnimationFrame(1)
|
||||
{
|
||||
Frames =
|
||||
{
|
||||
new Frame
|
||||
{
|
||||
Origin = new Vector3(0, 0, 0),
|
||||
Orientation = Quaternion.Identity,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var refs = SetupMesh.Flatten(setup);
|
||||
|
||||
var single = Assert.Single(refs);
|
||||
Assert.Equal(0x01000100u, single.GfxObjId);
|
||||
// Identity-ish transform
|
||||
Assert.Equal(Matrix4x4.Identity, single.PartTransform);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Flatten_TwoPartSetup_YieldsTwoMeshRefs()
|
||||
{
|
||||
var setup = new Setup
|
||||
{
|
||||
Parts = { 0x01000100u, 0x01000200u },
|
||||
DefaultScale = { Vector3.One, Vector3.One },
|
||||
PlacementFrames =
|
||||
{
|
||||
[Placement.Default] = new AnimationFrame(2)
|
||||
{
|
||||
Frames =
|
||||
{
|
||||
new Frame { Origin = new(0, 0, 0), Orientation = Quaternion.Identity },
|
||||
new Frame { Origin = new(10, 0, 0), Orientation = Quaternion.Identity },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var refs = SetupMesh.Flatten(setup);
|
||||
|
||||
Assert.Equal(2, refs.Count);
|
||||
Assert.Equal(0x01000100u, refs[0].GfxObjId);
|
||||
Assert.Equal(0x01000200u, refs[1].GfxObjId);
|
||||
// Second part is translated by 10 on X.
|
||||
Assert.Equal(10f, refs[1].PartTransform.Translation.X);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Flatten_PartScale_IsAppliedToTransform()
|
||||
{
|
||||
var setup = new Setup
|
||||
{
|
||||
Parts = { 0x01000100u },
|
||||
DefaultScale = { new Vector3(2, 3, 4) },
|
||||
PlacementFrames =
|
||||
{
|
||||
[Placement.Default] = new AnimationFrame(1)
|
||||
{
|
||||
Frames = { new Frame { Orientation = Quaternion.Identity } },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var refs = SetupMesh.Flatten(setup);
|
||||
|
||||
// The transform's M11 = 2 (scale X), M22 = 3, M33 = 4
|
||||
Assert.Equal(2f, refs[0].PartTransform.M11);
|
||||
Assert.Equal(3f, refs[0].PartTransform.M22);
|
||||
Assert.Equal(4f, refs[0].PartTransform.M33);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Flatten_MissingPlacementFrame_UsesIdentity()
|
||||
{
|
||||
var setup = new Setup
|
||||
{
|
||||
Parts = { 0x01000100u },
|
||||
DefaultScale = { Vector3.One },
|
||||
// PlacementFrames deliberately empty
|
||||
};
|
||||
|
||||
var refs = SetupMesh.Flatten(setup);
|
||||
|
||||
Assert.Single(refs);
|
||||
Assert.Equal(Matrix4x4.Identity, refs[0].PartTransform);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue