Search Unity

  1. Are you interested in providing feedback directly to Unity teams? Sign up to become a member of Unity Pulse, our new product feedback and research community.
    Dismiss Notice

Prefab Breaks on Mesh Update

Discussion in 'Animation' started by LightStriker, Nov 25, 2014.

  1. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,662
    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?
     
  2. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,662
  3. Pixeldamage

    Pixeldamage

    Joined:
    Sep 6, 2010
    Posts:
    52
    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.
     
    Z-Dom and theANMATOR2b like this.
  4. aer0ace

    aer0ace

    Joined:
    May 11, 2012
    Posts:
    1,376
    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!
     
    Pixeldamage likes this.
  5. Alima-Studios

    Alima-Studios

    Joined:
    Nov 12, 2014
    Posts:
    72
    this bug still persits
     
  6. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,662
    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.
     
  7. BlankMauser

    BlankMauser

    Joined:
    Dec 10, 2013
    Posts:
    117
    This is a problem for me as well. This seems like something that should be common functionality in Unity.
     
  8. Alima-Studios

    Alima-Studios

    Joined:
    Nov 12, 2014
    Posts:
    72
    please fix this , I cant belive this persits ,
     
    M_R_M likes this.
  9. Alima-Studios

    Alima-Studios

    Joined:
    Nov 12, 2014
    Posts:
    72
    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

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class resetbonemap : MonoBehaviour {
    6.     //public GameObject target;
    7.     public GameObject original;
    8.  
    9.     //internal Mesh mesh;
    10.    
    11.     internal SkinnedMeshRenderer skin;
    12.     void Awake()
    13.     {
    14.  
    15.         //skin = GetComponent<SkinnedMeshRenderer>();
    16.        
    17.  
    18.  
    19.         //gameObject.GetComponent<SkinnedMeshRenderer>().rootBone = GameObject.FindGameObjectWithTag("pelvisbebe").transform;
    20.         //tela = GetComponent<SkinnedMeshRenderer>().sharedMesh;
    21.  
    22.         //skin.sharedMesh = tela;
    23.  
    24.  
    25.  
    26.     }
    27.  
    28.     void Start()
    29.     {
    30.         //mesh.boneWeights = tela.boneWeights;
    31.        
    32.         SkinnedMeshRenderer targetRenderer = GetComponent<SkinnedMeshRenderer>();
    33.         SkinnedMeshRenderer originalRenderer = original.GetComponent<SkinnedMeshRenderer>();
    34.         //Dictionary<string, Transform> boneMap = new Dictionary<string, Transform>();
    35.  
    36.        
    37.         SkinnedMeshRenderer myRenderer = GetComponent<SkinnedMeshRenderer>();
    38.         Transform[] newBones = new Transform[myRenderer.bones.Length];
    39.  
    40.         int a = 0;
    41.         foreach (Transform boneoriginal in originalRenderer.bones)
    42.         {
    43.             int b = 0;
    44.             foreach (Transform bonenuevo in targetRenderer.bones)
    45.             {
    46.                 if (bonenuevo.name == boneoriginal.name)
    47.                 {
    48.                     newBones[a] = targetRenderer.bones[b];
    49.                     continue;
    50.                 }
    51.                 b++;
    52.             }
    53.             a++;
    54.         }
    55.        
    56.         targetRenderer.sharedMesh = originalRenderer.sharedMesh;
    57.  
    58.         targetRenderer.bones = newBones;
    59.  
    60.     }
    61.  
    62.  
    63.  
    64.  
    65.  
    66. }
    67.  
     
    Last edited: Apr 10, 2016
    andreiagmu and mbowen89 like this.
  10. mbowen89

    mbowen89

    Joined:
    Jan 21, 2013
    Posts:
    639
    This worked great, thanks!!
     
  11. TrickyHandz

    TrickyHandz

    Joined:
    Jul 23, 2010
    Posts:
    196
    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.
    Code (csharp):
    1.  
    2. //Copyright(c) 2016 Tim McDaniel (TrickyHandz on forum.unity3d.com)
    3. // Adapted from code provided by Alima Studios on forum.unity.com
    4. // http://forum.unity3d.com/threads/prefab-breaks-on-mesh-update.282184/#post-2661445
    5.  
    6. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
    7. // software and associated documentation files (the "Software"), to deal in the Software without
    8. // restriction, including without limitation the rights to use, copy, modify, merge, publish,
    9. // distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    10. // Software is furnished to do so, subject to
    11. // the following conditions:
    12.  
    13. // The above copyright notice and this permission notice shall be included in all copies or
    14. // substantial portions of the Software.
    15.  
    16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,  
    17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    18. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    20. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    21. // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    22. // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    23. // SOFTWARE.
    24. using UnityEngine;
    25. using UnityEditor;
    26.  
    27. public class UpdateSkinnedMeshWindow : EditorWindow
    28. {
    29.     [MenuItem("Window/Update Skinned Mesh Bones")]
    30.     public static void OpenWindow()
    31.     {
    32.         var window = GetWindow<UpdateSkinnedMeshWindow>();
    33.         window.titleContent = new GUIContent("Skin Updater");
    34.     }
    35.  
    36.     private SkinnedMeshRenderer targetSkin;
    37.     private SkinnedMeshRenderer originalSkin;
    38.  
    39.     private void OnGUI()
    40.     {
    41.         target = EditorGUILayout.ObjectField("Target", targetSkin, typeof(SkinnedMeshRenderer), true) as SkinnedMeshRenderer;
    42.         original = EditorGUILayout.ObjectField("Original", originalSkin, typeof(SkinnedMeshRenderer), true) as SkinnedMeshRenderer;
    43.  
    44.         GUI.enabled = (targetSkin != null && originalSkin != null);
    45.  
    46.         if (GUILayout.Button("Update Skinned Mesh Renderer")) {
    47.  
    48.             Transform[] newBones = new Transform[targetSkin.bones.Length];
    49.  
    50.             int a = 0;
    51.             for(int i=0; i<originalSkin.bones.Length; i++      
    52.             foreach (var originalBone in originalSkin.bones) {
    53.                 int b = 0;
    54.                 foreach (var newBone in targetSkin.bones) {
    55.                     if (newBone.name == originalBone.name) {
    56.                         newBones[a] = targetSkin.bones[b];
    57.                         continue;
    58.                     }
    59.                     b++;
    60.                 }
    61.                 a++;
    62.             }
    63.             targetSkin.sharedMesh = originalSkin.sharedMesh;
    64.             targetSkin.bones = newBones;
    65.         }
    66.     }
    67. }
    68.  
    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.]
     
    Last edited: Jun 3, 2016
  12. mbowen89

    mbowen89

    Joined:
    Jan 21, 2013
    Posts:
    639
    Awesome idea. Did you do this because it was something you'd be using as well? :)
     
  13. TrickyHandz

    TrickyHandz

    Joined:
    Jul 23, 2010
    Posts:
    196
    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.
     
    theANMATOR2b likes this.
  14. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,662
    Your script appear to work only if one does not add or remove bones from the previous imported list, right?
     
  15. TrickyHandz

    TrickyHandz

    Joined:
    Jul 23, 2010
    Posts:
    196
    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.
     
  16. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,662
    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.
     
    TrickyHandz likes this.
  17. TrickyHandz

    TrickyHandz

    Joined:
    Jul 23, 2010
    Posts:
    196
    @LightStriker, 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.
     
  18. rikke.jansen

    rikke.jansen

    Joined:
    Jan 11, 2016
    Posts:
    1
    @TrickyHandz: Script doesn't seem to work for me :(

    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?
     
  19. mbowen89

    mbowen89

    Joined:
    Jan 21, 2013
    Posts:
    639
    You are going to have to create a custom script for your situation probably if you want to TRY and fully automate it.
     
  20. joaobsneto

    joaobsneto

    Joined:
    Dec 10, 2009
    Posts:
    152
    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.
     
  21. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    7,229
    It's not, what do you mean by marked solved?

    It sounds serious if your diagnose is right. It would worth to submit a proper bug report and give Unity a chance to properly follow the steps to recreate this bug so they can fix it.
     
  22. sacb0y

    sacb0y

    Joined:
    May 9, 2016
    Posts:
    351
    That scrip has a lot of errors and doesn't work for me.
     
  23. shelim

    shelim

    Joined:
    Aug 26, 2017
    Posts:
    11
    Here is working version (2019).

    Note that you will NOT get confirmation message after applying the window;

    You need to provide new object to reparent as well as root bone from the new system.
    It will work only if bone names are exactly the same.

    NOTE: you need to change Root Bone inspector setting in newly reparented object manually.

    Code (CSharp):
    1. //Copyright(c) 2016 Tim McDaniel (TrickyHandz on forum.unity3d.com)
    2. // Updated by Piotr Kosek 2019 (shelim on forum.unity3d.com)
    3. //
    4. // Adapted from code provided by Alima Studios on forum.unity.com
    5. // http://forum.unity3d.com/threads/prefab-breaks-on-mesh-update.282184/#post-2661445
    6.  
    7. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
    8. // software and associated documentation files (the "Software"), to deal in the Software without
    9. // restriction, including without limitation the rights to use, copy, modify, merge, publish,
    10. // distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
    11. // Software is furnished to do so, subject to
    12. // the following conditions:
    13.  
    14. // The above copyright notice and this permission notice shall be included in all copies or
    15. // substantial portions of the Software.
    16.  
    17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    18. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    19. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    20. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    21. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    22. // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
    23. // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    24. // SOFTWARE.
    25. using UnityEngine;
    26. using UnityEditor;
    27.  
    28. public class UpdateSkinnedMeshWindow : EditorWindow
    29. {
    30.     [MenuItem("Window/Update Skinned Mesh Bones")]
    31.     public static void OpenWindow()
    32.     {
    33.         var window = GetWindow<UpdateSkinnedMeshWindow>();
    34.         window.titleContent = new GUIContent("Skin Updater");
    35.     }
    36.  
    37.     private SkinnedMeshRenderer targetSkin;
    38.     private Transform rootBone;
    39.  
    40.     private void OnGUI()
    41.     {
    42.         targetSkin = EditorGUILayout.ObjectField("Target", targetSkin, typeof(SkinnedMeshRenderer), true) as SkinnedMeshRenderer;
    43.         rootBone = EditorGUILayout.ObjectField("RootBone", rootBone, typeof(Transform), true) as Transform;
    44.  
    45.         GUI.enabled = (targetSkin != null && rootBone != null);
    46.  
    47.         if (GUILayout.Button("Update Skinned Mesh Renderer"))
    48.         {
    49.  
    50.             Transform[] newBones = new Transform[targetSkin.bones.Length];
    51.  
    52.             for (int i = 0; i < targetSkin.bones.Length; i++)
    53.             {
    54.                 foreach (var newBone in rootBone.GetComponentsInChildren<Transform>())
    55.                 {
    56.                     if (newBone.name == targetSkin.bones[i].name)
    57.                     {
    58.                         newBones[i] = newBone;
    59.                         continue;
    60.                     }
    61.                 }
    62.             }
    63.             targetSkin.bones = newBones;
    64.         }
    65.     }
    66. }
     
    chakaramba, DevDop, Alekxss and 10 others like this.
  24. Deleted User

    Deleted User

    Guest

    Thanks a lot !!!
     
  25. LuckskyTR

    LuckskyTR

    Joined:
    Feb 2, 2019
    Posts:
    1
    This is awesome !!!
     
  26. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,662
    7 years later... Unity still hasn't figured it out. Well, at least now we have a copy-paste in the inspector! Just took 12 years.
     
    M_R_M likes this.
  27. chakaramba

    chakaramba

    Joined:
    Apr 16, 2019
    Posts:
    4
    Hello there from 2021 Q2! Thanks a lot to @shelim for that snippet :)
     
  28. Alima-Studios

    Alima-Studios

    Joined:
    Nov 12, 2014
    Posts:
    72
    Can't believe Unity still has this problem!
     
    M_R_M likes this.
  29. mrphilipjoel

    mrphilipjoel

    Joined:
    Jul 6, 2019
    Posts:
    7
    How can I achieve this at runtime? I'm importing a .glb from the web.
     
unityunity