Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TetoCommandoSkin v1.0.1
plugins/TetoCommandoSkin/TetoCommandoSkin.dll
Decompiled 11 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("TetoCommandoSkin")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("TetoCommandoSkin")] [assembly: AssemblyTitle("TetoCommandoSkin")] [assembly: AssemblyVersion("1.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TetoCommandoSkin { [BepInPlugin("meko.TetoCommandoSkin", "TetoCommandoSkin", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "meko.TetoCommandoSkin"; public const string Name = "TetoCommandoSkin"; public const string Version = "1.0.1"; private const string BodyAddress = "RoR2/Base/Commando/CommandoBody.prefab"; private const string NameToken = "MEKO_SKIN_COMMANDO_TETO_NAME"; private ConfigEntry<float> scaleMultiplier; private ConfigEntry<string> boneOverrides; private ConfigEntry<bool> exportDebugObj; private ConfigEntry<float> shoulderLowerPercent; private ConfigEntry<float> headRaisePercent; private string dir; private GameObject body; private Object bodySkin; private Object displaySkin; private Material material; private Sprite icon; private static readonly Dictionary<string, Type> typeCache = new Dictionary<string, Type>(); private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private void Awake() { dir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); scaleMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Fitting", "ScaleMultiplier", 1f, "Multiplies the model's overall size after it is fitted to Commando's height."); boneOverrides = ((BaseUnityPlugin)this).Config.Bind<string>("Fitting", "BoneOverrides", "", "Only if the log says a bone was matched wrongly: role=ExactBoneName pairs separated by ';', e.g. hand.l=hand.l;neck=neck. Roles: " + string.Join(", ", Retargeter.Roles)); shoulderLowerPercent = ((BaseUnityPlugin)this).Config.Bind<float>("Fitting", "ShoulderLowerPercent", 0f, "Lowers the shoulders by this percent of the character's height (negative raises them)."); headRaisePercent = ((BaseUnityPlugin)this).Config.Bind<float>("Fitting", "HeadRaisePercent", 0f, "Raises the neck and head by this percent of the character's height (negative lowers them)."); exportDebugObj = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "ExportDebugObj", false, "Writes TetoCommandoSkin_debug.obj (the fitted mesh in Commando's bind pose) next to the plugin so it can be inspected."); try { Build(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Teto skin was not added: " + ex)); } } private void Build() { body = LoadAddressable<GameObject>("RoR2/Base/Commando/CommandoBody.prefab") ?? throw new Exception("could not load RoR2/Base/Commando/CommandoBody.prefab"); Component model = body.GetComponentInChildren(FindType("RoR2.CharacterModel"), true) ?? throw new Exception("CommandoBody has no CharacterModel"); SkinnedMeshRenderer smr = MainRenderer(model); Mesh mesh = FitMesh(model, smr, "body", exportDebugObj.Value); material = MakeMaterial(model, smr); icon = LoadIcon(); FindType("R2API.LanguageAPI").GetMethod("Add", new Type[2] { typeof(string), typeof(string) }).Invoke(null, new object[2] { "MEKO_SKIN_COMMANDO_TETO_NAME", "Kasane Teto" }); bodySkin = CreateSkinDef(model, smr, mesh, "skinCommandoTeto"); bool flag = (bool)FindType("R2API.Skins").GetMethod("AddSkinToCharacter", new Type[2] { typeof(GameObject), FindType("RoR2.SkinDef") }).Invoke(null, new object[2] { body, bodySkin }); ((BaseUnityPlugin)this).Logger.LogInfo((object)(flag ? "Kasane Teto skin added to Commando." : "R2API refused the skin (see R2API log above).")); if (flag) { Subscribe("RoR2.UI.MainMenu.MainMenuController", "OnMainMenuInitialised", EnsureDisplaySkin); SceneManager.sceneLoaded += delegate { EnsureDisplaySkin(); }; } } private void EnsureDisplaySkin() { //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Expected O, but got Unknown try { GameObject val = FindDisplayPrefab(); if (!Object.op_Implicit((Object)(object)val)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Commando display prefab not found; preview not patched."); return; } Component componentInChildren = val.GetComponentInChildren(FindType("RoR2.CharacterModel"), true); if (!Object.op_Implicit((Object)(object)componentInChildren)) { return; } Type type = FindType("RoR2.ModelSkinController"); Type elementType = FindType("RoR2.SkinDef"); Array array = (Array)Get(body.GetComponentInChildren(type, true), "skins"); int num = Array.IndexOf(array, bodySkin); if (num < 0) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Our skin is no longer in Commando's skin list."); return; } Component o = componentInChildren.GetComponent(type) ?? componentInChildren.gameObject.AddComponent(type); Array array2 = (Get(o, "skins") as Array) ?? Array.CreateInstance(elementType, 0); if (!Object.op_Implicit(displaySkin)) { SkinnedMeshRenderer smr = MainRenderer(componentInChildren); displaySkin = CreateSkinDef(componentInChildren, smr, FitMesh(componentInChildren, smr, "display", writeDebug: false), "skinCommandoTetoDisplay"); } try { Set(displaySkin, "skinIndex", Get(bodySkin, "skinIndex")); } catch { } if (num < array2.Length && array2.GetValue(num) == displaySkin) { return; } if (array2.Length <= num) { Array array3 = Array.CreateInstance(elementType, num + 1); Array.Copy(array2, array3, array2.Length); Object val2 = (Object)((array2.Length > 0) ? /*isinst with value type is only supported in some contexts*/: null); for (int i = array2.Length; i < num; i++) { Object val3 = Object.Instantiate((Object)array.GetValue(i)); try { Set(val3, "skinIndex", Get(array.GetValue(i), "skinIndex")); } catch { } if (Object.op_Implicit(val2)) { Array array4 = Array.CreateInstance(elementType, 1); array4.SetValue(val2, 0); Set(val3, "baseSkins", array4); Set(val3, "rootObject", Get(val2, "rootObject") ?? componentInChildren.gameObject); } array3.SetValue(val3, i); } array2 = array3; } array2.SetValue(displaySkin, num); Set(o, "skins", array2); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Character-select preview: Teto placed in display slot {num} of {array2.Length}."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Could not set up the character-select preview: " + ex)); } } private GameObject FindDisplayPrefab() { IEnumerable enumerable = (FindType("RoR2.SurvivorCatalog")?.GetProperty("survivorDefs", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) as IEnumerable) ?? (FindType("RoR2.SurvivorCatalog")?.GetField("survivorDefs", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null) as IEnumerable); if (enumerable != null) { foreach (object item in enumerable) { Object val = (Object)((item is Object) ? item : null); if (val != null && Object.op_Implicit(val) && (Object)/*isinst with value type is only supported in some contexts*/ == (Object)(object)body) { object obj = Get(item, "displayPrefab"); return (GameObject)((obj is GameObject) ? obj : null); } } } return TryLoad<GameObject>("RoR2/Base/Commando/CommandoDisplay.prefab"); } private static SkinnedMeshRenderer MainRenderer(Component model) { object obj = Get(model, "mainSkinnedMeshRenderer"); return (SkinnedMeshRenderer)(((obj is SkinnedMeshRenderer) ? obj : null) ?? (from r in model.GetComponentsInChildren<SkinnedMeshRenderer>(true) orderby r.bones.Length descending select r).First()); } private Object CreateSkinDef(Component model, SkinnedMeshRenderer smr, Mesh mesh, string name) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Expected O, but got Unknown Array array = (Array)Get(model, "baseRendererInfos"); Array array2 = Array.CreateInstance(array.GetType().GetElementType(), array.Length); for (int i = 0; i < array.Length; i++) { object value = array.GetValue(i); if ((Object)Get(value, "renderer") == (Object)(object)smr) { Set(value, "defaultMaterial", material); } array2.SetValue(value, i); } Type type = FindType("RoR2.SkinDefParams+MeshReplacement"); object obj = Activator.CreateInstance(type); Set(obj, "renderer", smr); Set(obj, "mesh", mesh); Array array3 = Array.CreateInstance(type, 1); array3.SetValue(obj, 0); Type elementType = FindType("RoR2.SkinDef"); Component component = model.GetComponent(FindType("RoR2.ModelSkinController")); Array array4 = (Object.op_Implicit((Object)(object)component) ? (Get(component, "skins") as Array) : null); Array array5 = Array.CreateInstance(elementType, (array4 != null && array4.Length > 0) ? 1 : 0); if (array5.Length > 0) { array5.SetValue(array4.GetValue(0), 0); } Type type2 = FindType("R2API.SkinDefParamsInfo"); object obj2 = Activator.CreateInstance(type2); Set(obj2, "Name", name); Set(obj2, "NameToken", "MEKO_SKIN_COMMANDO_TETO_NAME"); Set(obj2, "Icon", icon); Set(obj2, "RootObject", model.gameObject); Set(obj2, "BaseSkins", array5); Set(obj2, "RendererInfos", array2); Set(obj2, "MeshReplacements", array3); return (Object)FindType("R2API.Skins").GetMethod("CreateNewSkinDef", new Type[1] { type2 }).Invoke(null, new object[1] { obj2 }); } private Material MakeMaterial(Component model, SkinnedMeshRenderer smr) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown Array obj = (Array)Get(model, "baseRendererInfos"); Material val = null; foreach (object item in obj) { if ((Object)Get(item, "renderer") == (Object)(object)smr) { object obj2 = Get(item, "defaultMaterial"); val = (Material)((obj2 is Material) ? obj2 : null); } } if (!Object.op_Implicit((Object)(object)val)) { val = ((Renderer)smr).sharedMaterial; } if (!Object.op_Implicit((Object)(object)val)) { val = TryLoad<Material>("RoR2/Base/Commando/matCommandoDualies.mat"); } if (!Object.op_Implicit((Object)(object)val)) { val = new Material(TryLoad<Shader>("RoR2/Base/Shaders/HGStandard.shader") ?? Shader.Find("Hopoo Games/Deferred/Standard") ?? Shader.Find("Standard")); } Material val2 = new Material(val) { name = "matTetoCommando" }; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Base material: " + ((Object)val).name + " / shader " + ((Object)val.shader).name)); val2.mainTexture = (Texture)(object)LoadTexture("TetoAtlas.png", compress: true); SetTex(val2, "_MainTex", val2.mainTexture); SetTex(val2, "_NormalTex", null); SetTex(val2, "_EmTex", null); SetColor(val2, "_Color", Color.white); SetColor(val2, "_EmColor", Color.black); SetFloat(val2, "_SpecularStrength", 0.05f); SetFloat(val2, "_Smoothness", 0.1f); return val2; } private Mesh FitMesh(Component model, SkinnedMeshRenderer smr, string label, bool writeDebug) { //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_05d1: Unknown result type (might be due to invalid IL or missing references) //IL_05d6: Unknown result type (might be due to invalid IL or missing references) //IL_05e9: Expected O, but got Unknown //IL_06fe: Unknown result type (might be due to invalid IL or missing references) //IL_07b4: Unknown result type (might be due to invalid IL or missing references) //IL_07b6: Unknown result type (might be due to invalid IL or missing references) Transform[] bones = smr.bones; ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("[{0}] renderer: {1}, {2} bones, mesh {3}", label, ((Object)smr).name, bones.Length, Object.op_Implicit((Object)(object)smr.sharedMesh) ? ((Object)smr.sharedMesh).name : "(none)")); Matrix4x4[] array = ReadBindposes(smr); List<Transform> list = model.GetComponentsInChildren<Transform>(true).ToList(); Dictionary<Transform, int> dictionary = new Dictionary<Transform, int>(); for (int i = 0; i < list.Count; i++) { dictionary[list[i]] = i; } Dictionary<int, int> boneSlot = new Dictionary<int, int>(); for (int j = 0; j < bones.Length; j++) { if (Object.op_Implicit((Object)(object)bones[j]) && dictionary.TryGetValue(bones[j], out var value) && !boneSlot.ContainsKey(value)) { boneSlot[value] = j; } } Matrix4x4 worldToLocalMatrix = ((Component)smr).transform.worldToLocalMatrix; List<TargetBone> tgt = new List<TargetBone>(); for (int k = 0; k < list.Count; k++) { Transform val = list[k]; Vector3 val2; if (!boneSlot.TryGetValue(k, out var value2)) { val2 = ((Matrix4x4)(ref worldToLocalMatrix)).MultiplyPoint3x4(val.position); } else { Matrix4x4 inverse = ((Matrix4x4)(ref array[value2])).inverse; val2 = Vector4.op_Implicit(((Matrix4x4)(ref inverse)).GetColumn(3)); } Vector3 val3 = val2; tgt.Add(new TargetBone { Name = ((Object)val).name, Parent = ((Object.op_Implicit((Object)(object)val.parent) && dictionary.TryGetValue(val.parent, out var value3)) ? value3 : (-1)), Pos = new V3(val3.x, val3.y, val3.z), Skinned = boneSlot.ContainsKey(k) }); } if (label == "body") { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Commando skinned bones (name <- parent @ bind position):\n" + string.Join("\n", from b in tgt where b.Skinned select string.Format(" {0} <- {1} @ {2}", b.Name, (b.Parent >= 0) ? tgt[b.Parent].Name : "-", b.Pos)))); } Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); Type type = FindType("ChildLocator"); Component val4 = ((type != null) ? model.GetComponent(type) : null); if (Object.op_Implicit((Object)(object)val4)) { MethodInfo method = type.GetMethod("FindChild", new Type[1] { typeof(string) }); foreach (KeyValuePair<string, string> item in new Dictionary<string, string> { ["hand.l"] = "HandL", ["hand.r"] = "HandR", ["head"] = "Head", ["chest"] = "Chest", ["pelvis"] = "Pelvis", ["foot.l"] = "FootL", ["foot.r"] = "FootR", ["thigh.l"] = "ThighL", ["thigh.r"] = "ThighR", ["calf.l"] = "CalfL", ["calf.r"] = "CalfR", ["upperarm.l"] = "UpperArmL", ["upperarm.r"] = "UpperArmR", ["forearm.l"] = "LowerArmL", ["forearm.r"] = "LowerArmR", ["spine"] = "Stomach" }) { object? obj = method?.Invoke(val4, new object[1] { item.Value }); Transform val5 = (Transform)((obj is Transform) ? obj : null); if (Object.op_Implicit((Object)(object)val5) && dictionary.TryGetValue(val5, out var value4)) { dictionary2[item.Key] = value4; } } } Dictionary<string, string> dictionary3 = new Dictionary<string, string>(); string[] array2 = (boneOverrides.Value ?? "").Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int num = 0; num < array2.Length; num++) { string[] array3 = array2[num].Split('='); if (array3.Length == 2) { dictionary3[array3[0].Trim()] = array3[1].Trim(); } } List<string> list2 = new List<string>(); Dictionary<string, int> roles = Retargeter.FindRoles(tgt, dictionary2, dictionary3, list2); SourceModel src; using (FileStream s = File.OpenRead(Path.Combine(dir, "TetoSkin.rskn"))) { src = SourceModel.Read(s); } Retargeter.Options opt = new Retargeter.Options { ScaleMultiplier = scaleMultiplier.Value, ShoulderLower = (double)shoulderLowerPercent.Value / 100.0, HeadRaise = (double)headRaisePercent.Value / 100.0 }; RetargetResult res = Retargeter.Fit(src, tgt, roles, opt); bool flag = Retargeter.NeedsWindingFlip(src, tgt, roles, res); list2.AddRange(res.Log); list2.Add("flip triangle winding: " + flag); ((BaseUnityPlugin)this).Logger.LogInfo((object)("[" + label + "] fitting:\n " + string.Join("\n ", list2))); int num2 = res.Positions.Length; Mesh val6 = new Mesh { name = "TetoCommandoMesh_" + label }; val6.vertices = ((IEnumerable<V3>)res.Positions).Select((Func<V3, Vector3>)((V3 p) => new Vector3((float)p.x, (float)p.y, (float)p.z))).ToArray(); val6.normals = ((IEnumerable<V3>)res.Normals).Select((Func<V3, Vector3>)((V3 p) => new Vector3((float)p.x, (float)p.y, (float)p.z))).ToArray(); val6.uv = Enumerable.Range(0, num2).Select((Func<int, Vector2>)((int num6) => new Vector2(src.U[num6], src.V[num6]))).ToArray(); int[] array4 = (int[])src.Indices.Clone(); if (flag) { for (int num3 = 0; num3 + 2 < array4.Length; num3 += 3) { int num4 = array4[num3 + 1]; array4[num3 + 1] = array4[num3 + 2]; array4[num3 + 2] = num4; } } val6.triangles = array4; BoneWeight[] array5 = (BoneWeight[])(object)new BoneWeight[num2]; int v; for (v = 0; v < num2; v++) { int num5 = v; BoneWeight val7 = default(BoneWeight); ((BoneWeight)(ref val7)).boneIndex0 = S(0); ((BoneWeight)(ref val7)).weight0 = res.Weights[v][0]; ((BoneWeight)(ref val7)).boneIndex1 = S(1); ((BoneWeight)(ref val7)).weight1 = res.Weights[v][1]; ((BoneWeight)(ref val7)).boneIndex2 = S(2); ((BoneWeight)(ref val7)).weight2 = res.Weights[v][2]; ((BoneWeight)(ref val7)).boneIndex3 = S(3); ((BoneWeight)(ref val7)).weight3 = res.Weights[v][3]; array5[num5] = val7; } val6.boneWeights = array5; val6.bindposes = array; val6.RecalculateBounds(); val6.RecalculateTangents(); if (writeDebug) { WriteObj(Path.Combine(dir, "TetoCommandoSkin_debug.obj"), val6, tgt, roles); } return val6; int S(int num6) { if (!(res.Weights[v][num6] > 0f)) { return 0; } return boneSlot[res.BoneIndices[v][num6]]; } } private static void Subscribe(string typeName, string member, Action handler) { Type type = FindType(typeName); EventInfo eventInfo = type?.GetEvent(member, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (eventInfo != null) { eventInfo.AddEventHandler(null, Delegate.CreateDelegate(eventInfo.EventHandlerType, handler.Target, handler.Method)); return; } FieldInfo fieldInfo = type?.GetField(member, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (fieldInfo != null && fieldInfo.FieldType == typeof(Action)) { fieldInfo.SetValue(null, Delegate.Combine((Action)fieldInfo.GetValue(null), handler)); } } private Matrix4x4[] ReadBindposes(SkinnedMeshRenderer smr) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) Transform[] bones = smr.bones; Matrix4x4[] array = null; try { if (Object.op_Implicit((Object)(object)smr.sharedMesh)) { array = smr.sharedMesh.bindposes; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not read Commando's bindposes (" + ex.Message + "); using the prefab pose.")); } Matrix4x4[] array2 = bones.Select((Transform b) => (!Object.op_Implicit((Object)(object)b)) ? Matrix4x4.identity : (b.worldToLocalMatrix * ((Component)smr).transform.localToWorldMatrix)).ToArray(); if (array != null && array.Length == bones.Length) { double num = 0.0; for (int num2 = 0; num2 < bones.Length; num2++) { double val = num; Matrix4x4 inverse = ((Matrix4x4)(ref array[num2])).inverse; Vector4 column = ((Matrix4x4)(ref inverse)).GetColumn(3); inverse = ((Matrix4x4)(ref array2[num2])).inverse; Vector4 val2 = column - ((Matrix4x4)(ref inverse)).GetColumn(3); num = Math.Max(val, ((Vector4)(ref val2)).magnitude); } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Using mesh bindposes (prefab pose differs by up to {num:0.####})."); return array; } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Using bindposes computed from the prefab pose."); return array2; } private Texture2D LoadTexture(string file, bool compress) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true) { name = Path.GetFileNameWithoutExtension(file) }; ImageConversion.LoadImage(val, File.ReadAllBytes(Path.Combine(dir, file)), false); ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).anisoLevel = 4; if (compress) { val.Compress(true); } val.Apply(true, true); return val; } private Sprite LoadIcon() { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); ImageConversion.LoadImage(val, File.ReadAllBytes(Path.Combine(dir, "SkinIcon.png"))); return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } private static void SetTex(Material m, string p, Texture t) { if (m.HasProperty(p)) { m.SetTexture(p, t); } } private static void SetColor(Material m, string p, Color c) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (m.HasProperty(p)) { m.SetColor(p, c); } } private static void SetFloat(Material m, string p, float f) { if (m.HasProperty(p)) { m.SetFloat(p, f); } } private void WriteObj(string path, Mesh mesh, List<TargetBone> tgt, Dictionary<string, int> roles) { //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) CultureInfo invariantCulture = CultureInfo.InvariantCulture; using (StreamWriter streamWriter = new StreamWriter(path)) { streamWriter.WriteLine("# Teto fitted to Commando bind pose (Unity mesh space). Roles:"); foreach (KeyValuePair<string, int> role in roles) { streamWriter.WriteLine($"# role {role.Key} {tgt[role.Value].Name} {tgt[role.Value].Pos}"); } foreach (TargetBone item in tgt.Where((TargetBone b) => b.Skinned)) { streamWriter.WriteLine("# bone " + item.Name + " " + ((item.Parent >= 0) ? tgt[item.Parent].Name : "-") + " " + item.Pos.x.ToString(invariantCulture) + " " + item.Pos.y.ToString(invariantCulture) + " " + item.Pos.z.ToString(invariantCulture)); } Vector3[] vertices = mesh.vertices; foreach (Vector3 val in vertices) { streamWriter.WriteLine(string.Format(invariantCulture, "v {0} {1} {2}", val.x, val.y, val.z)); } Vector2[] uv = mesh.uv; foreach (Vector2 val2 in uv) { streamWriter.WriteLine(string.Format(invariantCulture, "vt {0} {1}", val2.x, val2.y)); } int[] triangles = mesh.triangles; for (int num2 = 0; num2 < triangles.Length; num2 += 3) { streamWriter.WriteLine($"f {triangles[num2] + 1} {triangles[num2 + 1] + 1} {triangles[num2 + 2] + 1}"); } } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Wrote " + path)); } private static Type FindType(string fullName) { if (typeCache.TryGetValue(fullName, out var value)) { return value; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { value = assembly.GetType(fullName, throwOnError: false); } catch { value = null; } if (value != null) { break; } } return typeCache[fullName] = value; } private static object Get(object o, string member) { Type type = o.GetType(); FieldInfo field = type.GetField(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(o); } PropertyInfo property = type.GetProperty(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(o); } throw new MissingMemberException(type.FullName, member); } private static void Set(object o, string member, object value) { Type type = o.GetType(); FieldInfo field = type.GetField(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(o, value); return; } PropertyInfo property = type.GetProperty(member, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { property.SetValue(o, value); return; } throw new MissingMemberException(type.FullName, member); } private T TryLoad<T>(string key) where T : Object { try { return LoadAddressable<T>(key); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not load " + key + ": " + ex.GetBaseException().Message)); return default(T); } } private static T LoadAddressable<T>(string key) where T : Object { object obj = (FindType("UnityEngine.AddressableAssets.Addressables") ?? throw new Exception("Addressables not found")).GetMethods(BindingFlags.Static | BindingFlags.Public).First((MethodInfo m) => m.Name == "LoadAssetAsync" && m.IsGenericMethodDefinition && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(object)).MakeGenericMethod(typeof(T)) .Invoke(null, new object[1] { key }); object? obj2 = obj.GetType().GetMethod("WaitForCompletion").Invoke(obj, null); return (T)((obj2 is T) ? obj2 : null); } } public struct V3 { public double x; public double y; public double z; public double Length => Math.Sqrt(Dot(this, this)); public V3 Normalized { get { double length = Length; if (!(length > 1E-12)) { return new V3(0.0, 0.0, 0.0); } return this / length; } } public V3(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } public static V3 operator +(V3 a, V3 b) { return new V3(a.x + b.x, a.y + b.y, a.z + b.z); } public static V3 operator -(V3 a, V3 b) { return new V3(a.x - b.x, a.y - b.y, a.z - b.z); } public static V3 operator -(V3 a) { return new V3(0.0 - a.x, 0.0 - a.y, 0.0 - a.z); } public static V3 operator *(V3 a, double s) { return new V3(a.x * s, a.y * s, a.z * s); } public static V3 operator /(V3 a, double s) { return new V3(a.x / s, a.y / s, a.z / s); } public static double Dot(V3 a, V3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } public static V3 Cross(V3 a, V3 b) { return new V3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); } public override string ToString() { return $"({x:0.###}, {y:0.###}, {z:0.###})"; } } public struct M3 { public double[] m; public static M3 Identity => FromRows(new V3(1.0, 0.0, 0.0), new V3(0.0, 1.0, 0.0), new V3(0.0, 0.0, 1.0)); public M3 Transposed => new M3 { m = new double[9] { m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8] } }; public static M3 FromRows(V3 a, V3 b, V3 c) { return new M3 { m = new double[9] { a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z } }; } public static M3 FromCols(V3 a, V3 b, V3 c) { return new M3 { m = new double[9] { a.x, b.x, c.x, a.y, b.y, c.y, a.z, b.z, c.z } }; } public static V3 operator *(M3 a, V3 v) { return new V3(a.m[0] * v.x + a.m[1] * v.y + a.m[2] * v.z, a.m[3] * v.x + a.m[4] * v.y + a.m[5] * v.z, a.m[6] * v.x + a.m[7] * v.y + a.m[8] * v.z); } public static M3 operator *(M3 a, M3 b) { double[] array = new double[9]; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { array[i * 3 + j] = a.m[i * 3] * b.m[j] + a.m[i * 3 + 1] * b.m[3 + j] + a.m[i * 3 + 2] * b.m[6 + j]; } } return new M3 { m = array }; } public static M3 operator *(M3 a, double s) { return new M3 { m = a.m.Select((double x) => x * s).ToArray() }; } public static M3 FromTo(V3 a, V3 b) { a = a.Normalized; b = b.Normalized; V3 v = V3.Cross(a, b); double num = V3.Dot(a, b); if (num < -0.999999) { return AxisAngle(V3.Cross(a, (Math.Abs(a.x) < 0.9) ? new V3(1.0, 0.0, 0.0) : new V3(0.0, 1.0, 0.0)).Normalized, Math.PI); } double num2 = 1.0 / (1.0 + num); return new M3 { m = new double[9] { v.x * v.x * num2 + num, v.x * v.y * num2 - v.z, v.x * v.z * num2 + v.y, v.y * v.x * num2 + v.z, v.y * v.y * num2 + num, v.y * v.z * num2 - v.x, v.z * v.x * num2 - v.y, v.z * v.y * num2 + v.x, v.z * v.z * num2 + num } }; } public static M3 AxisAngle(V3 axis, double ang) { axis = axis.Normalized; double num = Math.Cos(ang); double num2 = Math.Sin(ang); double num3 = 1.0 - num; double x = axis.x; double y = axis.y; double z = axis.z; return new M3 { m = new double[9] { num3 * x * x + num, num3 * x * y - num2 * z, num3 * x * z + num2 * y, num3 * x * y + num2 * z, num3 * y * y + num, num3 * y * z - num2 * x, num3 * x * z - num2 * y, num3 * y * z + num2 * x, num3 * z * z + num } }; } public static M3 AxisScale(V3 d, double s) { d = d.Normalized; double num = s - 1.0; return new M3 { m = new double[9] { 1.0 + num * d.x * d.x, num * d.x * d.y, num * d.x * d.z, num * d.y * d.x, 1.0 + num * d.y * d.y, num * d.y * d.z, num * d.z * d.x, num * d.z * d.y, 1.0 + num * d.z * d.z } }; } } public struct Affine { public M3 L; public V3 t; public static Affine Identity => new Affine { L = M3.Identity, t = new V3(0.0, 0.0, 0.0) }; public V3 Point(V3 p) { return L * p + t; } } public class SourceBone { public string Name; public int Parent; public V3 Head; public string Role; } public class SourceModel { public List<SourceBone> Bones = new List<SourceBone>(); public V3[] Positions; public V3[] Normals; public float[] U; public float[] V; public int[][] WeightBones; public float[][] Weights; public int[] Indices; public static SourceModel Read(Stream s) { BinaryReader r = new BinaryReader(s, Encoding.UTF8); if (new string(r.ReadChars(4)) != "RSKN") { throw new InvalidDataException("not an RSKN file"); } r.ReadInt32(); SourceModel sourceModel = new SourceModel(); int num = r.ReadInt32(); for (int i = 0; i < num; i++) { sourceModel.Bones.Add(new SourceBone { Name = Str(), Parent = r.ReadInt32(), Head = Vec(), Role = Str() }); } int num2 = r.ReadInt32(); sourceModel.Positions = new V3[num2]; sourceModel.Normals = new V3[num2]; sourceModel.U = new float[num2]; sourceModel.V = new float[num2]; sourceModel.WeightBones = new int[num2][]; sourceModel.Weights = new float[num2][]; for (int j = 0; j < num2; j++) { sourceModel.Positions[j] = Vec(); sourceModel.Normals[j] = Vec(); sourceModel.U[j] = r.ReadSingle(); sourceModel.V[j] = r.ReadSingle(); sourceModel.WeightBones[j] = new int[4]; sourceModel.Weights[j] = new float[4]; for (int k = 0; k < 4; k++) { sourceModel.WeightBones[j][k] = r.ReadInt32(); sourceModel.Weights[j][k] = r.ReadSingle(); } } int num3 = r.ReadInt32(); sourceModel.Indices = new int[num3]; for (int l = 0; l < num3; l++) { sourceModel.Indices[l] = r.ReadInt32(); } return sourceModel; string Str() { return Encoding.UTF8.GetString(r.ReadBytes(r.ReadInt32())); } V3 Vec() { return new V3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); } } } public class TargetBone { public string Name; public int Parent; public V3 Pos; public bool Skinned; } public class RetargetResult { public V3[] Positions; public V3[] Normals; public int[][] BoneIndices; public float[][] Weights; public Dictionary<string, int> RoleToTarget; public List<string> Log = new List<string>(); } public static class Retargeter { public class Options { public double ScaleMultiplier = 1.0; public double MinAxisScale = 0.5; public double MaxAxisScale = 2.0; public HashSet<string> Pinned = new HashSet<string> { "pelvis", "spine", "chest", "forearm.l", "hand.l", "forearm.r", "hand.r", "thigh.l", "calf.l", "foot.l", "toe.l", "thigh.r", "calf.r", "foot.r", "toe.r" }; public double ShoulderLower; public double HeadRaise; } public static readonly string[] Roles = new string[21] { "pelvis", "spine", "chest", "neck", "head", "clavicle.l", "upperarm.l", "forearm.l", "hand.l", "clavicle.r", "upperarm.r", "forearm.r", "hand.r", "thigh.l", "calf.l", "foot.l", "toe.l", "thigh.r", "calf.r", "foot.r", "toe.r" }; private static readonly Dictionary<string, string[]> Next = new Dictionary<string, string[]> { ["pelvis"] = new string[2] { "spine", "chest" }, ["spine"] = new string[2] { "chest", "neck" }, ["chest"] = new string[2] { "neck", "head" }, ["neck"] = new string[1] { "head" }, ["clavicle.l"] = new string[1] { "upperarm.l" }, ["upperarm.l"] = new string[1] { "forearm.l" }, ["forearm.l"] = new string[1] { "hand.l" }, ["clavicle.r"] = new string[1] { "upperarm.r" }, ["upperarm.r"] = new string[1] { "forearm.r" }, ["forearm.r"] = new string[1] { "hand.r" }, ["thigh.l"] = new string[1] { "calf.l" }, ["calf.l"] = new string[1] { "foot.l" }, ["foot.l"] = new string[1] { "toe.l" }, ["thigh.r"] = new string[1] { "calf.r" }, ["calf.r"] = new string[1] { "foot.r" }, ["foot.r"] = new string[1] { "toe.r" } }; private static readonly string[] Excluded = new string[12] { "ik", "target", "pole", "twist", "end", "nub", "muzzle", "gun", "item", "jiggle", "socket", "attach" }; private static string Norm(string n) { return new string(n.ToLowerInvariant().Where(char.IsLetterOrDigit).ToArray()); } public static char? Side(string name) { string text = name.ToLowerInvariant(); if (text.Contains("left")) { return 'l'; } if (text.Contains("right")) { return 'r'; } string[] source = text.Split(new char[6] { '.', '_', ' ', '-', '|', ':' }, StringSplitOptions.RemoveEmptyEntries); if (source.Contains("l")) { return 'l'; } if (source.Contains("r")) { return 'r'; } if ((name.Length > 1 && char.IsLower(name[name.Length - 2])) || (name.Length > 1 && char.IsDigit(name[name.Length - 2]))) { if (name.EndsWith("L")) { return 'l'; } if (name.EndsWith("R")) { return 'r'; } } return null; } private static bool NameIs(string name, params string[] keys) { string n = Norm(name); if (name.ToLowerInvariant().Split(new char[6] { '.', '_', ' ', '-', '|', ':' }, StringSplitOptions.RemoveEmptyEntries).Any((string t) => Excluded.Contains(t)) || Excluded.Any((string e) => e.Length > 2 && n.Contains(e))) { return false; } return keys.Any((string k) => n.Contains(k)); } private static bool IsAncestor(List<TargetBone> b, int anc, int i) { for (int parent = b[i].Parent; parent >= 0; parent = b[parent].Parent) { if (parent == anc) { return true; } } return false; } private static int SkinnedSelfOrAncestor(List<TargetBone> b, int i) { while (i >= 0 && !b[i].Skinned) { i = b[i].Parent; } return i; } private static int SkinnedParent(List<TargetBone> b, int i) { if (i >= 0) { return SkinnedSelfOrAncestor(b, b[i].Parent); } return -1; } private static int Lca(List<TargetBone> b, int i, int j) { HashSet<int> hashSet = new HashSet<int>(); for (int num = i; num >= 0; num = b[num].Parent) { hashSet.Add(num); } for (int num2 = j; num2 >= 0; num2 = b[num2].Parent) { if (hashSet.Contains(num2)) { return num2; } } return -1; } private static int FindByName(List<TargetBone> b, char? side, params string[] keys) { List<int> list = (from i in Enumerable.Range(0, b.Count) where b[i].Skinned && NameIs(b[i].Name, keys) && (!side.HasValue || Side(b[i].Name) == side) select i).ToList(); if (list.Count == 0) { return -1; } return (from i in list orderby Depth(b, i), b[i].Name.Length select i).First(); } private static int Depth(List<TargetBone> b, int i) { int num = 0; for (int parent = b[i].Parent; parent >= 0; parent = b[parent].Parent) { num++; } return num; } public static Dictionary<string, int> FindRoles(List<TargetBone> b, Dictionary<string, int> hints, Dictionary<string, string> overrides, List<string> log) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); char[] array = new char[2] { 'l', 'r' }; for (int i = 0; i < array.Length; i++) { char value = array[i]; int num = Pick("hand." + value, value, new string[2] { "hand", "wrist" }); int foot = Pick("foot." + value, value, new string[2] { "foot", "ankle" }); if (num >= 0) { dictionary["hand." + value] = num; int num2 = Pick("forearm." + value, value, new string[3] { "forearm", "lowerarm", "elbow" }); if (num2 < 0 || !IsAncestor(b, num2, num)) { num2 = SkinnedParent(b, num); } int num3 = Pick("upperarm." + value, value, new string[3] { "upperarm", "uparm", "humerus" }); if (num3 < 0 || !IsAncestor(b, num3, num2)) { num3 = SkinnedParent(b, num2); } if (num2 >= 0) { dictionary["forearm." + value] = num2; } if (num3 >= 0) { dictionary["upperarm." + value] = num3; } } if (foot >= 0) { dictionary["foot." + value] = foot; int num4 = Pick("calf." + value, value, new string[4] { "calf", "shin", "lowerleg", "knee" }); if (num4 < 0 || !IsAncestor(b, num4, foot)) { num4 = SkinnedParent(b, foot); } int num5 = Pick("thigh." + value, value, new string[4] { "thigh", "upperleg", "upleg", "femur" }); if (num5 < 0 || !IsAncestor(b, num5, num4)) { num5 = SkinnedParent(b, num4); } if (num4 >= 0) { dictionary["calf." + value] = num4; } if (num5 >= 0) { dictionary["thigh." + value] = num5; } int num6 = (from index in Enumerable.Range(0, b.Count) where b[index].Skinned && SkinnedParent(b, index) == foot orderby NameIs(b[index].Name, "toe", "ball") ? 1 : 0 descending select index).DefaultIfEmpty(-1).First(); if (num6 >= 0) { dictionary["toe." + value] = num6; } } } if (dictionary.TryGetValue("upperarm.l", out var value2) && dictionary.TryGetValue("upperarm.r", out var value3)) { int num7 = SkinnedParent(b, value2); int num8 = SkinnedParent(b, value3); int num9; if (num7 >= 0 && num7 == num8) { num9 = num7; } else { num9 = SkinnedSelfOrAncestor(b, Lca(b, value2, value3)); if (num7 >= 0 && num7 != num9) { dictionary["clavicle.l"] = num7; } if (num8 >= 0 && num8 != num9) { dictionary["clavicle.r"] = num8; } } if (num9 >= 0) { dictionary["chest"] = num9; } } else { int num10 = Pick("chest", null, new string[5] { "chest", "spine2", "spine3", "upperchest", "thorax" }); if (num10 >= 0) { dictionary["chest"] = num10; } } if (dictionary.TryGetValue("thigh.l", out var value4) && dictionary.TryGetValue("thigh.r", out var value5)) { int num11 = SkinnedParent(b, value4); int num12 = SkinnedParent(b, value5); dictionary["pelvis"] = ((num11 >= 0 && num11 == num12) ? num11 : SkinnedSelfOrAncestor(b, Lca(b, value4, value5))); } else { int num13 = Pick("pelvis", null, new string[3] { "pelvis", "hip", "hips" }); if (num13 >= 0) { dictionary["pelvis"] = num13; } } int num14 = Pick("head", null, new string[1] { "head" }); if (num14 >= 0) { dictionary["head"] = num14; int num15 = SkinnedParent(b, num14); if (num15 >= 0 && (!dictionary.TryGetValue("chest", out var value6) || num15 != value6)) { dictionary["neck"] = num15; } } if (dictionary.TryGetValue("chest", out var value7) && dictionary.TryGetValue("pelvis", out var value8) && IsAncestor(b, value8, value7)) { List<int> list = new List<int>(); int num16 = SkinnedParent(b, value7); while (num16 >= 0 && num16 != value8) { list.Add(num16); num16 = SkinnedParent(b, num16); } if (list.Count > 0) { dictionary["spine"] = list[list.Count - 1]; } } if (overrides != null) { foreach (KeyValuePair<string, string> kv in overrides) { int num17 = b.FindIndex((TargetBone x) => x.Name == kv.Value); if (num17 >= 0) { dictionary[kv.Key] = SkinnedSelfOrAncestor(b, num17); log.Add("override " + kv.Key + " -> " + kv.Value); continue; } log.Add("override " + kv.Key + " -> " + kv.Value + ": no bone with that name"); } } string[] roles = Roles; foreach (string text in roles) { log.Add(dictionary.TryGetValue(text, out var value9) ? $"role {text,-12} = {b[value9].Name} {b[value9].Pos}" : $"role {text,-12} = (not found)"); } return dictionary; int Hint(string role) { if (hints != null && hints.TryGetValue(role, out var value10) && value10 >= 0) { return SkinnedSelfOrAncestor(b, value10); } return -1; } int Pick(string role, char? side, string[] keys) { int num18 = FindByName(b, side, keys); if (num18 < 0) { return Hint(role); } return num18; } } private static string Group(string role) { if (role != null) { if (!role.StartsWith("clavicle") && !role.StartsWith("upperarm")) { if (!(role == "neck") && !(role == "head")) { return null; } return "head"; } return "shoulder" + role.Substring(role.Length - 1); } return null; } public static RetargetResult Fit(SourceModel src, List<TargetBone> tgt, Dictionary<string, int> roles, Options opt) { RetargetResult retargetResult = new RetargetResult { RoleToTarget = roles }; List<string> log = retargetResult.Log; List<SourceBone> sb = src.Bones; int count = sb.Count; Dictionary<string, int> srcRole = new Dictionary<string, int>(); for (int i = 0; i < count; i++) { if (!string.IsNullOrEmpty(sb[i].Role) && !srcRole.ContainsKey(sb[i].Role)) { srcRole[sb[i].Role] = i; } } string[] array = new string[4] { "pelvis", "head", "upperarm.l", "upperarm.r" }; foreach (string text in array) { if (!Has(text)) { throw new Exception("Could not identify the '" + text + "' bone on the target skeleton. See the log for the bone list and set BoneOverrides in the config."); } } bool flag = Has("foot.l") && Has("foot.r"); V3 normalized = (S("head") - S("pelvis")).Normalized; V3 normalized2 = (T("head") - T("pelvis")).Normalized; V3 v = S("upperarm.l") - S("upperarm.r"); V3 v2 = T("upperarm.l") - T("upperarm.r"); v = (v - normalized * V3.Dot(v, normalized)).Normalized; v2 = (v2 - normalized2 * V3.Dot(v2, normalized2)).Normalized; V3 v3 = ((!Has("toe.l") || !Has("foot.l")) ? V3.Cross(v, normalized) : (S("toe.l") - S("foot.l"))); v3 = (v3 - normalized * V3.Dot(v3, normalized) - v * V3.Dot(v3, v)).Normalized; V3 v4 = V3.Cross(normalized2, v2).Normalized; if (Has("toe.l") && Has("foot.l")) { V3 a = T("toe.l") - T("foot.l"); a -= normalized2 * V3.Dot(a, normalized2); if (a.Length > 1E-06 && V3.Dot(a, v4) < 0.0) { log.Add("toe direction disagrees with Cross(up,left); trusting toes"); v4 = -v4; } } double num = V3.Dot(S("head") - FootMid(S, flag), normalized); double num2 = V3.Dot(T("head") - FootMid(T, flag && roles.ContainsKey("foot.l") && roles.ContainsKey("foot.r")), normalized2); double num3 = num2 / num * opt.ScaleMultiplier; log.Add($"source height {num:0.###}, target height {num2:0.###}, scale {num3:0.#####}"); M3 m = M3.FromRows(v, normalized, v3); M3 m2 = M3.FromCols(v2, normalized2, v4); Affine G = new Affine { L = m2 * m * num3 }; G.t = T("pelvis") - G.L * S("pelvis"); V3[] array2 = sb.Select((SourceBone sourceBone) => G.Point(sourceBone.Head)).ToArray(); M3[] array3 = new M3[count]; Affine[] array4 = new Affine[count]; foreach (int item in Enumerable.Range(0, count).OrderBy(delegate(int index) { int num14 = 0; for (int parent2 = sb[index].Parent; parent2 >= 0; parent2 = sb[parent2].Parent) { num14++; } return num14; })) { int parent = sb[item].Parent; M3 m3 = ((parent >= 0) ? array3[parent] : M3.Identity); Affine affine = ((parent >= 0) ? array4[parent] : Affine.Identity); string role = sb[item].Role; if (string.IsNullOrEmpty(role) || !Has(role)) { array3[item] = m3; array4[item] = affine; continue; } bool flag2 = opt.Pinned.Contains(role); V3 v5 = (flag2 ? T(role) : affine.Point(array2[item])); string text2 = Group(role); string role2 = ((parent >= 0 && Has(sb[parent].Role ?? "")) ? sb[parent].Role : null); if (text2 != null && Group(role2) != text2) { if (text2.StartsWith("shoulder")) { v5 -= normalized2 * (opt.ShoulderLower * num2); } else { v5 += normalized2 * (opt.HeadRaise * num2); } } M3 m4 = m3; M3 m5 = M3.Identity; string[] value; string text3 = (Next.TryGetValue(role, out value) ? value.FirstOrDefault(Has) : null); if (text3 != null && opt.Pinned.Contains(text3)) { V3 v6 = G.L * (S(text3) - S(role)) / num3; V3 b = T(text3) - v5; if (v6.Length > 1E-09 && b.Length > 1E-09) { m4 = M3.FromTo(m3 * v6, b) * m3; double val = b.Length / (v6.Length * num3); val = Math.Max(opt.MinAxisScale, Math.Min(opt.MaxAxisScale, val)); m5 = M3.AxisScale(v6, val); } } else if (text3 != null && flag2 && role != "chest") { V3 v7 = G.L * (S(text3) - S(role)) / num3; V3 b2 = T(text3) - v5; if (v7.Length > 1E-09 && b2.Length > 1E-09) { m4 = M3.FromTo(m3 * v7, b2) * m3; } } array3[item] = m4; M3 m6 = m4 * m5; array4[item] = new Affine { L = m6, t = v5 - m6 * array2[item] }; } int[] array5 = new int[count]; for (int num4 = 0; num4 < count; num4++) { int num5 = num4; array5[num4] = -1; while (num5 >= 0) { if (Has(sb[num5].Role ?? "")) { array5[num4] = roles[sb[num5].Role]; break; } num5 = sb[num5].Parent; } if (array5[num4] < 0) { array5[num4] = roles["pelvis"]; } } int num6 = src.Positions.Length; retargetResult.Positions = new V3[num6]; retargetResult.Normals = new V3[num6]; retargetResult.BoneIndices = new int[num6][]; retargetResult.Weights = new float[num6][]; for (int num7 = 0; num7 < num6; num7++) { V3 p = G.Point(src.Positions[num7]); V3 normalized3 = (G.L * src.Normals[num7]).Normalized; V3 v8 = default(V3); V3 v9 = default(V3); Dictionary<int, double> dictionary = new Dictionary<int, double>(); double num8 = 0.0; for (int num9 = 0; num9 < 4; num9++) { double num10 = src.Weights[num7][num9]; if (!(num10 <= 0.0)) { int num11 = src.WeightBones[num7][num9]; v8 += array4[num11].Point(p) * num10; v9 += array3[num11] * normalized3 * num10; num8 += num10; int key = array5[num11]; dictionary[key] = (dictionary.TryGetValue(key, out var value2) ? value2 : 0.0) + num10; } } if (num8 <= 0.0) { v8 = array4[0].Point(p); v9 = array3[0] * normalized3; dictionary[roles["pelvis"]] = 1.0; num8 = 1.0; } else { v8 /= num8; } retargetResult.Positions[num7] = v8; retargetResult.Normals[num7] = v9.Normalized; List<KeyValuePair<int, double>> list = dictionary.OrderByDescending((KeyValuePair<int, double> kv) => kv.Value).Take(4).ToList(); double num12 = list.Sum((KeyValuePair<int, double> kv) => kv.Value); retargetResult.BoneIndices[num7] = new int[4]; retargetResult.Weights[num7] = new float[4]; for (int num13 = 0; num13 < list.Count; num13++) { retargetResult.BoneIndices[num7][num13] = list[num13].Key; retargetResult.Weights[num7][num13] = (float)(list[num13].Value / num12); } } return retargetResult; static V3 FootMid(Func<string, V3> P, bool has) { if (!has) { return P("pelvis"); } return (P("foot.l") + P("foot.r")) / 2.0; } bool Has(string key2) { if (roles.ContainsKey(key2)) { return srcRole.ContainsKey(key2); } return false; } V3 S(string key2) { return sb[srcRole[key2]].Head; } V3 T(string key2) { return tgt[roles[key2]].Pos; } } public static bool NeedsWindingFlip(SourceModel src, List<TargetBone> tgt, Dictionary<string, int> roles, RetargetResult res) { int num = 0; int num2 = 0; for (int i = 0; i + 2 < src.Indices.Length; i += 3) { int num3 = src.Indices[i]; int num4 = src.Indices[i + 1]; int num5 = src.Indices[i + 2]; V3 a = V3.Cross(res.Positions[num4] - res.Positions[num3], res.Positions[num5] - res.Positions[num3]); V3 b = res.Normals[num3] + res.Normals[num4] + res.Normals[num5]; double num6 = V3.Dot(a, b); if (num6 > 0.0) { num++; } else if (num6 < 0.0) { num2++; } } return num2 > num; } } }