Search Unity

  1. Unity 2020.2 has been released.
    Dismiss Notice
  2. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Replacing Mesh In SkinnedMeshRenderer Causes Deformation

Discussion in 'General Graphics' started by skalev, Sep 1, 2015.

  1. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    Hi All.

    We have a system to replace parts of a character in order to produce the effect of an outfit change.

    All of our characters use the same rig, and the same avatar. The problem we are facing is a deformation of the replacement mesh that is assigned at either runtime or in editor, which only happens if we DO NOT have "Optimize Game Objects" checked. Below in the screen shot you can see 2 instances of the same model, with the same replacement mesh assigned. The one on the left is completely deformed, this is the one without the "Optimize Game Objects" checked. The one on the right, which has the option checked, works perfectly.

    deformedmesh.jpg


    I'd like to get some insight as to why this is happening, and what can be done (perhaps via script?) to make the non - optimized version work when replacing the mesh.

    * By replacing the mesh I mean assigning a different mesh object onto the mesh property in editor, or into the sharedMesh property at runtime.
    * We cannot use the "Optimize Game Object" option, as it is unsupported by FinalIK, which we need to use in the project.
     
    hersheys72 likes this.
  2. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,782
    I think this is caused by the original character being setup as non-optimized, so the replacement meshes need to match the import settings of the original character mesh - either optimized or not.
    I'm not 100% positive about this, but I believe this was the solution which resolved a similar issue I encountered.
     
  3. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    Thanks for the reply. Unfortunately, this isn't the case, as I stated in the OP, all characters share the same avatar. You can only setup the optimization in the import settings of the Rig part of the original avatar, so when shared, there is no way to setup different settings.
     
  4. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,782
    Yes - you are correct, my mistake.
     
  5. yos_316

    yos_316

    Joined:
    Dec 11, 2013
    Posts:
    6
    I'm getting the same issue.
    Anyone had the same problem?
     
  6. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,408
    When I've run into this before, it turned out to be that the bone order for the mesh was different. Have you tried checking the contents of SkinnedMeshRenderer.bones for your different SkinnedMeshRenderers to see that the bones are in the same order for each?
     
  7. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    Yep, It seems that the bone order is completely different, which must be the issue.
     
  8. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,782
    It's great you found the problem. Sorry I couldn't help, I couldn't replicate the problem.

    @superpig What causes the bone order change and what is the fix?
     
  9. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,408
    From looking at the code, it seems we just use the bones in whatever order the FBX SDK gives them to us. I'm not sure why they're coming out in different orders for different models from your content tool. It might make sense for us to renumber the bones such that they're sorted in a consistent order, but I'm not sure - perhaps there are workflows out there which would be broken by that.

    If you wanted the bones to always be sorted correctly, you could probably write an AssetPostprocessor script to modify all imported skinned meshes to do that.
     
  10. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    @superpig - I'm wondering if re-numbering the bones will mean that we have to re-number the boneWeights as well to accommodate?
     
  11. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,408
    If you reorder the bones in the bones array, you'll need to process all the vertices to update the boneIndex values as well, yes.

    I'd do it by building a Dictionary<int, int> that records the old bone index => new bone index mappings, then run through every vertex and just do boneIndexN = dict[boneIndexN] for each weight on each vertex.
     
  12. theANMATOR2b

    theANMATOR2b

    Joined:
    Jul 12, 2014
    Posts:
    7,782
    @skalev - if it is the exact same rig how is the bone order changing?
    Sorry - there seems to be a little info that is missing - why is the bone order is changing? Is there an extra bone in the chain for the clothing, just for placement or something, but does not have any vertex influence?
     
  13. skalev

    skalev

    Joined:
    Feb 16, 2012
    Posts:
    264
    The bones are the same. The order is different. The modeler is assuming that perhaps minor changes between exports change instanceIDs and causing the order to change.
     
    theANMATOR2b likes this.
  14. tsubaki_t1

    tsubaki_t1

    Joined:
    Jul 2, 2012
    Posts:
    29
    cicha09 and GenaSG like this.
  15. GenaSG

    GenaSG

    Joined:
    Apr 19, 2014
    Posts:
    108
  16. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    26,749
  17. MartinAdcada

    MartinAdcada

    Joined:
    Jan 24, 2020
    Posts:
    6
    For me it worked like this:
    Code (CSharp):
    1. public Mesh femaleMesh;
    2. public Mesh maleMesh;
    3.  
    4.  
    5.  
    6.  
    7.   public void SelectGender(string gender)
    8.     {
    9.         switch (gender)
    10.         {
    11.             case "male":
    12.                 avatarObject.GetComponentInChildren<SkinnedMeshRenderer>().sharedMesh = maleMesh;
    13.                 break;
    14.             case "female":
    15.                 avatarObject.GetComponentInChildren<SkinnedMeshRenderer>().sharedMesh = femaleMesh;
    16.                 break;
    17.         }
    18.     }
     
  18. Rodolfo-Rubens

    Rodolfo-Rubens

    Joined:
    Nov 17, 2012
    Posts:
    1,159
    Hey guys, sorry to bump the thread but in case someone is suffering with this I have a solution that might help.
    I thought my problem had anything to do with the weights, or anything like this but for me that wasn't making any sense since I was changing the mesh for a mesh that was binded with the exactly same bones (and their respective names), it is even in the same prefab under the same animator. But still, I was getting the skinned mesh renderer to deform my mesh.
    Turns out the problem was with the skinned mesh renderer 'bones' property, even if they share the same skeleton, we need to update their bones because they might use or not the same bones of the mesh that was originally there in the skinned mesh renderer. What I did was this:

    Code (csharp):
    1.  
    2. private void ReplaceBones(SkinnedMeshRenderer origin, SkinnedMeshRenderer target, Transform skeletonRoot)
    3.         {
    4.             Dictionary<string, Transform> allBones = new Dictionary<string, Transform>(); // you can just cache this (and consequently the foreach below) or pass through parameter if in a static context. Leaving here for simplicity
    5.             var childrenBones = skeletonRoot.GetComponentsInChildren<Transform>();
    6.             foreach(Transform b in childrenBones)
    7.             {
    8.                 allBones.Add(b.name, b);
    9.             }
    10.             var originBones = origin.bones;
    11.             var targetBones = new List<Transform>();
    12.             foreach(Transform b in originBones)
    13.             {
    14.                 if(allBones.TryGetValue(b.name, out var foundBone))
    15.                 {
    16.                     targetBones.Add(foundBone);
    17.                 }
    18.             }
    19.             target.bones = targetBones.ToArray();
    20.         }
    21.  
    That way we can tell the skinned mesh renderer which bones should be used by the new Mesh. Btw: In this case you will need the reference for the skinned mesh renderer that carries the mesh that you are swapping to, it is there the info we need: the name of the joints we need to find instantiated. I don't know about swapping by a mesh that was binded with different bone names, maybe that would required a map telling which bone name is which in the new hierarchy structure.

    Thanks!
     
    marrl4killer likes this.
  19. StoneHunterEvans

    StoneHunterEvans

    Joined:
    Oct 31, 2020
    Posts:
    1
    Well done. One suggestion though: could you make this into a Unity package so that I can see it in action? I am confused by the code.
     
    Last edited: Jan 11, 2021
unityunity