using System.Numerics;
using AcDream.Core.World;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Enums;
using DatReaderWriter.Types;
namespace AcDream.Core.Meshing;
public static class SetupMesh
{
///
/// 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.
///
public static IReadOnlyList Flatten(Setup setup, AnimationFrame? motionFrameOverride = null)
{
// Pose source priority:
// 1. motionFrameOverride — caller has resolved an idle/animation
// frame from the entity's MotionTable (Phase 6). This is the
// best source for creatures and characters because their
// Setup.PlacementFrames don't define an upright idle pose.
// 2. Setup.PlacementFrames[Resting] — used by static objects
// that have a Resting frame defined (e.g. signs, doors).
// 3. Setup.PlacementFrames[Default] — fallback for everything
// else (most scenery), which is the only frame those setups
// define and renders correctly.
//
// Without an override, creatures used to render in their Default
// pose (T-pose-ish or aggressive crouch) because their MotionTable
// wasn't consulted. The Phase 6 MotionResolver provides the override
// by walking Setup.DefaultMotionTable → MotionTable.Cycles →
// Animation.PartFrames[LowFrame].
AnimationFrame? defaultAnim = motionFrameOverride;
if (defaultAnim is null && setup.PlacementFrames.TryGetValue(Placement.Resting, out var resting))
defaultAnim = resting;
if (defaultAnim is null && setup.PlacementFrames.TryGetValue(Placement.Default, out var af))
defaultAnim = af;
// Last resort: use the first available placement frame (matches ACME's
// StaticObjectManager.GetDefaultPlacementFrame third fallback). Handles
// rare Setups that define only an unusual placement frame key.
if (defaultAnim is null)
{
foreach (var kvp in setup.PlacementFrames)
{
defaultAnim = kvp.Value;
break;
}
}
var result = new List(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;
}
}