Search Unity

Prefab Breaks on Mesh Update

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

  1. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    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,717
  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,513
    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:
    78
    this bug still persits
     
  6. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    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:
    138
    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:
    78
    please fix this , I cant belive this persits ,
     
    M_R_M likes this.
  9. Alima-Studios

    Alima-Studios

    Joined:
    Nov 12, 2014
    Posts:
    78
    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
    Arthur-ROLL7, 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.
     
    ml785 and theANMATOR2b like this.
  14. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    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,717
    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. 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:
    874
    That scrip has a lot of errors and doesn't work for me.
     
  23. shelim

    shelim

    Joined:
    Aug 26, 2017
    Posts:
    29
    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. }
     
    Chrisad, Kodo, popov_maks and 16 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,717
    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:
    6
    Hello there from 2021 Q2! Thanks a lot to @shelim for that snippet :)
     
  28. Alima-Studios

    Alima-Studios

    Joined:
    Nov 12, 2014
    Posts:
    78
    Can't believe Unity still has this problem!
     
    FaffyWaffles and M_R_M like this.
  29. mrphilipjoel

    mrphilipjoel

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

    hyunsuPark

    Joined:
    Mar 5, 2017
    Posts:
    1
    thank you you save my life!!
     
  31. atahfarid

    atahfarid

    Joined:
    Jan 23, 2022
    Posts:
    1
    How do I use this script?
     
  32. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
  33. bealion

    bealion

    Joined:
    May 23, 2021
    Posts:
    2
    Alima-Studios likes this.
  34. bealion

    bealion

    Joined:
    May 23, 2021
    Posts:
    2
    I think I have found the solution with prefab variant, we must create a prefab variant from the FBX, then the updates made to that FBX are applied in our variant without affecting the relationship between the bones, or so it seems...
     
    Edy and Alima-Studios like this.
  35. SteenLund

    SteenLund

    Unity Technologies

    Joined:
    Jan 20, 2011
    Posts:
    639
    @bealion

    Exactly! Right click on the FBX file in the project browser and from the context menu select
    Create -> Prefab Variant
    .

    Any further updates to the FBX will automatically update the variant.
     
  36. Subquake

    Subquake

    Joined:
    Dec 21, 2013
    Posts:
    1
    Thank you so much for this! This is exactly what I needed. I got a modular character with individual parts of it as an FBX file and root bones for each part and this allowed me to combine them all to use one root bone structure!
     
  37. 3itg

    3itg

    Joined:
    Jan 22, 2013
    Posts:
    5
    Years (or a decade) later... still extremely helpful.
     
  38. ghellee

    ghellee

    Joined:
    Dec 6, 2017
    Posts:
    5
    Thanks a LOT!!! Your script saved a lot of my time!

    As I spotted, there is one thing to consider when using it - When I deleted the existing Root and then tried to attach it to the target Root, that didn't work. So, left the bones, used the script, and then deleted the unneeded Root.
     
    Onsterion likes this.
  39. alienware377

    alienware377

    Joined:
    Jun 26, 2021
    Posts:
    2
    I can't figure out how to use the script, it just gives errors every way i try to apply it. I'm somewhat new to unity scripting, any help for beginners?
     
  40. booyagrandmma

    booyagrandmma

    Joined:
    Mar 26, 2012
    Posts:
    11
    I actually had issues and errors with the above script as well, some bones were coming back null and even when checking for null bones it wouldn't work.

    my solution was to just grab a reference piece of geo and just copy the whole array of bones. this works in my situation because the body is always there and is consistent. this wont work for everyone.

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4. public class UpdateSkinnedMeshWindow : EditorWindow {
    5.     [MenuItem("Window/Update Skinned Mesh Bones")]
    6.     public static void OpenWindow() {
    7.         var window = GetWindow<UpdateSkinnedMeshWindow>();
    8.         window.titleContent = new GUIContent("Skin Match");
    9.     }
    10.     private SkinnedMeshRenderer targetSkin;
    11.     private SkinnedMeshRenderer referenceSkin;
    12.     private void OnGUI() {
    13.         targetSkin = EditorGUILayout.ObjectField("Target", targetSkin, typeof(SkinnedMeshRenderer), true) as SkinnedMeshRenderer;
    14.         referenceSkin = EditorGUILayout.ObjectField("Reference", referenceSkin, typeof(SkinnedMeshRenderer), true) as SkinnedMeshRenderer;
    15.         GUI.enabled = (targetSkin != null && referenceSkin != null);
    16.         if (GUILayout.Button("Update Skinned Mesh Renderer")) {
    17.             targetSkin.bones = referenceSkin.bones;
    18.         }
    19.     }
    20. }
    21.  
     
  41. BuzzKirill

    BuzzKirill

    Joined:
    Nov 14, 2017
    Posts:
    48
    Guys, how do I use this exactly? My problem is very similar to the OP, except I need to create new assets for an existing character that a previous artist was responsible for. And hard as a try to copy the exact hierarchy as well as various settings, the resulting mesh still ends up being messed up. Even if I copy the existing body part and replace the skinned mesh renderer reference mesh with my own, it still comes out messed up. I believe the bone index thing is the culprit, but using these scripts yields no results unfortunately. Literally I can see no difference, whether I use @shelim's script or @booyagrandmma's.

    Update: I tried exporting the rigged model from Maya instead and that worked (no more Lovecraftian nightmare when trying to use new model with old animation). I also used a version of the script from another thread https://forum.unity.com/threads/how...er-over-to-another-model.921854/#post-9742717
    Unfortunate that I couldn't do this using Blender, there must be a way though.
     
    Last edited: Apr 3, 2024