Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Skinned mesh replacement

Discussion in 'Scripting' started by DonLoz, Jun 13, 2008.

  1. DonLoz

    DonLoz

    Joined:
    Jun 10, 2008
    Posts:
    6
    Hi,
    Sorry if this has been covered before - I couldn't find anything on it specifically - but basically I need to replace a mesh (in MeshFilter) that my prefab skinned character uses to another currently loaded with Resources.Load.
    I'd have thought it should be very simple to do, but for some reason it doesn't update the mesh.

    The code snippet is:

    Code (csharp):
    1.     var SwapMesh = Resources.Load("Torso/S3", Mesh);
    2.     var Swapper : MeshFilter = CharTorso.gameObject.GetComponent(MeshFilter);
    3.     Swapper.mesh = SwapMesh;
    4.  
    It does update the MeshFilter reference in the inspector, but doesn't update the drawn mesh.

    Please show me where i've been stupid!

    thanks!
     
  2. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    For skinned meshes, you have to use SkinnedMeshRenderer.sharedMesh

    Skinned mesh renderer does not actually use or need the MeshFilter, it can be removed.
     
    Magiichan likes this.
  3. DonLoz

    DonLoz

    Joined:
    Jun 10, 2008
    Posts:
    6
    Thanks for that.

    Ok, i've tried using SkinnedMeshRenderer.sharedMesh, but the skinning doesn't seem to be working - getting strange vertex placement (as if my character has exploded).
    Is there anything in particular I need to do? I've done something along the lines of:
    SwapMesh = Resources.Load("Torso/S3", Mesh);
    var Swapper : SkinnedMeshRenderer = CharTorso.GetComponent(SkinnedMeshRenderer);
    Swapper.sharedMesh = SwapMesh;

    I've tried combinations of how the components are referred to, but not getting any success.

    thanks..
     
  4. DonLoz

    DonLoz

    Joined:
    Jun 10, 2008
    Posts:
    6
    Any further help on this?

    Its part of an evaluation that i'm conducting on whether we can adopt Unity completely. Unfortunately, without a few key features such as skinned mesh replacement and morph target support (we can work around that after seeing some people's approaches to this elsewhere), I can't see how we can sanction moving over to it.
    Essentially I need to convert our character generation system to be driven via Unity, without having to go via plugins. Replacement of skinned meshes, materials and handling of mesh morphs are vital, and all preferably via resource loading as opposed to enabling/disabling loaded 'skins' (for memory reasons).

    Its from a feasibility point of view - I need to know and prove that it can be done before code time can be given to it.

    Damn shame as i've loved everything about it so far!

    thanks..
     
    ChosenOne_ likes this.
  5. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Are the meshes you're trying to swap made for the same skeleton? Right now what you're swapping is just the mesh, and the skeleton remains the same. It should work as long as both meshes used the same (or very similar) skeletons (same bone hierarchy, same names, etc.).
     
  6. DonLoz

    DonLoz

    Joined:
    Jun 10, 2008
    Posts:
    6
    Yes, skeletons are identical for both meshes and they are all skinned/rigged to the same one. Only differences are vertex count assigned materials.

    I've tried having all of the torsoes in the same FBX and simply disable the draw for those I don't want to show - so I know the skeleton setup works fine - but I can't have so many in memory at any one time which is why I need to go the way of resource loading.
     
  7. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Hmm, I can't reproduce this. I just tried a badly skinned and badly animated cylinder: web player link, and it works just fine. I am just switching which mesh is used on the SkinnedMeshRenderer.

    Can you file a bug and attach a project that shows where it does not work for you?
     
  8. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    I digged up this old post and I have exactly the same problem, replaced skinned mesh looks exploded.
    The exported meshes are skinned off of the same skeleton.
    I'm pulling my hair for several days for this problem.

    Here is the function I'm using,

    Code (csharp):
    1.     void SetSkinnedMesh(GameObject destGO, string destMesh, string srcFilename, string srcMatFilename)
    2.     {
    3.         GameObject srcGO = (GameObject)Instantiate(Resources.Load(srcFilename));
    4.         SkinnedMeshRenderer destSMR = FindSkinMeshRender(destGO, destMesh);
    5.         SkinnedMeshRenderer srcSMR = (SkinnedMeshRenderer)srcGO.GetComponentInChildren(typeof (SkinnedMeshRenderer));
    6.  
    7.         destSMR.sharedMesh = srcSMR.sharedMesh;
    8.         DestroyObject(srcGO);
    9.     }
    10.  
    My questions are,
    1. What's wrong with the code?
    2. I'm loading it(fbx file) as GameObject and it looks like I must instantiate(and later destroy it) in order to access SkinnedMeshRenderer, however, DonLoz is just importing the skin without instantiating nor destroying it. How is this possible?
    3. I looked at the weblink for sample. Can you please share the src for reference? Better yet, community will really appreciate a short tutorial on customizing character with skin replacement(for cloths) and mesh attachement(weapons)

    Thank you very much and I'm really hoping to hear the answer soon.
     
  9. BearishSun

    BearishSun

    Joined:
    Dec 1, 2008
    Posts:
    175
    First of, your problems are very likely related to your modeling tool/exporter. I don't think it's a problem with unity because I've been switching shared meshes successfully.

    Why would you need to instantiate the mesh? Simply use Resource.Load("Meshes/Model/Mesh", Mesh). 'Model' being the GameObject.
     
  10. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    Thanks BearishSun, I got it loaded as Mesh without instanciating. ^^ But mesh still looks incorrect when copied to existing skinned mesh. But from the editor's "Project" tab, it looks correct. It gets screwed up when I copy it to existing skinned mesh.
    It looks like vertex or triangle index is getting screwed up when copied.

    My artist exports skinned mesh with bones, thus, the FBX file has mesh and bone data. According to him, if he just exports skinned mesh data without bone data at the same time, it won't export as skinned mesh type but just plain mesh type. Is this correct?

    What's the correct way of exporting skinned mesh?

    My artist is building skinned mesh on a single skeleton and selecting one mesh at a time when exporting it.
    And the version he uses is Max8.

    Thanks.
     
  11. BearishSun

    BearishSun

    Joined:
    Dec 1, 2008
    Posts:
    175
    Which FBX exporter is your artist using?

    I think I recall we had some problems with the 2008 version, and we're using the 2006 version. I gotta check with our modelers tho, not sure.
     
  12. K-A277

    K-A277

    Joined:
    Mar 6, 2012
    Posts:
    8
    After spent several days for this issue, I found what needs to be done to replace skinned mesh without exploded vertices :) (my mesh looked exploded after replacement before I fix boneWeights). If you have similar problem, below explanation may help you some.

    I had to let a player change her avatar’s costume(whole look) in my project.
    Conditions for destSMR and srcSMR in my case:
    * destSMR: skinned mesh renderer of player avatar GameObject.
    * srcSMR: skinned mesh renderer of new costume GameObject
    * both should use the same number of bones and structure and identical bone naming.
    (meaning, eventually srcSMR would use player avatar’s bones for animation)

    With such conditions, I made new Mesh using info from srcSMR.sharedMesh and assign the new mesh to destSMR.sharedMesh.

    About making the new Mesh,
    I simply assign vertices, uv, normals, triangles, bindposes from srcSMR to the new Mesh [ like, newMesh.uv = srcSMR.sharedMesh.uv;] except boneWeights.

    About boneWeights,
    Unfortunately, my destSMR and srcSMR were using different index numbering. i.e, Head bone is 1 for destSMR and 5 for srcSMR. You can check the differences of your smr’s boneWeights easily by printing them on console window.
    for example,
    for (int i=0; i < srcSMR.sharedMesh.boneWeights.Length; i++)
    {
    Debug.Log(destSMR.sharedMesh.boneWeights.boneIndex0 + “ : “ + srcSMR.sharedMesh.boneWeights.boneIndex0);
    }

    In such case, vertices of the srcSMR’s mesh will be influence by wrong bones.
    So, have to find correct index using bone name indirectly. I set up hash table from destSMR.bones with name as the key and index as the value. You may find recalculating index example in http://www.unifycommunity.com/wiki/index.php?title=CombineSkinnedMeshes.

    I recalculated index for all boneIndex0, boneIndex1, boneIndex2, boneIndex3.
    and did simple copy for weight0 ~ weight3.
     
    Last edited: Mar 22, 2012
  13. TimGS

    TimGS

    Joined:
    Apr 24, 2014
    Posts:
    70
    Sorry for bumping this old thread but I can't make it work.

    Here's my script:
    Code (csharp):
    1. public void ChangeArmorMesh(SkinnedMeshRenderer newSkin)
    2. {
    3.     Debug.Log(newSkin.bones[newSkin.sharedMesh.boneWeights[0].boneIndex0].name + " => " + newSkin.sharedMesh.boneWeights[0].weight0);
    4.  
    5.     // Creating new mesh for armor skin
    6.     Mesh tempMesh = new Mesh();
    7.     tempMesh.Clear();
    8.     tempMesh.vertices = newSkin.sharedMesh.vertices;
    9.     tempMesh.uv = newSkin.sharedMesh.uv;
    10.     tempMesh.triangles = newSkin.sharedMesh.triangles;
    11.     tempMesh.RecalculateBounds();
    12.     tempMesh.bindposes = newSkin.sharedMesh.bindposes;
    13.     tempMesh.normals = newSkin.sharedMesh.normals;
    14.     tempMesh.tangents = newSkin.sharedMesh.tangents;
    15.  
    16.     // Creating hashtable for character skin bones' names and their indices
    17.     Hashtable bones = new Hashtable();
    18.     for(int i = 0; i < skin.bones.Length; i++)
    19.     {
    20.         bones.Add(skin.bones[i].name, i);
    21.     }
    22.  
    23.     // Recalculating bone indices for bone weights and making new bone weights array
    24.     List<BoneWeight> bws = new List<BoneWeight>();
    25.     foreach(var bw in newSkin.sharedMesh.boneWeights)
    26.     {
    27.         bws.Add(recalculateIndexes(bw,bones,newSkin.bones));
    28.     }
    29.  
    30.     tempMesh.boneWeights = bws.ToArray();
    31.  
    32.     skin.sharedMesh = tempMesh;
    33.  
    34.     Debug.Log(skin.bones[skin.sharedMesh.boneWeights[0].boneIndex0].name + " => " + skin.sharedMesh.boneWeights[0].weight0);
    35. }
    36.  
    37. BoneWeight recalculateIndexes(BoneWeight bw, Hashtable boneHash, Transform[] meshBones)
    38. {
    39.     BoneWeight retBw = bw;
    40.     retBw.boneIndex0 = (int)boneHash[meshBones[bw.boneIndex0].name];
    41.     retBw.boneIndex1 = (int)boneHash[meshBones[bw.boneIndex1].name];
    42.     retBw.boneIndex2 = (int)boneHash[meshBones[bw.boneIndex2].name];
    43.     retBw.boneIndex3 = (int)boneHash[meshBones[bw.boneIndex3].name];
    44.     return retBw;
    45. }
    The output is:
    Code (csharp):
    1. Spine2 => 0.4772818
    2. Spine2 => 0.4772818
    So everything is recalculated correctly.
    But I get error "Bone influences do not match bones" and my mesh is messed up.
     
  14. argc_argv

    argc_argv

    Joined:
    Dec 14, 2013
    Posts:
    35
    anyone else trying to figure out how to do this?
     
  15. K-A277

    K-A277

    Joined:
    Mar 6, 2012
    Posts:
    8
    In case someone still having the same issue, I uploaded the code that I used at that time. It's an old file, but probably it 's not a bad reference for someone though.

    Note: In the file, bone index and weight remapping is commented out since later artists made index&weight matching skinned mesh so I did not have to perform remapping. I guess we did it to make switching SMR faster.
     

    Attached Files:

  16. guillermogrw_unity

    guillermogrw_unity

    Joined:
    Oct 8, 2020
    Posts:
    1
    Are you saying the index and weights were transferred over to another mesh with out it needing to have the exact bones? I currently have two meshes with the same vertex count but one is part of the armature and the other is not. I want to transfer the weights to the mesh without them to theoretically be able to swap them.
     
  17. K-A277

    K-A277

    Joined:
    Mar 6, 2012
    Posts:
    8
    no i'm saying that srcSMR's bone name to bone index list should be the same as destSMR's, and i had to remap those indices in BoneWeight list of srcSMR because bone name to index infos were different than destSMR's, but later i didn't have to do the remapping at runtime since srcSMRs with the same info as destSMR were provided.
    (ex)
    srcSMR.bones[5].name : pelvis, desSMR.bones[5].name : neck <--- need to remap runtime (index 5 is pointing different bone)

    new_srcSMR.bones[5].name : neck, desSMR.bones[5].name : neck <--- if index to name for all bones are matching like this, no need to remap.


    if you are just updating bone weights of vertices no matter which bones will influence, why not. you might want to match vertex indices though.