diff options
Diffstat (limited to 'Poser.cs')
| -rw-r--r-- | Poser.cs | 2320 |
1 files changed, 2320 insertions, 0 deletions
diff --git a/Poser.cs b/Poser.cs new file mode 100644 index 0000000..39ca033 --- /dev/null +++ b/Poser.cs @@ -0,0 +1,2320 @@ +//#define QUARANTINED_FEATURES + +using System.Numerics; +using System.Runtime.InteropServices; +using System.Globalization; +using System.Text; +using System.Text.Json; + +using ImGuiNET; +using SharpPluginLoader.Core; +using SharpPluginLoader.Core.MtTypes; +using SharpPluginLoader.Core.Entities; +using SharpPluginLoader.Core.Memory; +using SharpPluginLoader.Core.Rendering; + +namespace NewCamera +{ + using static Plugin; + + public unsafe class Poser + { + private const string posePath = "nativePC/plugins/CSharp/Poses"; + + private static class Armor + { + public const byte Body = 0; + public const byte Helmet = 1; + public const byte Arm = 2; + public const byte Waist = 3; + public const byte Leg = 4; + public const byte Slinger = 5; + public const byte Hair = 6; + public const byte Face = 7; + public const byte EyeLens = 8; + } + + private static class Joint + { + public const byte Root = 0x0; + public const byte Root2 = 0xD; + public const byte LowerBack = 0x1; + public const byte UpperBack = 0x2; + public const byte LowerNeck = 0x3; + public const byte LowerNeckNull = 0xFE; + public const byte UpperNeck = 0x4; + public const byte LeftCollarbone = 0x5; + public const byte LeftShoulderNull = 0x46; + public const byte LeftShoulder = 0x6; + public const byte LeftElbow = 0x7; + public const byte LeftWrist = 0x8; + public const byte LeftWristNull = 0x1E; + public const byte LeftThumbLow = 0x1F; + public const byte LeftThumbMid = 0x20; + public const byte LeftThumbHigh = 0x21; + public const byte LeftIndexLow = 0x22; + public const byte LeftIndexMid = 0x23; + public const byte LeftIndexHigh = 0x24; + public const byte LeftMiddleLow = 0x25; + public const byte LeftMiddleMid = 0x26; + public const byte LeftMiddleHigh = 0x27; + public const byte LeftPalm = 0x28; + public const byte LeftRingLow = 0x29; + public const byte LeftRingMid = 0x2A; + public const byte LeftRingHigh = 0x2B; + public const byte LeftPinkyLow = 0x2C; + public const byte LeftPinkyMid = 0x2D; + public const byte LeftPinkyHigh = 0x2E; + public const byte LeftArmUnk1 = 0x51; + public const byte LeftArmUnk2 = 0x65; + public const byte LeftArmUnk3 = 0x66; + public const byte LeftArmUnk4 = 0x67; + public const byte LeftArmUnk5 = 0x68; + public const byte LeftArmUnk6 = 0x47; + public const byte LeftArmUnk7 = 0x50; + public const byte RightCollarbone = 0x9; + public const byte RightShoulder = 0xA; + public const byte RightElbow = 0xB; + public const byte RightWrist = 0xC; + public const byte RightWristNull = 0x2F; + public const byte RightThumbLow = 0x30; + public const byte RightThumbMid = 0x31; + public const byte RightThumbHigh = 0x32; + public const byte RightIndexLow = 0x33; + public const byte RightIndexMid = 0x34; + public const byte RightIndexHigh = 0x35; + public const byte RightMiddleLow = 0x36; + public const byte RightMiddleMid = 0x37; + public const byte RightMiddleHigh = 0x38; + public const byte RightPalm = 0x39; + public const byte RightRingLow = 0x3A; + public const byte RightRingMid = 0x3B; + public const byte RightRingHigh = 0x3C; + public const byte RightPinkyLow = 0x3D; + public const byte RightPinkyMid = 0x3E; + public const byte RightPinkyHigh = 0x3F; + public const byte RightArmUnk1 = 0x53; + public const byte RightArmUnk2 = 0x49; + public const byte RightArmUnk3 = 0x52; + public const byte RightArmUnk4 = 0x48; + public const byte Unknown1 = 0xF7; + public const byte Unknown2 = 0xF8; + public const byte LeftHip = 0xE; + public const byte LeftKnee = 0xF; + public const byte LeftAnkle = 0x10; + public const byte LeftToes = 0x11; + public const byte LeftAnkleNull = 0x54; + public const byte LeftKneeNull = 0x4B; + public const byte LeftHipNull = 0x4A; + public const byte Unknown3 = 0x40; + public const byte Unknown4 = 0x42; + public const byte RightHip = 0x12; + public const byte RightKnee = 0x13; + public const byte RightAnkle = 0x14; + public const byte RightToes = 0x15; + public const byte RightAnkleNull = 0x55; + public const byte RightKneeNull = 0x4D; + public const byte RightHipNull = 0x4C; + public const byte Unknown5 = 0x43; + public const byte Unknown6 = 0x45; + public const byte Unknown7 = 0xF9; + public const byte Unknown8 = 0xFA; + public const byte Unknown9 = 0xFB; + public const byte Unknown10 = 0xFC; + public const byte Unknown11 = 0xFD; + } + + private static class FaceJoint + { + public const byte LeftCheek1 = 0x6C; + public const byte LeftCheek2 = 0x99; + public const byte RightCheek1 = 0x71; + public const byte RightCheek2 = 0x9E; + public const byte RightCheek3 = 0x70; + public const byte LeftCheek3 = 0x6B; + public const byte RightCheek4 = 0x6F; + public const byte LeftCheek4 = 0x6A; + public const byte RightCheek5 = 0x72; + public const byte LeftCheek5 = 0x6D; + public const byte LeftCheek = 0x6E; + public const byte RightCheek = 0x73; + public const byte LeftEye = 0x3A; + public const byte LeftEye1 = 0x3B; + public const byte LeftEye2 = 0x43; + public const byte LeftEye3 = 0x42; + public const byte LeftEye4 = 0x41; + public const byte LeftEye5 = 0x40; + public const byte LeftEye6 = 0x3F; + public const byte LeftEye7 = 0x46; + public const byte LeftEye8 = 0x45; + public const byte LeftEye9 = 0x44; + public const byte LeftEye10 = 0x3C; + public const byte LeftEye11 = 0x3E; + public const byte LeftEye12 = 0x3D; + public const byte LeftEye13 = 0x68; + public const byte RightEye = 0x47; + public const byte RightEye1 = 0x48; + public const byte RightEye2 = 0x50; + public const byte RightEye3 = 0x4F; + public const byte RightEye4 = 0x4E; + public const byte RightEye5 = 0x4D; + public const byte RightEye6 = 0x4C; + public const byte RightEye7 = 0x51; + public const byte RightEye8 = 0x52; + public const byte RightEye9 = 0x53; + public const byte RightEye10 = 0x4A; + public const byte RightEye11 = 0x4B; + public const byte RightEye12 = 0x49; + public const byte RightEye13 = 0x69; + public const byte Nose = 0x54; + public const byte Nose1 = 0x55; + public const byte Nose2 = 0x56; + public const byte Nose3 = 0x58; + public const byte Nose4 = 0x64; + public const byte Nose5 = 0x5E; + public const byte Nose6 = 0x5F; + public const byte Nose7 = 0x65; + public const byte Nose8 = 0x5C; + public const byte Nose9 = 0x62; + public const byte Nose10 = 0x5D; + public const byte Nose11 = 0x63; + public const byte Nose12 = 0x5B; + public const byte Nose13 = 0x61; + public const byte Nose14 = 0x5A; + public const byte Nose15 = 0x60; + public const byte Nose16 = 0x57; + public const byte Nose17 = 0x59; + public const byte Nose18 = 0x66; + public const byte Nose19 = 0x67; + public const byte Mouth = 0x75; + public const byte Mouth1 = 0x80; + public const byte Mouth2 = 0x81; + public const byte Mouth3 = 0x86; + public const byte Mouth4 = 0x85; + public const byte Mouth5 = 0x84; + public const byte Mouth6 = 0x83; + public const byte Mouth7 = 0x82; + public const byte Mouth8 = 0x8B; + public const byte Mouth9 = 0x8A; + public const byte Mouth10 = 0x89; + public const byte Mouth11 = 0x88; + public const byte Mouth12 = 0x87; + public const byte Mouth13 = 0x8F; + public const byte Mouth14 = 0x8E; + public const byte Mouth15 = 0x90; + public const byte Mouth16 = 0x8D; + public const byte Mouth17 = 0x91; + public const byte Mouth18 = 0x8C; + public const byte Mouth19 = 0x92; + public const byte Mouth20 = 0x76; + public const byte Mouth21 = 0x77; + public const byte Mouth22 = 0x78; + public const byte Mouth23 = 0x79; + public const byte Mouth24 = 0x7A; + public const byte Mouth25 = 0x7F; + public const byte Mouth26 = 0x7E; + public const byte Mouth27 = 0x7D; + public const byte Mouth28 = 0x7C; + public const byte Mouth29 = 0x7B; + public const byte Mouth30 = 0x74; + public const byte ChinMiddle = 0x94; + public const byte ChinRight = 0x9C; + public const byte ChinLeft = 0x9B; + public const byte ChinRightBack = 0x9D; + public const byte ChinLeftBack = 0x9A; + public const byte NeckMiddle = 0xA0; + public const byte Jaw = 0x97; + public const byte Jaw1 = 0xEA; + public const byte Jaw2 = 0xEB; + public const byte JawLeft = 0x98; + public const byte JawRight = 0x96; + public const byte JawUpLeft = 0x93; + public const byte JawUpRight = 0x95; + public const byte NeckLeft = 0x9F; + public const byte NeckRight = 0xA1; + public const byte EyeAgainLeft = 0xF4; + public const byte EyeAgainLeft1 = 0xF5; + public const byte NoseAgain = 0xF6; + public const byte MouthAgain = 0xF7; + public const byte Forehead0 = 0xF8; + public const byte Forehead = 0xA2; + public const byte Forehead1 = 0x36; + public const byte Forehead2 = 0x37; + public const byte Forehead3 = 0x38; + public const byte Forehead4 = 0x35; + public const byte Forehead5 = 0x34; + public const byte Forehead6 = 0x33; + public const byte Forehead7 = 0x32; + public const byte Forehead8 = 0x2E; + public const byte Forehead9 = 0x2D; + public const byte Forehead10 = 0x2F; + public const byte Forehead11 = 0x2C; + public const byte Forehead12 = 0x30; + public const byte Forehead13 = 0x31; + public const byte Forehead14 = 0x39; + public const byte Base1 = 0xFB; + public const byte Base2 = 0xFC; + public const byte Base3 = 0xFD; + } + + private static nint[] playerArmor = new nint[9]; + private static List<nint> playerWeapon = new List<nint>(); + private static nint[] bodyJointMap = new nint[0xFF]; + private static nint[] faceJointMap = new nint[0xFF]; + private static int lastArmorRefresh = -1; + + private delegate void UpdateJoints(nint obj); + private static Hook<UpdateJoints>? updateJointRotationHook; + private static Hook<UpdateJoints>? updateJointPositionHook; + + public static List<nint> bodyJoints = new List<nint>(); + private static List<nint> ignoredJoints = new List<nint>(); + private static Dictionary<nint, Vector4> bodyRotations = new Dictionary<nint, Vector4>(); + private static Dictionary<nint, Vector3> bodyPositions = new Dictionary<nint, Vector3>(); + private static Dictionary<nint, Vector3> bodyOffsets = new Dictionary<nint, Vector3>(); + + private static List<nint> faceJoints = new List<nint>(); + private static Dictionary<nint, Vector4> faceRotations = new Dictionary<nint, Vector4>(); + private static Dictionary<nint, Vector3> facePositions = new Dictionary<nint, Vector3>(); + private static Dictionary<nint, Vector3> faceOffsets = new Dictionary<nint, Vector3>(); + + private static List<nint> jiggleBones = new List<nint>(); + + private static bool ikForcedOff = false; + private static Patch forceDisableIK; +#if QUARANTINED_FEATURES + private delegate void UpdateIKPosition(nint superOfChild, nint joint, nint obj); + private static Hook<UpdateIKPosition>? updateIKPositionHook; + private static List<nint> ikJoints = new List<nint>(); + private static Dictionary<nint, Vector3> ikOffsets = new Dictionary<nint, Vector3>(); +#endif + + public static void Initialize() + { + forceDisableIK = new Patch(0x14031FD40, [0xC6, 0x81, 0x81, 0x01, 0x00, 0x00, 0x00, 0xC3]); + + updateJointRotationHook = Hook.Create<UpdateJoints>(0x14223BA30, UpdateJointRotationHook); + updateJointPositionHook = Hook.Create<UpdateJoints>(0x141FBCCD0, UpdateJointPositionHook); +#if QUARANTINED_FEATURES + updateIKPositionHook = Hook.Create<UpdateIKPosition>(0x142472C00, UpdateIKPositionHook); +#endif + } + + private static void dropArmorState() + { + Array.Clear(playerArmor, 0, playerArmor.Length); + playerWeapon.Clear(); + Array.Clear(bodyJointMap, 0, bodyJointMap.Length); + Array.Clear(faceJointMap, 0, faceJointMap.Length); + bodyJoints.Clear(); + ignoredJoints.Clear(); + faceJoints.Clear(); +#if QUARANTINED_FEATURES + ikJoints.Clear(); +#endif + jiggleBones.Clear(); + PlayerExt.Attach.DropState(); + } + + private static void dropJointOffsets() + { + bodyRotations.Clear(); + bodyPositions.Clear(); + bodyOffsets.Clear(); + faceRotations.Clear(); + facePositions.Clear(); + faceOffsets.Clear(); +#if QUARANTINED_FEATURES + ikOffsets.Clear(); +#endif + } + + private static void refreshArmorState(Player player) + { + dropArmorState(); + if (!collectArmorAndJoints(player)) + { + dropJointOffsets(); + } + /* + if (playerArmor[Armor.Waist] != 0x0) + { + if (hideKnife) + { + MemoryUtil.GetRef<byte>(playerArmor[Armor.Waist] + 0x2D0) &= 0xFD; + } + else + { + MemoryUtil.GetRef<byte>(playerArmor[Armor.Waist] + 0x2D0) |= 0x2; + } + } + */ + lastArmorRefresh = updateTick; + } + + private static void collectArmorParts(nint baseAddr, nint addr) + { + int part; + string fullString = Marshal.PtrToStringAnsi(addr + 0xC)!; + string partName = Models.extractPartName(fullString); + if (partName == "body") part = Armor.Body; + else if (partName == "helm") part = Armor.Helmet; + else if (partName == "arm") part = Armor.Arm; + else if (partName == "wst") part = Armor.Waist; + else if (partName == "leg") part = Armor.Leg; + else if (partName.StartsWith("slg")) part = Armor.Slinger; + else if (partName.StartsWith("hair")) part = Armor.Hair; + else if (partName == "face000") part = Armor.Face; + else if (partName == "face000_eyelens") part = Armor.EyeLens; + else if (fullString.StartsWith("wp\\")) + { + playerWeapon.Add(baseAddr); + return; + } + else return; + playerArmor[part] = baseAddr; + + int jointCount = MemoryUtil.Read<int>(baseAddr + 0x4A0); + nint jointAddr = MemoryUtil.Read<nint>(baseAddr + 0x4A8); + for (int i = 0; i < jointCount; i++) + { + nint childAddr = MemoryUtil.Read<nint>(jointAddr + 0x8); + if (childAddr != 0x0 && MemoryUtil.Read<nint>(childAddr) == 0x142F21A90) + { + childAddr = MemoryUtil.Read<nint>(childAddr + 0x8); + if (childAddr != 0x0) + { + nint childVtable = MemoryUtil.Read<nint>(childAddr); + if (childVtable == 0x1435195B8) // Jiggle bone container. + { + if (part == Armor.Body) + { + ignoredJoints.Add(jointAddr); + } + } + else if (childVtable == 0x143524BD8) + { + childAddr = MemoryUtil.Read<nint>(childAddr + 0x188); + if (childAddr != 0x0 && MemoryUtil.Read<nint>(childAddr) == 0x143519530) + { + nint jiggleData = MemoryUtil.Read<nint>(MemoryUtil.Read<nint>(childAddr + 0xA0)); + if (MemoryUtil.Read<nint>(jiggleData) == 0x143519508) + { + if (!jiggleBones.Contains(jiggleData)) + { + int jiggleIndex = jiggleBones.Count(); + jiggleBones.Add(jiggleData); + PlayerExt.Attach.OnBone(jiggleData, jiggleIndex, part == Armor.Body); + } + } + } + if (part == Armor.Body) + { + ignoredJoints.Add(jointAddr); + } + } +#if QUARANTINED_FEATURES + else if (childVtable == 0x143249170) + { + childAddr = MemoryUtil.Read<nint>(childAddr + 0x188); + if (childAddr != 0x0 && MemoryUtil.Read<nint>(childAddr) == 0x1432492F0) + { + ikJoints.Add(jointAddr); + } + } +#endif + } + } + if (part == Armor.Body) + { + bodyJoints.Add(jointAddr); + } + else if (part == Armor.Face) + { + faceJoints.Add(jointAddr); + } + jointAddr += 0xC0; + } + + if (part == Armor.Body) + { + for (int i = 0; i < bodyJoints.Count; i++) + { + jointAddr = bodyJoints[i]; + if (ignoredJoints.Contains(jointAddr)) + { + continue; + } + byte id = MemoryUtil.Read<byte>(jointAddr + 0xA0); + Assert(bodyJointMap[id] == 0x0); + bodyJointMap[id] = jointAddr; + } + foreach ((nint joint, _) in bodyRotations.Where((e) => !bodyJoints.Contains(e.Key))) bodyRotations.Remove(joint); + foreach ((nint joint, _) in bodyPositions.Where((e) => !bodyJoints.Contains(e.Key))) bodyPositions.Remove(joint); + foreach ((nint joint, _) in bodyOffsets.Where((e) => !bodyJoints.Contains(e.Key))) bodyOffsets.Remove(joint); +#if QUARANTINED_FEATURES + foreach ((nint joint, _) in ikOffsets.Where((e) => !bodyJoints.Contains(e.Key))) ikOffsets.Remove(joint); +#endif + } + else if (part == Armor.Face) + { + for (int i = 0; i < faceJoints.Count; i++) + { + jointAddr = faceJoints[i]; + byte id = MemoryUtil.Read<byte>(jointAddr + 0xA0); + Assert(faceJointMap[id] == 0x0); + faceJointMap[id] = jointAddr; + } + foreach ((nint joint, _) in faceRotations.Where((e) => !faceJoints.Contains(e.Key))) faceRotations.Remove(joint); + foreach ((nint joint, _) in facePositions.Where((e) => !faceJoints.Contains(e.Key))) facePositions.Remove(joint); + foreach ((nint joint, _) in faceOffsets.Where((e) => !faceJoints.Contains(e.Key))) faceOffsets.Remove(joint); + } + } + + private static bool collectArmorAndJoints(Player player) + { + nint bodyArmorParts = MemoryUtil.Read<nint>(player.Instance + 0x2A0); + if (bodyArmorParts == 0x0) + { + return false; + } + // MonsterHunterWorld.exe+203E8A2 - mov rax,[rbx+000126C8] + nint combineArmorList = MemoryUtil.Read<nint>(player.Instance + 0x126C8); + if (combineArmorList == 0x0) + { + return false; + } + collectArmorParts(player.Instance, bodyArmorParts); + nint next, partsAddr; + nint weapon = MemoryUtil.Read<nint>(player.Instance + 0x76B0); + if (weapon != 0x0) + { + partsAddr = MemoryUtil.Read<nint>(weapon + 0x2A0); + if (partsAddr != 0x0) + { + collectArmorParts(weapon, partsAddr); + } + nint weaponParts = MemoryUtil.Read<nint>(weapon + 0x21D8); + next = weaponParts; + for (;next != 0x0;) + { + weaponParts = next; + next = MemoryUtil.Read<nint>(weaponParts + 0x30); + nint vTable = MemoryUtil.Read<nint>(weaponParts); + if (vTable == 0x1434D1D58) // uWeaponEmblem + { + if (MemoryUtil.Read<nint>(weaponParts + 0x590) != player.Instance) + { + continue; + } + nint parentAddr = MemoryUtil.Read<nint>(weaponParts + 0x5A8); + if (parentAddr != 0x0) + { + vTable = MemoryUtil.Read<nint>(parentAddr); + if (vTable == 0x1434D7C88 || vTable == 0x1434D7928) // uWpSubGeneral || uWpQuiver + { + if (MemoryUtil.Read<nint>(parentAddr + 0x9B0) != player.Instance) + { + continue; + } + partsAddr = MemoryUtil.Read<nint>(parentAddr + 0x2A0); + if (partsAddr != 0x0) + { + collectArmorParts(parentAddr, partsAddr); + } + } + } + } + else if (vTable == 0x1434D2400) // uWeaponParts + { + if (MemoryUtil.Read<nint>(weaponParts + 0x990) != player.Instance) + { + continue; + } + } + else + { + continue; + } + partsAddr = MemoryUtil.Read<nint>(weaponParts + 0x2A0); + if (partsAddr != 0x0) + { + collectArmorParts(weaponParts, partsAddr); + } + } + } + nint slinger = MemoryUtil.Read<nint>(player.Instance + 0x8918); + if (slinger != 0x0) + { + partsAddr = MemoryUtil.Read<nint>(slinger + 0x2A0); + if (partsAddr != 0x0) + { + collectArmorParts(slinger, partsAddr); + } + } + combineArmorList = MemoryUtil.Read<nint>(combineArmorList + 0x70); + next = combineArmorList; + for (;next != 0x0;) + { + nint armorAddr = next; + next = MemoryUtil.Read<nint>(armorAddr + 0x30); + nint vTable = MemoryUtil.Read<nint>(armorAddr); + if (vTable == 0x1434A7560) // uPlCombineArmor + { + nint parentAddr = MemoryUtil.Read<nint>(armorAddr + 0x548); + if (parentAddr != 0x0) + { + if (MemoryUtil.Read<nint>(parentAddr) == 0x143451520) // Face. + { + if (MemoryUtil.Read<nint>(parentAddr + 0x998) != player.Instance) + { + continue; + } + if (playerArmor[Armor.Face] == 0x0) + { + partsAddr = MemoryUtil.Read<nint>(parentAddr + 0x2A0); + if (partsAddr != 0x0) + { + collectArmorParts(parentAddr, partsAddr); + } + nint childAddr = MemoryUtil.Read<nint>(parentAddr + 0x9C8); + if (childAddr != 0x0 && MemoryUtil.Read<nint>(childAddr) == 0x143505E98) // Eye lens. + { + partsAddr = MemoryUtil.Read<nint>(childAddr + 0x2A0); + if (partsAddr != 0x0) + { + collectArmorParts(childAddr, partsAddr); + } + } + } + } + else if (parentAddr != player.Instance) + { + continue; + } + } + } + else + { + continue; + } + partsAddr = MemoryUtil.Read<nint>(armorAddr + 0x2A0); + if (partsAddr != 0x0) + { + collectArmorParts(armorAddr, partsAddr); + } + if (!playerArmor.Where((p) => p == 0x0).Any()) + { + break; + } + } + return true; + } + + private static void UpdateJointRotationHook(nint model) + { + debugLog($"UpdateJointRotationHook(0x{model:X}) @ {updateTick}"); + + Player? player = null; + bool updateBody = bodyRotations.Count() > 0 || (enableFreeCamera && orbitPlayer && orbitJoint >= 0); + bool updateFace = faceRotations.Count() > 0; + if (updateBody || updateFace) + { + player = PlayerExt.getPlayerWithFallback(); + } + + if (updateBody) + { + if (player != null && model == player.Instance) + { + refreshArmorState(player); + foreach ((nint addr, Vector4 rotation) in bodyRotations) + { + MemoryUtil.GetRef<Quaternion>(addr + 0x70) = new Quaternion(rotation.X, rotation.Y, rotation.Z, rotation.W); + } + } + } + + if (updateFace) + { + if (player != null && model == playerArmor[Armor.Face]) + { + if (!updateBody) + { + refreshArmorState(player); + } + else + { + Assert(lastArmorRefresh == updateTick); + } + foreach ((nint addr, Vector4 rotation) in faceRotations) + { + MemoryUtil.GetRef<Quaternion>(addr + 0x70) = new Quaternion(rotation.X, rotation.Y, rotation.Z, rotation.W); + } + } + } + + updateJointRotationHook!.Original(model); + } + + private static void UpdateJointPositionHook(nint model) + { + debugLog($"UpdateJointPositionHook(0x{model:X}) @ {updateTick}"); + + updateJointPositionHook!.Original(model); + + Player? player = null; + bool updateBody = bodyPositions.Count() > 0 || bodyOffsets.Count() > 0; + bool updateFace = facePositions.Count() > 0 || faceOffsets.Count() > 0; + if (updateBody || updateFace) + { + player = PlayerExt.getPlayerWithFallback(); + } + + if (updateBody) + { + if (player != null && model == player.Instance) + { + refreshArmorState(player); + Quaternion rootRotation = new Quaternion(player.Rotation.X, player.Rotation.Y, player.Rotation.Z, player.Rotation.W); + Vector3 rootForward = Vector3.Transform(new Vector3(0.0f, 0.0f, 1.0f), rootRotation); + Vector3 rootRight = Vector3.Transform(new Vector3(1.0f, 0.0f, 0.0f), rootRotation); + Vector3 rootUp = new Vector3(0.0f, 1.0f, 0.0f); + foreach ((nint addr, Vector3 position) in bodyPositions) + { + MemoryUtil.GetRef<Vector3>(addr + 0x50) = player.Position + ((rootForward * position.X) + (rootUp * position.Y) + (rootRight * position.Z)); + } + foreach ((nint addr, Vector3 offset) in bodyOffsets) + { + MemoryUtil.GetRef<Vector3>(addr + 0x50) += ((rootForward * offset.X) + (rootUp * offset.Y) + (rootRight * offset.Z)); + } + } + } + + if (updateFace) + { + if (player != null && model == playerArmor[Armor.Face]) + { + if (!updateBody) + { + refreshArmorState(player); + } + else + { + Assert(lastArmorRefresh == updateTick); + } + Quaternion rootRotation = new Quaternion(player.Rotation.X, player.Rotation.Y, player.Rotation.Z, player.Rotation.W); + Vector3 rootForward = Vector3.Transform(new Vector3(0.0f, 0.0f, 1.0f), rootRotation); + Vector3 rootRight = Vector3.Transform(new Vector3(1.0f, 0.0f, 0.0f), rootRotation); + Vector3 rootUp = new Vector3(0.0f, 1.0f, 0.0f); + foreach ((nint addr, Vector3 position) in facePositions) + { + MemoryUtil.GetRef<Vector3>(addr + 0x50) = player.Position + ((rootForward * position.X) + (rootUp * position.Y) + (rootRight * position.Z)); + } + foreach ((nint addr, Vector3 offset) in faceOffsets) + { + MemoryUtil.GetRef<Vector3>(addr + 0x50) += ((rootForward * offset.X) + (rootUp * offset.Y) + (rootRight * offset.Z)); + } + + } + } + } + +#if QUARANTINED_FEATURES + private static void UpdateIKPositionHook(nint superOfChild, nint joint, nint obj) + { + debugLog($"UpdateIKPositionHook(0x{superOfChild:X}, 0x{joint:X}, 0x{obj:X}) @ {updateTick}"); + + if (ikOffsets.ContainsKey(joint)) + { + Vector3 offset = ikOffsets[joint]; + MemoryUtil.GetRef<Vector3>(superOfChild + 0x1380) += offset; + } + + updateIKPositionHook!.Original(superOfChild, joint, obj); + } +#endif + + private static void drawJoint(nint jointAddr, Dictionary<nint, Vector4> rotations, Dictionary<nint, Vector3> offsets) + { + if (jointAddr == 0x0) return; + ImGui.PushID(jointAddr); + bool hasRotation = rotations.ContainsKey(jointAddr); + bool hasOffset = offsets.ContainsKey(jointAddr); +#if QUARANTINED_FEATURES + bool hasIkOffset = ikOffsets.ContainsKey(jointAddr); +#endif + ref Vector3 position = ref MemoryUtil.GetRef<Vector3>(jointAddr + 0x50); + ref Vector4 rotation = ref MemoryUtil.GetRef<Vector4>(jointAddr + 0x70); + ImGui.DragFloat4("Rotation", ref rotation, 0.005f); + if (hasRotation) + { + ImGui.PushID("Rotation"); + bool doRemove = ImGui.Button("X"); + ImGui.SameLine(); + if (ImGui.Button("Reset")) + { + rotations[jointAddr] = default; + } + ImGui.SameLine(); + if (Models.DrawRotate("##Rotate", rotations[jointAddr], out Vector4 result)) + { + rotations[jointAddr] = result; + } + if (doRemove) rotations.Remove(jointAddr); + ImGui.PopID(); + } + ImGui.DragFloat3("Position", ref position, 0.005f); + if (hasOffset) + { + ImGui.PushID("Position"); + bool doRemove = ImGui.Button("X"); + ImGui.SameLine(); + if (ImGui.Button("Reset")) + { + offsets[jointAddr] = default; + } + ImGui.SameLine(); + Vector3 v = offsets[jointAddr]; + if (ImGui.DragFloat3($"##Offset", ref v, 0.005f)) + { + offsets[jointAddr] = v; + } + if (doRemove) offsets.Remove(jointAddr); + ImGui.PopID(); + } +#if QUARANTINED_FEATURES + if (hasIkOffset) + { + ImGui.PushID("IK"); + bool doRemove = ImGui.Button("X"); + ImGui.SameLine(); + if (ImGui.Button("Reset")) + { + ikOffsets[jointAddr] = default; + } + ImGui.SameLine(); + Vector3 v = ikOffsets[jointAddr]; + if (ImGui.DragFloat3($"##IK Offset", ref v, 0.025f)) + { + ikOffsets[jointAddr] = v; + } + if (doRemove) ikOffsets.Remove(jointAddr); + ImGui.PopID(); + } +#endif + if (!hasRotation) + { + if (ImGui.Button("Add Rotation")) + { + rotations.Add(jointAddr, rotation); + } + } + if (!hasOffset) + { + if (!hasRotation) + { + ImGui.SameLine(); + } + if (ImGui.Button("Add Offset")) + { + offsets.Add(jointAddr, new Vector3()); + } + } +#if QUARANTINED_FEATURES + if (ikJoints.Contains(jointAddr) && !hasIkOffset) + { + if (!hasOffset) + { + ImGui.SameLine(); + } + if (ImGui.Button("Add IK Offset")) + { + ikOffsets.Add(jointAddr, new Vector3()); + } + } +#endif + ImGui.PopID(); + } + + private static void drawBodyJoint(nint jointAddr) + { + drawJoint(jointAddr, bodyRotations, bodyOffsets); + } + + private static void drawFaceJoint(nint jointAddr) + { + drawJoint(jointAddr, faceRotations, faceOffsets); + } + + private static void drawJointLeaf(ref List<nint> joints, int i, Dictionary<nint, Vector4> rotations, Dictionary<nint, Vector3> offsets) + { + if (i >= joints.Count) return; + nint jointAddr = joints[i]; + byte id = MemoryUtil.Read<byte>(jointAddr + 0xA0); + ImGui.PushID(jointAddr); + if (ImGui.CollapsingHeader($"Joint #{i} (0x{jointAddr:X}, id: 0x{id:X})")) + { + drawJoint(jointAddr, rotations, offsets); + } + ImGui.PopID(); + drawJointLeaf(ref joints, i + 1, rotations, offsets); + } + + private static void drawBodyJoints() + { + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.Framed; + bool expanded; + if (ImGui.TreeNodeEx("Root", flags)) + { + drawBodyJoint(bodyJointMap[Joint.Root2]); + ImGui.TreePop(); + } + ImGui.PushID("Neck"); + if (ImGui.TreeNodeEx("Neck", flags)) + { + if (ImGui.TreeNodeEx("Upper", flags)) + { + drawBodyJoint(bodyJointMap[Joint.UpperNeck]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Lower", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LowerNeck]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Neck. + ImGui.PushID("Back"); + if (ImGui.TreeNodeEx("Back", flags)) + { + if (ImGui.TreeNodeEx("Upper", flags)) + { + drawBodyJoint(bodyJointMap[Joint.UpperBack]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Lower", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LowerBack]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Back. + ImGui.PushID("Right Arm"); + if (ImGui.TreeNodeEx("Right Arm", flags)) + { + if (ImGui.TreeNodeEx("Collarbone", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightCollarbone]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Shoulder", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightShoulder]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Elbow", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightElbow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Wrist", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightWrist]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Right Arm. + ImGui.PushID("Right Hand"); + if (ImGui.TreeNodeEx("Right Hand", flags)) + { + ImGui.PushID("Thumb"); + if (ImGui.TreeNodeEx("Thumb", flags)) + { + if (ImGui.TreeNodeEx("Base", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightThumbLow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightThumbMid]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Tip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightThumbHigh]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); + ImGui.PushID("Index"); + if (ImGui.TreeNodeEx("Index", flags)) + { + if (ImGui.TreeNodeEx("Base", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightIndexLow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightIndexMid]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Tip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightIndexHigh]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); + ImGui.PushID("Middle"); + if (ImGui.TreeNodeEx("Middle", flags)) + { + if (ImGui.TreeNodeEx("Base", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightMiddleLow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightMiddleMid]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Tip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightMiddleHigh]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); + ImGui.PushID("Ring"); + if (ImGui.TreeNodeEx("Ring", flags)) + { + if (ImGui.TreeNodeEx("Base", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightRingLow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightRingMid]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Tip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightRingHigh]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); + ImGui.PushID("Pinky"); + if (ImGui.TreeNodeEx("Pinky", flags)) + { + if (ImGui.TreeNodeEx("Base", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightPinkyLow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightPinkyMid]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Tip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightPinkyHigh]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); + expanded = ImGui.TreeNodeEx("Palm", flags); + if (ImGui.BeginItemTooltip()) + { + ImGui.Text("Outer Palm/Pinky+Ring"); + ImGui.EndTooltip(); + } + if (expanded) + { + drawBodyJoint(bodyJointMap[Joint.RightPalm]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Right Hand. + ImGui.PushID("Right Leg"); + if (ImGui.TreeNodeEx("Right Leg", flags)) + { + if (ImGui.TreeNodeEx("Hip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightHip]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Knee", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightKnee]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Ankle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightAnkle]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Toes", flags)) + { + drawBodyJoint(bodyJointMap[Joint.RightToes]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Right Leg. + ImGui.PushID("Left Arm"); + if (ImGui.TreeNodeEx("Left Arm", flags)) + { + if (ImGui.TreeNodeEx("Collarbone", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftCollarbone]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Shoulder", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftShoulder]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Elbow", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftElbow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Wrist", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftWrist]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Left Arm. + ImGui.PushID("Left Hand"); + if (ImGui.TreeNodeEx("Left Hand", flags)) + { + ImGui.PushID("Thumb"); + if (ImGui.TreeNodeEx("Thumb", flags)) + { + if (ImGui.TreeNodeEx("Base", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftThumbLow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftThumbMid]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Tip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftThumbHigh]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); + ImGui.PushID("Index"); + if (ImGui.TreeNodeEx("Index", flags)) + { + if (ImGui.TreeNodeEx("Base", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftIndexLow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftIndexMid]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Tip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftIndexHigh]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); + ImGui.PushID("Middle"); + if (ImGui.TreeNodeEx("Middle", flags)) + { + if (ImGui.TreeNodeEx("Base", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftMiddleLow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftMiddleMid]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Tip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftMiddleHigh]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); + ImGui.PushID("Ring"); + if (ImGui.TreeNodeEx("Ring", flags)) + { + if (ImGui.TreeNodeEx("Base", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftRingLow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftRingMid]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Tip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftRingHigh]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); + ImGui.PushID("Pinky"); + if (ImGui.TreeNodeEx("Pinky", flags)) + { + if (ImGui.TreeNodeEx("Base", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftPinkyLow]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftPinkyMid]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Tip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftPinkyHigh]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); + expanded = ImGui.TreeNodeEx("Palm", flags); + if (ImGui.BeginItemTooltip()) + { + ImGui.Text("Outer Palm/Pinky+Ring"); + ImGui.EndTooltip(); + } + if (expanded) + { + drawBodyJoint(bodyJointMap[Joint.LeftPalm]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Left Hand. + ImGui.PushID("Left Leg"); + if (ImGui.TreeNodeEx("Left Leg", flags)) + { + if (ImGui.TreeNodeEx("Hip", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftHip]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Knee", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftKnee]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Ankle", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftAnkle]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Toes", flags)) + { + drawBodyJoint(bodyJointMap[Joint.LeftToes]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Left Leg. + } + + private static void drawFaceJoints() + { + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags.Framed; + ImGui.PushID("Lips"); + if (ImGui.TreeNodeEx("Lips", flags)) + { + if (ImGui.TreeNodeEx("Right Edge", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth2]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Lower 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth3]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Lower 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth4]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Upper 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth29]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Upper 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth28]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Edge", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth1]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Lower 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth7]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Lower 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth6]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Upper 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth25]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Upper 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth26]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle Lower", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth5]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle Upper", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth27]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Lips. + ImGui.PushID("Mouth"); + if (ImGui.TreeNodeEx("Mouth", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth]); + if (ImGui.TreeNodeEx("Bottom Teeth", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth30]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Lower 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth12]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Lower 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth11]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Upper 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth24]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Upper 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth23]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Lower 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth8]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Lower 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth9]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Upper 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth20]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Upper 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth21]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle Lower", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth10]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle Upper", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth22]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Under", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth15]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Under", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth14]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Middle Under", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth13]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Cheek 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth17]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Cheek 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth19]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Cheek 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth16]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Cheek 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Mouth18]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Mouth. + ImGui.PushID("Cheeks"); + if (ImGui.TreeNodeEx("Cheeks", flags)) + { + if (ImGui.TreeNodeEx("Right Cheek", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightCheek]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Cheek 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightCheek1]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Cheek 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightCheek2]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Cheek 3", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightCheek3]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Cheek 4", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightCheek4]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Right Cheek 5", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightCheek5]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Cheek", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftCheek]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Cheek 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftCheek1]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Cheek 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftCheek2]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Cheek 3", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftCheek3]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Cheek 4", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftCheek4]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Left Cheek 5", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftCheek5]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Cheeks. + ImGui.PushID("Right Eye"); + if (ImGui.TreeNodeEx("Right Eye", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye]); + if (ImGui.TreeNodeEx("Eyeball", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye1]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Outer", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye2]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye3]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye4]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid 3", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye5]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Inner", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye6]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Lower 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye7]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Lower 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye8]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Lower 3", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye9]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Top 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye10]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Top 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye11]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Top 3", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye12]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Under Eye", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.RightEye13]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Right Eye. + ImGui.PushID("Left Eye"); + if (ImGui.TreeNodeEx("Left Eye", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye]); + if (ImGui.TreeNodeEx("Eyeball", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye1]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Outer", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye2]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye3]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye4]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid 3", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye5]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Inner", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye6]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Lower 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye7]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Lower 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye8]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Lower 3", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye9]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Top 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye10]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Top 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye11]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Eyelid Top 3", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye12]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Under Eye", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.LeftEye13]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Left Eye. + ImGui.PushID("Chin"); + if (ImGui.TreeNodeEx("Chin", flags)) + { + if (ImGui.TreeNodeEx("Chin Right", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.ChinRight]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Chin Right Back", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.ChinRightBack]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Chin Middle", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.ChinMiddle]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Chin Left", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.ChinLeft]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Chin Left Back", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.ChinLeftBack]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Neck Right", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.NeckRight]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Neck Middle", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.NeckMiddle]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Neck Left", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.NeckLeft]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Jaw", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Jaw]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Jaw Right", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.JawRight]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Jaw Up Right", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.JawUpRight]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Jaw Left", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.JawLeft]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Jaw Up Left", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.JawUpLeft]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Chin. + ImGui.PushID("Brow"); + if (ImGui.TreeNodeEx("Brow", flags)) + { + if (ImGui.TreeNodeEx("Brow 0", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead0]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead1]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead2]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 3", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead3]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 4", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead4]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 5", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead5]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 6", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead6]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 7", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead7]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 8", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead8]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 9", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead9]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 10", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead10]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 11", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead11]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 12", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead12]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 13", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead13]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Brow 14", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Forehead14]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Brow. + ImGui.PushID("Nose"); + if (ImGui.TreeNodeEx("Nose", flags)) + { + if (ImGui.TreeNodeEx("Nose", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 1", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose1]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 2", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose2]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 3", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose3]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 4", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose4]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 5", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose5]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 6", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose6]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 7", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose7]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 8", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose8]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 9", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose9]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 10", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose10]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 11", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose11]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 12", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose12]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 13", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose13]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 14", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose14]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 15", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose15]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 16", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose16]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 17", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose17]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 18", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose18]); + ImGui.TreePop(); + } + if (ImGui.TreeNodeEx("Nose 19", flags)) + { + drawFaceJoint(faceJointMap[FaceJoint.Nose19]); + ImGui.TreePop(); + } + ImGui.TreePop(); + } + ImGui.PopID(); // Nose. + } + + private static string typedPoseName = ""; + private static string selectedPoseName = ""; + private static string[] poseList = []; + private static bool poseSelectionStarted = false; + + private static float StringToSingle(string s) { return Convert.ToSingle(s, CultureInfo.InvariantCulture); } + + public static Vector3 StringToVector3(string s, char delim = ',') + { + if (String.IsNullOrEmpty(s)) throw new FormatException(); + string[] sp = s.Split(delim).Select((v) => v.Trim()).ToArray(); + if (sp.Length != 3) throw new FormatException(); + return new Vector3(StringToSingle(sp[0]), StringToSingle(sp[1]), StringToSingle(sp[2])); + } + + public static Vector4 StringToVector4(string s, char delim = ',') + { + if (String.IsNullOrEmpty(s)) throw new FormatException(); + string[] sp = s.Split(delim).Select((v) => v.Trim()).ToArray(); + if (sp.Length != 4) throw new FormatException(); + return new Vector4(StringToSingle(sp[0]), StringToSingle(sp[1]), StringToSingle(sp[2]), StringToSingle(sp[3])); + } + + private static string Vector3ToString(Vector3 v) + { + return string.Format(CultureInfo.InvariantCulture, "{0},{1},{2}", v.X, v.Y, v.Z); + } + + private static string Vector4ToString(Vector4 v) + { + return string.Format(CultureInfo.InvariantCulture, "{0},{1},{2},{3}", v.X, v.Y, v.Z, v.W); + } + + public static bool overlayJoints = false; + + public static void DrawUI(float width, Player player) + { + bool armorStateRefreshed = false; + + ImGui.PushID("Joints"); + if (ImGui.CollapsingHeader("Joints")) + { + if (!armorStateRefreshed) + { + refreshArmorState(player); + armorStateRefreshed = true; + } + ImGui.Checkbox("Overlay Joints", ref overlayJoints); + ImGui.SetCursorPos(ImGui.GetCursorPos() + new Vector2(0.0f, 6.0f)); +#if QUARANTINED_FEATURES + ImGui.Text($"Rotations: {bodyRotations.Count + faceRotations.Count}, Positions: {bodyPositions.Count + facePositions.Count}, Offsets: {bodyOffsets.Count + faceOffsets.Count + ikOffsets.Count}"); +#else + ImGui.Text($"Rotations: {bodyRotations.Count + faceRotations.Count}, Positions: {bodyPositions.Count + facePositions.Count}, Offsets: {bodyOffsets.Count + faceOffsets.Count}"); +#endif + ImGui.SameLine(); + ImGui.SetCursorPos(ImGui.GetCursorPos() - new Vector2(0.0f, 6.0f)); + if (ImGui.Button("Clear")) + { + dropJointOffsets(); + } + /* + ref byte ikDisabledB = ref MemoryUtil.GetRef<byte>(MemoryUtil.Read<nint>(player.Instance + 0x990) + 0x5C); + bool ikDisabled = ikDisabledB == 0x0; + if (ImGui.Checkbox("Disable IK", ref ikDisabled)) + { + ikDisabledB = ByteFlag(!ikDisabled); + } + if (ImGui.BeginItemTooltip()) + { + ImGui.Text("Mostly disable inverse kinematics on your player. To rotate ankles, you need to use Force Disable IK."); + ImGui.EndTooltip(); + } + ImGui.SameLine(); + */ + if (ImGui.Checkbox("Disable IK", ref ikForcedOff)) + { + if (ikForcedOff) + { + forceDisableIK.Enable(); + } + else + { + forceDisableIK.Disable(); + } + } + if (ImGui.BeginItemTooltip()) + { + ImGui.Text("Forcefully disable inverse kinematics."); + ImGui.EndTooltip(); + } + ImGui.SameLine(); + ref byte lookAtDisabledB = ref MemoryUtil.GetRef<byte>(MemoryUtil.Read<nint>(player.Instance + 0x9C0) + 0x5C); + bool lookAtDisabled = lookAtDisabledB == 0x0; + if (ImGui.Checkbox("Disable Look At", ref lookAtDisabled)) + { + lookAtDisabledB = ByteFlag(!lookAtDisabled); + } + if (ImGui.BeginItemTooltip()) + { + ImGui.Text("Stop your character from looking at people, animals, and objects they pass."); + ImGui.EndTooltip(); + } + + ImGui.PushItemWidth(width * 0.3f); + ImGui.InputText("##Pose Name", ref typedPoseName, 99); + ImGui.SameLine(); + if (ImGui.Button("Save")) + { + string name = typedPoseName; + if (name != "") + { + if (!Directory.Exists(posePath)) + { + Directory.CreateDirectory(posePath); + } + string filename = name + ".json"; + string path = Path.Join(posePath, filename); + using (FileStream stream = File.Open(path, FileMode.Create)) + { + using (Utf8JsonWriter writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true })) + { + Vector3 rootPosition = player.Position; + Quaternion rootRotation = new Quaternion(player.Rotation.X, player.Rotation.Y, player.Rotation.Z, player.Rotation.W); + Vector3 rootForward = Vector3.Transform(new Vector3(0.0f, 0.0f, 1.0f), rootRotation); + Vector3 rootRight = Vector3.Transform(new Vector3(1.0f, 0.0f, 0.0f), rootRotation); + Vector3 rootUp = new Vector3(0.0f, 1.0f, 0.0f); + writer.WriteStartObject(); + writer.WriteStartObject("body"); + foreach (nint jointAddr in bodyJoints) + { + byte id = MemoryUtil.Read<byte>(jointAddr + 0xA0); + writer.WritePropertyName($"{id}"); + writer.WriteStartArray(); + writer.WriteStringValue(Vector4ToString(MemoryUtil.GetRef<Vector4>(jointAddr + 0x70))); + Vector3 positionOffset = default; + Vector3 position = MemoryUtil.GetRef<Vector3>(jointAddr + 0x50); + Vector3 diff = position - rootPosition; + positionOffset.Y = diff.Y; + diff.Y = 0.0f; + positionOffset.X = Vector3.Dot(rootForward, diff); + positionOffset.Z = Vector3.Dot(rootRight, diff); + writer.WriteStringValue(Vector3ToString(positionOffset)); + if (bodyOffsets.ContainsKey(jointAddr)) + { + writer.WriteStringValue(Vector3ToString(bodyOffsets[jointAddr])); + } + writer.WriteEndArray(); + } + writer.WriteEndObject(); + writer.WriteStartObject("face"); + foreach (nint jointAddr in faceJoints) + { + byte id = MemoryUtil.Read<byte>(jointAddr + 0xA0); + writer.WritePropertyName($"{id}"); + writer.WriteStartArray(); + writer.WriteStringValue(Vector4ToString(MemoryUtil.GetRef<Vector4>(jointAddr + 0x70))); + Vector3 positionOffset = default; + Vector3 position = MemoryUtil.GetRef<Vector3>(jointAddr + 0x50); + Vector3 diff = position - rootPosition; + positionOffset.Y = diff.Y; + diff.Y = 0.0f; + positionOffset.X = Vector3.Dot(rootForward, diff); + positionOffset.Z = Vector3.Dot(rootRight, diff); + writer.WriteStringValue(Vector3ToString(positionOffset)); + if (faceOffsets.ContainsKey(jointAddr)) + { + writer.WriteStringValue(Vector3ToString(faceOffsets[jointAddr])); + } + writer.WriteEndArray(); + } + writer.WriteEndObject(); + writer.WriteEndObject(); + writer.Flush(); + } + } + } + } + ImGui.SameLine(); + if (ImGui.BeginCombo("##Poses", selectedPoseName, ImGuiComboFlags.HeightLarge)) + { + if (!poseSelectionStarted) + { + poseList = Directory.GetFiles(posePath); + poseSelectionStarted = true; + } + foreach (string filePath in poseList) + { + if (Path.GetExtension(filePath) != ".json") + { + continue; + } + string fileName = Path.GetFileNameWithoutExtension(filePath); + bool isSelected = fileName == selectedPoseName; + if (ImGui.Selectable(fileName, isSelected)) + { + dropJointOffsets(); + selectedPoseName = fileName; + Utf8JsonReader reader = new Utf8JsonReader(File.ReadAllBytes(filePath)); + bool isFace = false; + byte currentId = 0xFF; + int arrayIndex = 0; + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonTokenType.PropertyName: + string property = reader.GetString()!; + if (property == "body") + { + isFace = false; + } + else if (property == "face") + { + isFace = true; + } + else + { + try + { + currentId = Convert.ToByte(property); + } + catch (FormatException) + { + currentId = 0xFF; + } + catch (OverflowException) + { + currentId = 0xFF; + } + } + break; + case JsonTokenType.StartArray: + arrayIndex = 0; + break; + case JsonTokenType.String: + if (currentId == 0xFF) break; + if (arrayIndex == 0) + { + if (isFace) + { + faceRotations[faceJointMap[currentId]] = StringToVector4(reader.GetString()!); + } + else + { + bodyRotations[bodyJointMap[currentId]] = StringToVector4(reader.GetString()!); + } + } + else if (arrayIndex == 1) + { + if (isFace) + { + facePositions[faceJointMap[currentId]] = StringToVector3(reader.GetString()!); + } + else + { + bodyPositions[bodyJointMap[currentId]] = StringToVector3(reader.GetString()!); + } + } + arrayIndex++; + break; + } + } + } + if (isSelected) ImGui.SetItemDefaultFocus(); + } + ImGui.EndCombo(); + } + else + { + poseSelectionStarted = false; + } + ImGui.SameLine(); + if (ImGui.Button("Delete")) + { + if (selectedPoseName != "") + { + selectedPoseName = ""; + } + } + ImGui.PopItemWidth(); + + ImGui.PushItemWidth(width * 0.75f); + drawBodyJoints(); + ImGui.Separator(); + if (ImGui.CollapsingHeader("Face")) + { + drawFaceJoints(); + ImGui.Separator(); + } + if (ImGui.CollapsingHeader("Jiggle Bones")) + { + for (int i = 0; i < jiggleBones.Count; i++) + { + nint jiggleData = jiggleBones[i]; + ImGui.PushID(jiggleData); + if (ImGui.CollapsingHeader($"Jiggle Bone #{i} (0x{jiggleData:X})")) + { + ImGui.DragFloat3("Position", ref MemoryUtil.GetRef<Vector3>(jiggleData + 0xD0), 0.05f); + if (ImGui.BeginItemTooltip()) + { + ImGui.Text("Drag this position around to influence the bone."); + ImGui.EndTooltip(); + } + } + ImGui.PopID(); + } + } + ImGui.Separator(); + if (ImGui.CollapsingHeader("All Joints")) + { + if (ImGui.CollapsingHeader("Body Joints")) + { + drawJointLeaf(ref bodyJoints, 0, bodyRotations, bodyOffsets); + } + if (ImGui.CollapsingHeader("Face Joints")) + { + drawJointLeaf(ref faceJoints, 0, faceRotations, faceOffsets); + } + } + ImGui.PopItemWidth(); + ImGui.Separator(); + } + ImGui.PopID(); + + ImGui.PushID("Parts"); + if (ImGui.CollapsingHeader("Armor Parts")) + { + if (!armorStateRefreshed) + { + refreshArmorState(player); + armorStateRefreshed = true; + } + bool expaned = ImGui.CollapsingHeader("Helmet"); + ImGui.SameLine(); + ImGui.Text($"(id: {MemoryUtil.Read<int>(player.Instance + 0x1373C):X})"); + if (expaned) + { + Models.drawModelParts(playerArmor[Armor.Helmet]); + ImGui.Separator(); + } + expaned = ImGui.CollapsingHeader("Body"); + ImGui.SameLine(); + ImGui.Text($"(id: {MemoryUtil.Read<int>(player.Instance + 0x13740):X})"); + if (expaned) + { + Models.drawModelParts(playerArmor[Armor.Body]); + ImGui.Separator(); + } + expaned = ImGui.CollapsingHeader("Arms"); + ImGui.SameLine(); + ImGui.Text($"(id: {MemoryUtil.Read<int>(player.Instance + 0x13744):X})"); + if (expaned) + { + Models.drawModelParts(playerArmor[Armor.Arm]); + ImGui.Separator(); + } + expaned = ImGui.CollapsingHeader("Waist"); + ImGui.SameLine(); + ImGui.Text($"(id: {MemoryUtil.Read<int>(player.Instance + 0x13748):X})"); + if (expaned) + { + Models.drawModelParts(playerArmor[Armor.Waist]); + ImGui.Separator(); + } + expaned = ImGui.CollapsingHeader("Legs"); + ImGui.SameLine(); + ImGui.Text($"(id: {MemoryUtil.Read<int>(player.Instance + 0x1374C):X})"); + if (expaned) + { + Models.drawModelParts(playerArmor[Armor.Leg]); + ImGui.Separator(); + } + expaned = ImGui.CollapsingHeader("Slinger"); + ImGui.SameLine(); + ImGui.Text($"(id: {MemoryUtil.Read<int>(player.Instance + 0x13D8C):X}_{MemoryUtil.Read<int>(player.Instance + 0x13D90):X})"); + if (expaned) + { + Models.drawModelParts(playerArmor[Armor.Slinger]); + ImGui.Separator(); + } + if (ImGui.CollapsingHeader("Hair")) + { + Models.drawModelParts(playerArmor[Armor.Hair]); + ImGui.Separator(); + } + if (ImGui.CollapsingHeader("Face")) + { + Models.drawModelParts(playerArmor[Armor.Face]); + ImGui.Separator(); + } + if (ImGui.CollapsingHeader("Eye Lens")) + { + Models.drawModelParts(playerArmor[Armor.EyeLens]); + } + ImGui.Separator(); + } + if (ImGui.CollapsingHeader("Weapon Parts")) + { + bool expaned = ImGui.CollapsingHeader("Weapon"); + if (expaned) + { + foreach (nint weaponPart in playerWeapon) + { + Models.drawModelParts(weaponPart); + ImGui.Separator(); + } + } + } + ImGui.PopID(); + } + + public static void OnRender() + { + if (overlayJoints) + { + for (int i = 0; i < faceJoints.Count; i++) + { + nint jointAddr = faceJoints[i]; + ref Vector3 position = ref MemoryUtil.GetRef<Vector3>(jointAddr + 0x50); + Primitives.RenderSphere(position, 0.1f, new MtColor(255, 0, 0, 255)); + } + } + } + } +} |