summaryrefslogtreecommitdiff
path: root/Poser.cs
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2026-10-09 13:13:59 -0400
committerAndrew Opalach <andrew@akon.city> 2026-10-09 13:13:59 -0400
commita66fbdfab71ece98223d257f3a6da7ab0339ee1f (patch)
tree5289018b0b7130d9509eff971b060d0699f71b92 /Poser.cs
parent8abc6082c701084d505fa4fdf391a8cbf69e4fec (diff)
downloadNewCamera-master.tar.gz
NewCamera-master.tar.bz2
NewCamera-master.zip
Bunch of wip stuffHEADmaster
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'Poser.cs')
-rw-r--r--Poser.cs2320
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));
+ }
+ }
+ }
+ }
+}