using System.Collections.Generic; using System.Numerics; using DatReaderWriter.DBObjs; using DatReaderWriter.Enums; using DatReaderWriter.Types; namespace AcDream.Core.Meshing; /// /// Computes retail's rigid per-part frames for attachments and effects. /// Visual GfxObj scale is deliberately excluded. /// /// /// Pose selection matches : an explicit motion /// frame, then Resting, Default, then the first placement frame. Retail /// CPartArray::UpdateParts (0x005190F0) scales only the frame /// origin by the object's scale. CPartArray::SetScaleInternal /// (0x00518A00) stores Setup DefaultScale separately on the visual /// CPhysicsPart::gfxobj_scale, so it never enters this rigid frame. /// public static class SetupPartTransforms { public static IReadOnlyList Compute( Setup setup, AnimationFrame? motionFrameOverride = null, float objectScale = 1.0f) { ArgumentNullException.ThrowIfNull(setup); AnimationFrame? source = motionFrameOverride; if (source is null && setup.PlacementFrames.TryGetValue(Placement.Resting, out var resting)) { source = resting; } else if (source is null && setup.PlacementFrames.TryGetValue(Placement.Default, out var def)) { source = def; } else if (source is null) { foreach (var kvp in setup.PlacementFrames) { source = kvp.Value; break; } } if (source is null) return Array.Empty(); int partCount = setup.Parts.Count; var result = new Matrix4x4[partCount]; for (int i = 0; i < partCount; i++) { Frame frame = i < source.Frames.Count ? source.Frames[i] : new Frame { Origin = Vector3.Zero, Orientation = Quaternion.Identity }; result[i] = Matrix4x4.CreateFromQuaternion(frame.Orientation) * Matrix4x4.CreateTranslation(frame.Origin * objectScale); } return result; } }