Prefab Breaks on Mesh Update

We have a file (FBX or OBJ) from which we import skinned meshes and their animation.

From that file, we create a prefab, on which we add a collection of scripts.

Our issue is simple; if we add vertices or have a vertex skinned on a bone it wasn’t previously skinned on, there’s the chance the prefab would break down and not properly be animated.

We tracked down the issue to this;

  • When you import a skinned mesh, the BoneWeight structure uses indices that are link to the list of bones in the SkinnedMeshRenderer component.
  • The SkinnedMeshRenderer “bones” list does not references all the bones in a skeleton, but only the one the mesh is using.
  • If the mesh is updated to target another bone, the SkinnedMeshRenderer list of bone in the prefab is obviously not updated. Which means the BoneWeight suddenly targets the wrong transform, or no transform at all.

Have you ever reimported a skinned mesh just to find that it broke down in a prefab? Is there a work around?

Bump?

Hey LightStriker. I see this is an old post (9 months ago) but it’s an issue that is really really frustrating. I’m responsible for all the characters / 3D on our project and I can’t tell you the number of times I’ve rebuilt their prefabs. So much so that I eventually got one of our coders to create a PFM (prefab manager) to auto generate them everytime I update the source FBX. So many things can break them - removes zero weights, mirroring weights if you’ve updated something etc etc. There’s also other weird stuff like it’ll remember the material ID of the prefab using the vertex order rather than any assigned mat IDs from 3dsmax. Found this out the hard way after hours of trying to work out why the eye materials were being displayed on the heads etc. Only way around it was reassigning mat IDs in 3dsmax according to the vertex order.

2 Likes

It’s unfortunate that this hasn’t been fixed yet. Good to know that others have a possible solution/workaround. A prefab manager sounds like a good idea!

1 Like

this bug still persits

Usually, in other engine I’ve worked with, when importing a rigged mesh, the importer would create a secondary file; a skeleton. That file is simply an index of all the bones, a list of their name in the proper imported order. It would be recreated every time the mesh is reimported. Many skinned prefab could target the same skeleton.

The component tasked with animating a mesh - in this case, SkinnedMeshRenderer - would only reference the skeleton file. When enabled, it would instantiate and map the rig, looping in the list and finding the proper bones based on their name.

This is a problem for me as well. This seems like something that should be common functionality in Unity.

please fix this , I cant belive this persits ,

1 Like

maybe this script could help

atatch to broken skinmesh

drag to original unbroken skinmesh (just drag to scene the fbxas new instance)

pres play , copy fixed compoment

press stop , paste values

remove the script

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class resetbonemap : MonoBehaviour {
    //public GameObject target;
    public GameObject original;

    //internal Mesh mesh;
   
    internal SkinnedMeshRenderer skin;
    void Awake()
    {

        //skin = GetComponent<SkinnedMeshRenderer>();
       


        //gameObject.GetComponent<SkinnedMeshRenderer>().rootBone = GameObject.FindGameObjectWithTag("pelvisbebe").transform;
        //tela = GetComponent<SkinnedMeshRenderer>().sharedMesh;

        //skin.sharedMesh = tela;



    }

    void Start()
    {
        //mesh.boneWeights = tela.boneWeights;
       
        SkinnedMeshRenderer targetRenderer = GetComponent<SkinnedMeshRenderer>();
        SkinnedMeshRenderer originalRenderer = original.GetComponent<SkinnedMeshRenderer>();
        //Dictionary<string, Transform> boneMap = new Dictionary<string, Transform>();

       
        SkinnedMeshRenderer myRenderer = GetComponent<SkinnedMeshRenderer>();
        Transform[] newBones = new Transform[myRenderer.bones.Length];

        int a = 0;
        foreach (Transform boneoriginal in originalRenderer.bones)
        {
            int b = 0;
            foreach (Transform bonenuevo in targetRenderer.bones)
            {
                if (bonenuevo.name == boneoriginal.name)
                {
                    newBones[a] = targetRenderer.bones[b];
                    continue;
                }
                b++;
            }
            a++;
        }
       
        targetRenderer.sharedMesh = originalRenderer.sharedMesh;

        targetRenderer.bones = newBones;

    }





}
3 Likes

