using System.Collections.Generic; using System.Numerics; using DatReaderWriter.DBObjs; using DatReaderWriter.Enums; using DatReaderWriter.Types; namespace AcDream.Core.Meshing; /// /// Compute the per-part static transforms for a Setup using its /// PlacementFrames. For each part i, the returned matrix takes a /// point in part-local space and yields a point in setup-local space at /// the Setup's resting pose. /// /// /// Mirrors 's pose-source priority — /// PlacementFrames[Resting][Default] → first available /// — so that a particle anchor at part i matches the part's /// visible rest position. If renderer and resolver ever drift on this /// priority, particles will visibly drift relative to their parent /// mesh; keep them in lockstep. /// /// /// /// Returns an empty list when the Setup has no PlacementFrames. The /// caller (e.g. ParticleHookSink.SpawnFromHook) should then fall /// back to per part, which is the /// pre-C.1.5b behavior. /// /// /// /// For animated entities, per-part transforms vary per animation frame /// and live in AnimatedEntityState; a future "animated /// DefaultScript" path would publish those each tick via the same /// SetEntityPartTransforms seam. Out of scope here. /// /// public static class SetupPartTransforms { public static IReadOnlyList Compute(Setup setup) { AnimationFrame? source = null; if (setup.PlacementFrames.TryGetValue(Placement.Resting, out var resting)) { source = resting; } else if (setup.PlacementFrames.TryGetValue(Placement.Default, out var def)) { source = def; } else { foreach (var kvp in setup.PlacementFrames) { source = kvp.Value; break; } } if (source is null) return System.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 }; Vector3 scale = i < setup.DefaultScale.Count ? setup.DefaultScale[i] : Vector3.One; result[i] = Matrix4x4.CreateScale(scale) * Matrix4x4.CreateFromQuaternion(frame.Orientation) * Matrix4x4.CreateTranslation(frame.Origin); } return result; } }