├── .gitignore ├── LICENSE ├── MeshTransferToolEditor.cs ├── README.md └── UnityMeshTransferTool-Cascadian.unitypackage /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.meta 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 CascadianWorks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MeshTransferToolEditor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | using UnityEditor; 4 | 5 | #if UNITY_EDITOR 6 | class MeshTransferToolEditor : EditorWindow 7 | { 8 | 9 | public static GameObject Armature; 10 | public static List Mesh_Renderers = new List(); 11 | 12 | public static Dictionary boneMap = new Dictionary(); 13 | 14 | private int meshNum = 0; 15 | 16 | [MenuItem("GameObject/TransferMeshes", false, 0)] 17 | public static void contextMenu(MenuCommand menuCommand) 18 | { 19 | Mesh_Renderers = new List(); 20 | foreach (var gameObject in Selection.gameObjects) 21 | { 22 | if (gameObject.GetComponent() == true) 23 | { 24 | Mesh_Renderers.Add(gameObject.GetComponent()); 25 | Debug.Log("MESH: " + gameObject.name); 26 | } 27 | else 28 | { 29 | Armature = gameObject.GetComponentsInChildren()[1].gameObject; 30 | Debug.Log("ROOT: " + Armature.name); 31 | } 32 | } 33 | 34 | MeshRemap(); 35 | 36 | } 37 | 38 | [MenuItem("Cascadian/MeshTransferTool")] 39 | static void Init() 40 | { 41 | // Get existing open window or if none, make a new one: 42 | MeshTransferToolEditor window = (MeshTransferToolEditor)EditorWindow.GetWindow(typeof(MeshTransferToolEditor)); 43 | window.Show(); 44 | } 45 | 46 | // Put in a map every bones until there is no more children 47 | static void GetBones(Transform pBone) 48 | { 49 | foreach (Transform bone in pBone){ 50 | if (bone.name.Equals("Armature")) { continue; } // skip sub-avatar in main avatar 51 | boneMap[bone.gameObject.name] = bone; 52 | GetBones(bone); 53 | } 54 | } 55 | 56 | //https://answers.unity.com/questions/44355/shared-skeleton-and-animation-state.html 57 | static void BoneRemap(SkinnedMeshRenderer Mesh) 58 | { 59 | Transform[] newBonesList = new Transform[Mesh.bones.Length]; 60 | for (int j = 0; j < Mesh.bones.Length; ++j) 61 | { 62 | Transform tempBone = Mesh.bones[j]; 63 | if (tempBone == null) { continue; } 64 | GameObject bone = tempBone.gameObject; 65 | if (!boneMap.TryGetValue(bone.name, out newBonesList[j])) // Check to see if bone exists in bone map 66 | { 67 | if (boneMap.TryGetValue(bone.transform.parent.name, out Transform pBone)) // try to find the parent reference in the target armature 68 | { 69 | //add the new bones to the target armature 70 | var components = bone.GetComponents(typeof(Component)); 71 | foreach (var component in components) 72 | { 73 | //Debug.Log(component.GetComponent(component)); 74 | } 75 | bone.transform.position += pBone.position - bone.transform.parent.position; 76 | bone.transform.SetParent(pBone); 77 | GetBones(pBone.transform); 78 | boneMap.TryGetValue(bone.name, out newBonesList[j]); 79 | } 80 | else 81 | { 82 | Debug.Log("Unable to map bone \"" + bone.name + "\" to target skeleton."); 83 | } 84 | } 85 | } 86 | 87 | Mesh.bones = newBonesList; 88 | } 89 | 90 | static void TransferMeshes(SkinnedMeshRenderer Mesh) 91 | { 92 | Mesh.gameObject.transform.SetParent(Armature.gameObject.transform.parent); 93 | Mesh.rootBone = Armature.gameObject.transform.parent.gameObject.GetComponent().GetBoneTransform(HumanBodyBones.Hips); 94 | } 95 | 96 | public void OnGUI() 97 | { 98 | GUILayout.Label("Remap Meshes", EditorStyles.largeLabel); 99 | 100 | GUILayout.Space(10f); 101 | 102 | GUILayout.Label("Armature From Target Skeleton:", EditorStyles.boldLabel); 103 | Armature = (GameObject)EditorGUILayout.ObjectField(Armature, typeof(GameObject), true, GUILayout.Height(25f)); 104 | 105 | GUILayout.Space(20f); 106 | 107 | { // Meshes 108 | EditorGUILayout.BeginHorizontal(); 109 | GUILayout.Label("Meshes to Transfer", EditorStyles.boldLabel); 110 | GUIStyle customButton = new GUIStyle("button"); 111 | customButton.fontSize = 20; 112 | 113 | if (GUILayout.Button("-", customButton, GUILayout.Width(25f), GUILayout.Height(25f))) 114 | { 115 | if (meshNum <= 0) {return;} 116 | meshNum--; 117 | Mesh_Renderers.RemoveAt(Mesh_Renderers.Count - 1); 118 | } 119 | 120 | if (GUILayout.Button("+", customButton, GUILayout.Width(25f), GUILayout.Height(25f))) 121 | { 122 | meshNum++; 123 | Mesh_Renderers.Add(null); 124 | } 125 | 126 | EditorGUILayout.EndHorizontal(); 127 | 128 | for (int i = 0; i < meshNum; i++) 129 | { 130 | Mesh_Renderers[i] = (SkinnedMeshRenderer)EditorGUILayout.ObjectField(Mesh_Renderers[i], typeof(SkinnedMeshRenderer), true, GUILayout.Height(30f)); 131 | } 132 | } 133 | 134 | GUILayout.Space(10f); 135 | 136 | if (GUILayout.Button("Remap Meshes")) 137 | { 138 | MeshRemap(); 139 | } 140 | } 141 | 142 | private static void MeshRemap() 143 | { 144 | if (Armature.gameObject.transform.parent.gameObject.GetComponent() == null) 145 | { 146 | Debug.LogError("Base Armature not Humanoid. Please set rig type to humanoid to proceed."); 147 | } 148 | 149 | if (Armature.name.Equals("Armature")) //the gameobject must be the armature of target avatar 150 | { 151 | GetBones(Armature.transform); 152 | 153 | for (int i = 0; i < Mesh_Renderers.Count; i++) 154 | { 155 | SkinnedMeshRenderer Mesh = Mesh_Renderers[i]; 156 | 157 | if (Mesh == null) 158 | { 159 | continue; 160 | } 161 | 162 | if (PrefabUtility.GetPrefabInstanceStatus(Mesh.transform.parent.gameObject) == PrefabInstanceStatus.Connected) //unpack prefab to move mesh and bones to the target avatar 163 | { 164 | PrefabUtility.UnpackPrefabInstance(Mesh.transform.parent.gameObject, unpackMode: PrefabUnpackMode.Completely, action: InteractionMode.AutomatedAction); 165 | } 166 | 167 | BoneRemap(Mesh); 168 | TransferMeshes(Mesh); 169 | } 170 | } 171 | else 172 | { 173 | Debug.LogError("Please select the \"Armature\" of target skeleton."); 174 | } 175 | } 176 | } 177 | #endif 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity Mesh Transfer Tool 2 | 3 | **A Unity Editor utility that can transfer skinned mesh renderers from one armature to another. 4 | This does not automatically weight anything, it just lets you use a mesh on a specified armature instead of only the one it was imported with.** 5 | 6 | ***Some uses for this tool include:*** 7 | - Adding meshes to an existing configured model 8 | - Replacing meshes to update instead of reimporting 9 | - Refreshing every mesh on the model while maintinging original armature 10 | 11 | *(And more)*

