using System.Numerics; using AcDream.Content; using DatReaderWriter; using DatReaderWriter.DBObjs; namespace AcDream.App.Rendering; /// /// Shared primitives behind retail's "resolve a rest-pose DID via master-map /// slot 7, load its Animation, hold the final frame" algorithm — the /// mechanism (paperdoll, /// gmPaperDollUI::RedressCreature @ 0x004A3C22) and /// (chargen preview, /// gmCG3DView::StopAnimation @ 0x004EE640) both implement. Extracted /// per the CC6a review's F11/F12 note ("before adding a FOURTH consumer... a /// shared RetailHeldPose helper is worth extracting before a fourth /// held-pose consumer exists") — CC6b's own idle-loop work makes chargen's /// implementation grow enough that mechanically sharing the two primitives /// BOTH sites already had byte-identical (DID resolution, final-frame /// transform composition) is a clean win without forcing the two sites' /// slightly different per-index LOOP shapes (paperdoll walks an /// already-built, already-filtered WorldEntity.MeshRefs; chargen /// walks the pre-filter, Setup-part-indexed scratch list) into one method /// they don't actually share. /// internal static class RetailHeldPose { /// /// DBCache::GetDIDFromEnumStatic(poseEnum, 7) equivalent: master /// map → slot 7's sub-map → 's Animation DID. /// Returns 0 if any link in the chain is missing. MUST be called under /// the caller's dat lock (see 's /// datLock doc — DatCollection is not thread-safe). /// public static uint ResolvePoseDid(IDatReaderWriter dats, uint poseEnum) { uint masterDid = (uint)dats.Portal.Db.Header.MasterMapId; if (masterDid == 0 || !dats.Portal.TryGet(masterDid, out var master) || !master.ClientEnumToID.TryGetValue(7u, out uint subDid) || !dats.Portal.TryGet(subDid, out var sub)) { return 0u; } return sub.ClientEnumToID.TryGetValue(poseEnum, out uint did) ? did : 0u; } /// /// Retail's per-part pose transform: Scale(defaultScale) * /// Rotate(orientation) * Translate(origin) — the SAME composition /// both RetailPaperdollPoseApplicator.Apply and /// 's pose steps use, whether /// the (origin, orientation) pair comes from a held final frame or an /// interpolated idle-cycle frame. /// public static Matrix4x4 ComposePartTransform(Vector3 defaultScale, Vector3 origin, Quaternion orientation) => Matrix4x4.CreateScale(defaultScale) * Matrix4x4.CreateFromQuaternion(orientation) * Matrix4x4.CreateTranslation(origin); }