Transfer the rig of a Skinned Mesh Renderer to a new SMR with code?

Hey guys,

I have a character that has hair (Skinned Mesh Renderer) and I am currently trying to use the same hair on the same character, the only difference is that this time it’s optimized and exported alone as hair.fbx (the first time it came with the character).

I am trying to make the new hair behave as the old one, which means to follow properly the animations, so I came to the conclusion that I have to copy the rig of the old hair’s SMR and paste it to the new one. Anyone has an idea how to do that in code?

Okay, I found out about SkinnedMeshRenderer.bones, I am still not sure how to exactly copy the bones from the old hair to the new hair model.

As far as I know both of them are rigged to follow the main body and the difference comes, because the second one is exported separately from the character.

Does anyone has a suggestion or has done something similar? I guess, it’s the same with changeable clothes.

1 Like

I finally found a solution, actually two solutions and you can try both of them, but the first one should work nicely, it’s just shorter, so you decide if you are going to use CopyBones() or CopyBonesWithDictionary().

  1. Your exported hair / clothes / whatever must have a Skinned Mesh Renderer (you must weight it to the mesh, in Daz that happens automatically if you use models from the store that come with rigs)
  2. Attach this script to your hair / clothes and assign the component, which is your character’s Skinned Mesh Renderer (if you are exporting from Daz it’s named something like Genesis3Female.Shape / Genesis3Male.Shape.

And here is the script itself

using System.Collections;
using System.Collections.Generic;
using Devdog.General.ThirdParty.UniLinq;
using UnityEngine;

public class BonesDuplicator : MonoBehaviour
{

    void Start()
    {
        CopyBones();
    }

    // Your character's Shape (containing Skinned Mesh Renderer)
    [SerializeField] private GameObject _sourceHumanBody;

    private void CopyBones()
    {
        var sourceRenderer = _sourceHumanBody.GetComponent<SkinnedMeshRenderer>();
        var targerRenderer = GetComponent<SkinnedMeshRenderer>();

        targerRenderer.bones = sourceRenderer.bones.Where(b => targerRenderer.bones.Any(t => t.name.Equals(b.name)))
            .ToArray();
    }


    private void CopyBonesWithDictionary()
    {
        SkinnedMeshRenderer targetRenderer = _sourceHumanBody.GetComponent<SkinnedMeshRenderer>();

        Dictionary<string, Transform> boneMap = new Dictionary<string, Transform>();
        foreach (Transform bone in targetRenderer.bones)
        {
            boneMap[bone.name] = bone;
        }

        SkinnedMeshRenderer thisRenderer = GetComponent<SkinnedMeshRenderer>();
        Transform[] boneArray = thisRenderer.bones;
        for (int idx = 0; idx < boneArray.Length; ++idx)
        {
            string boneName = boneArray[idx].name;
            if (false == boneMap.TryGetValue(boneName, out boneArray[idx]))
            {
                Debug.LogError("failed to get bone: " + boneName);
                Debug.Break();
            }
        }
        thisRenderer.bones = boneArray;
    }
}

I hope that will help you!

5 Likes