12 | [![](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/cascascas) 13 |

14 | 15 | 16 | # **Download** 17 | To download, go to the releases and download the .cs file and add it to your Unity project. 18 | 19 | https://github.com/CascadianWorks/Skinned-Mesh-Armature-Remapper/releases 20 | 21 | # Videos 22 | ### Example: 23 | https://user-images.githubusercontent.com/90723146/139330629-c4a3f25f-565b-4424-8895-b95f454c1310.mp4 24 | 25 | 26 | ### Youtube Walkthrough Tutorial: 27 | [Youtube Link](https://www.youtube.com/watch?v=N2BVAn7m2y4) 28 | 29 | # How to Use 30 | **1. You can access the editor window by toing to the top toolbar and going to Tools/Cascadian/Skinned-Mesh-Armature-Remapper.** 31 | 32 | ![image](https://user-images.githubusercontent.com/90723146/138570521-a9e0e431-d6f8-456c-a9e0-1439e369c71d.png) 33 | 34 | **2. Drag in the "Armature" object located under the name of your avatar to the first slot.** ***Make sure the Base armature is humanoid.*** 35 | 36 | ![image](https://user-images.githubusercontent.com/90723146/138570540-46d97e76-2f1f-485a-bdb8-c6738298bacb.png) 37 | 38 | **3. After that, drag in the skinned meshes to the list that you want to transfer.** 39 | 40 | ![image](https://user-images.githubusercontent.com/90723146/138570556-e541b9b6-cc01-4f53-a383-1a4bb899e21d.png) 41 | 42 | *(In this case, the Shoes and Skirt are being remapped to match the armature the body is attached to)* 43 | 44 | **4. Press the "Remap Bones" Button and that's it! You can delete the old empty armatures from the scene now.** ***Only bones with the SAME NAME will be remapped. Any new bones will added.*** 45 | 46 | **BEFORE:** 47 | 48 | ![image](https://user-images.githubusercontent.com/90723146/138570569-d66afa7d-7e5e-48ef-b035-261811935743.png) 49 | 50 | **AFTER:** 51 | 52 | ![image](https://user-images.githubusercontent.com/90723146/138570575-f82bccfa-2a1b-4766-8208-2723c2c29663.png) 53 | 54 | 55 | # **Issues/Improvements to Be Done** 56 | - Take default humanoid bone names into account (So even if one is named Shoulder_L and other is LeftShoulder, it will still map) 57 | - Maybe add missing bones to target armature?? (Maybe Possible) 58 | 59 | -------------------------------------------------------------------------------- /UnityMeshTransferTool-Cascadian.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CascadianVR/Unity-Mesh-Transfer-Utility/6f842448d8d94dacfbcad1b12ad4ec3f025a62a7/UnityMeshTransferTool-Cascadian.unitypackage --------------------------------------------------------------------------------