As I struggled so much with the solution I thought I would post another thread with a more obvious subject so that it will be easier for others to find.
The latest code which worked flawlessly for me is:
//Copyright(c) 2016 Tim McDaniel (TrickyHandz on forum.unity3d.com)
// Updated by Piotr Kosek 2019 (shelim on forum.unity3d.com)
//
// Adapted from code provided by Alima Studios on forum.unity.com
// http://forum.unity3d.com/threads/prefab-breaks-on-mesh-update.282184/#post-2661445
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to
// the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using UnityEngine;
using UnityEditor;
public class UpdateSkinnedMeshWindow : EditorWindow
{
[MenuItem("Window/Update Skinned Mesh Bones")]
public static void OpenWindow()
{
var window = GetWindow<UpdateSkinnedMeshWindow>();
window.titleContent = new GUIContent("Skin Updater");
}
private SkinnedMeshRenderer targetSkin;
private Transform rootBone;
private void OnGUI()
{
targetSkin = EditorGUILayout.ObjectField("Target", targetSkin, typeof(SkinnedMeshRenderer), true) as SkinnedMeshRenderer;
rootBone = EditorGUILayout.ObjectField("RootBone", rootBone, typeof(Transform), true) as Transform;
GUI.enabled = (targetSkin != null && rootBone != null);
if (GUILayout.Button("Update Skinned Mesh Renderer"))
{
Transform[] newBones = new Transform[targetSkin.bones.Length];
for (int i = 0; i < targetSkin.bones.Length; i++)
{
foreach (var newBone in rootBone.GetComponentsInChildren<Transform>())
{
if (newBone.name == targetSkin.bones[i].name)
{
newBones[i] = newBone;
continue;
}
}
}
targetSkin.bones = newBones;
}
}
}
I did a couple changes to the code, basically while doing some stuff with it I needed a bit more of debug info to fix my bones and stuff, hope it helps others too.
Support for disabled transforms.
Extra debug info: bones found, missing bone count, etc.
//Copyright(c) 2016 Tim McDaniel (TrickyHandz on forum.unity3d.com)
// Updated by Piotr Kosek 2019 (shelim on forum.unity3d.com)
// Updated by Orochii Zouveleki 2020: Added a couple extra debug info.
//
// Adapted from code provided by Alima Studios on forum.unity.com
// http://forum.unity3d.com/threads/prefab-breaks-on-mesh-update.282184/#post-2661445
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to
// the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using UnityEngine;
using UnityEditor;
public class UpdateSkinnedMeshWindow : EditorWindow {
[MenuItem("Window/Update Skinned Mesh Bones")]
public static void OpenWindow()
{
var window = GetWindow<UpdateSkinnedMeshWindow>();
window.titleContent = new GUIContent("Skin Updater");
}
private GUIContent statusContent = new GUIContent("Waiting...");
private SkinnedMeshRenderer targetSkin;
private Transform rootBone;
private bool includeInactive;
private string statusText = "Waiting...";
private void OnGUI()
{
targetSkin = EditorGUILayout.ObjectField("Target", targetSkin, typeof(SkinnedMeshRenderer), true) as SkinnedMeshRenderer;
rootBone = EditorGUILayout.ObjectField("RootBone", rootBone, typeof(Transform), true) as Transform;
includeInactive = EditorGUILayout.Toggle("Include Inactive", includeInactive);
bool enabled = (targetSkin != null && rootBone != null);
if (!enabled) {
statusText = "Add a target SkinnedMeshRenderer and a root bone to process.";
}
GUI.enabled = enabled;
if (GUILayout.Button("Update Skinned Mesh Renderer"))
{
statusText = "== Processing bones... ==";
// Look for root bone
string rootName = "";
if (targetSkin.rootBone != null) rootName = targetSkin.rootBone.name;
Transform newRoot = null;
// Reassign new bones
Transform[] newBones = new Transform[targetSkin.bones.Length];
Transform[] existingBones = rootBone.GetComponentsInChildren<Transform>(includeInactive);
int missingBones = 0;
for (int i = 0; i < targetSkin.bones.Length; i++)
{
if (targetSkin.bones[i] == null) {
statusText += System.Environment.NewLine + "WARN: Do not delete the old bones before the skinned mesh is processed!";
missingBones++;
continue;
}
string boneName = targetSkin.bones[i].name;
bool found = false;
foreach (var newBone in existingBones)
{
if (newBone.name == rootName) newRoot = newBone;
if (newBone.name == boneName)
{
statusText += System.Environment.NewLine + "· " + newBone.name + " found!";
newBones[i] = newBone;
found = true;
}
}
if (!found) {
statusText += System.Environment.NewLine + "· " + boneName + " missing!";
missingBones++;
}
}
targetSkin.bones = newBones;
statusText += System.Environment.NewLine + "Done! Missing bones: " + missingBones;
if (newRoot != null) {
statusText += System.Environment.NewLine + "· Setting " + rootName + " as root bone.";
targetSkin.rootBone = newRoot;
}
}
// Draw status because yeh why not?
statusContent.text = statusText;
EditorStyles.label.wordWrap = true;
GUILayout.Label(statusContent);
}
}
Sorry for the extra late reply, you probably figured out something by yourself at this point.
Techincally speaking you can do this same thing in runtime, only problem is that you’ll need to spawn the whole thing with an exact duplicate of the armature. I.e. spawn a whole fbx prefab, then run the code, then you’ll probably want to delete the old armature that came up with your skinned meshes.
The other way around this is that, since all we need from the original armature are the bone names, you can cache that as a list of strings, keeping the same order, store it somewhere, and then use that as reference to know what bones to assign and where, similar to what this code does. That way you can save up the hazzle of spawning the whole armature, and just spawn the necessary stuff.
Guys! You can simply duplicate original skinned mesh renderer object inside prefab, then you need to assign your new mesh to the mesh field of the duplicated object. And booom, now you have 2 skinned meshes on one armature ;*
oh my god if Vladislav6 didn’t save me here I would’ve been searching all day! You can indeed just switch the mesh of a skinned mesh renderer to change a meshes skeleton! I did this with my clothes system and it works just fine. I made the clothes in blender using the player skeleton as a reference and then I used this to make it use the players real skeleton. Works fine! Thanks Vladisla6!
I add Cloth Class to save bone names bc when im making a prefab from this swapped object bone array was getting clear so i must save this names in custom class
using UnityEditor;
using UnityEngine;
public class UpdateSkinnedMeshWindow : EditorWindow
{
[MenuItem(“Window/Update Skinned Mesh Bones”)]
public static void OpenWindow()
{
var window = GetWindow();
window.titleContent = new GUIContent(“Skin Updater”);
}
private SkinnedMeshRenderer targetSkin;
private Transform rootBone;
private void OnGUI()
{
targetSkin = EditorGUILayout.ObjectField(“Target”, targetSkin, typeof(SkinnedMeshRenderer), true) as SkinnedMeshRenderer;
rootBone = EditorGUILayout.ObjectField(“RootBone”, rootBone, typeof(Transform), true) as Transform;
GUI.enabled = (targetSkin != null && rootBone != null);
if (GUILayout.Button(“Update Skinned Mesh Renderer and add Cloth component”))
{
Transform[ ] newBones = new Transform[targetSkin.bones.Length];
Can someone explain to a noob why one would use the script posted (and iterated on) above, if you can just do this? What’s the advantage of using the script? thank you
Because the most important thing about the SkinnedMeshRenderer is the bones array (which is not visible in the inspector and is created when the model is imported). This array contains the actual reference to the concrete gameobject / transform instances of each bone. When you duplicate a SkinnedMeshRenderer, you would also duplicate the actual bone gameobject hierarchy. So each SMR would have it’s own bone gameobjects. In order to “combine” two SMR on one bone hierarchy, they would need to use the same bones. Note that when you import two meshes which have the same skeleton, the bones array could still be in a different order. This order is mesh specific. The order of the bones in the SMR is directly related to the order of the skinning information in the mesh itself. Namely bindPoses and boneWeights.
So when you import several meshes with the same skeleton, the order of the bones is not guaranteed. That’s what the script does. It matches the bones based on the names. Though those scripts all use a quite naive approach to match the bones in an O(n²) manner. For meshes large number of bones this would not be a good implementation. You usually want to use a Dictionary<string, Transform> to look up the target bone instances when you iterate through the old bones.
I’d like to thank the people that made and then improved this script. You literally saved my hobby project. I have a bunch of clothing for my character, and I just couldnt get the animations to sync up, and moving the clothes to the character just didnt work…
This however fixed that issue, thank you. You saved me hours of learning more indeph 3d modelling (If I didnt just give up)