This worked great, thanks!!

I took a look at the script above and modified it into and Editor Window found at “Window/Update Skinned Mesh Renderer”. Just place this in an “Editor” folder in the project and feel free to test this out to see if it accomplishes the same without having to do any copy and paste.

//Copyright(c) 2016 Tim McDaniel (TrickyHandz 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 SkinnedMeshRenderer originalSkin;

    private void OnGUI()
    {
        target = EditorGUILayout.ObjectField("Target", targetSkin, typeof(SkinnedMeshRenderer), true) as SkinnedMeshRenderer;
        original = EditorGUILayout.ObjectField("Original", originalSkin, typeof(SkinnedMeshRenderer), true) as SkinnedMeshRenderer;

        GUI.enabled = (targetSkin != null && originalSkin != null);

        if (GUILayout.Button("Update Skinned Mesh Renderer")) {

            Transform[] newBones = new Transform[targetSkin.bones.Length];

            int a = 0;
            for(int i=0; i<originalSkin.bones.Length; i++       
            foreach (var originalBone in originalSkin.bones) {
                int b = 0;
                foreach (var newBone in targetSkin.bones) {
                    if (newBone.name == originalBone.name) {
                        newBones[a] = targetSkin.bones[b];
                        continue;
                    }
                    b++;
                }
                a++;
            }
            targetSkin.sharedMesh = originalSkin.sharedMesh;
            targetSkin.bones = newBones;
        }
    }
}

Note that using the editor window should allow for syncing both scene and project objects, so you could select a prefab and the imported model file and sync those without having to add anything to a scene or sync a scene object with the model file in the project window. Treat this as experimental at the moment, but I would like to know if this helps people out.

[Edit 6/2/2016: Minor changes made to code to use more descriptive variable names and avoid erroneous linking to the Unity Scripting API.]

6 Likes

Awesome idea. Did you do this because it was something you’d be using as well? :slight_smile:

Currently I don’t have a use for it in any of my projects, but I thought it would be useful to have it around just in case. I’m not a 3D artist by any stretch, so changin model files is not something I tend to encounter often. I’ve edited the code snippet above to include the MIT License at the top. This way there is no question about usage. If you find any problems let me know.

2 Likes

Your script appear to work only if one does not add or remove bones from the previous imported list, right?

You are correct, the code only accounts for the amount of bones in the “broken” mesh. I don’t have a good repro case for the problem to help test this out, nor the facility to create one as I know nothing of modeling and rigging. I’m actually at a loss as to why the update to the model asset wouldn’t populate the changes down to all prefabs that reference the model.

Because Unity simply has no reference at all between an imported mesh and the prefab. Like I said above, other engine I worked with use to create a secondary file containing a hierarchical structure of the skeleton to be referenced. I have a feeling people at Unity simply doesn’t know how to handle that case properly.

1 Like

@LightStriker_1 , thanks for all the insight on this. I definitely have something to keep me thinking this weekend now. I’ll see if I can figure out a way to better implement this type of updating.

@TrickyHandz : Script doesn’t seem to work for me :frowning:

I have this same problem but all of my gameobjects have many children with skinned mesh renderers on them, so fixing them all manually will take as much time as just remaking the prefab. Anyone got a solution?

You are going to have to create a custom script for your situation probably if you want to TRY and fully automate it.

Why the thread is marked as solved?

I’m using git repository and sometimes an user edits a prefab, I don’t know why Unity updates the GUID in the meta file, but the scenes that refereces this prefab are not updated. Then the user commits the file and this prefab’s reference is missing in the scene files. Even in the machine of the person who changed the prefab, the references are missing, seems like Unity does not update the scene file when the GUID are changed.

I think GUID was not supossed to change. In my project I add a Temp folder so the artistis can add some artwork files before commiting to the repository. Meta files are created to those files, but they are no sent to the repository. Maybe it could provoked a GUID conflict.

What do you think?

All users are using Windows 10 and Unity 2018.2.8f1.