Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
using System;
using System.Collections.Generic;
using UnityEngine;
namespace AiGame
{
// Design is that you have a target with a SkinnedMeshRenderer and bone hierarchy.
// You provide a list of input SkinnedMeshRenderer's that use the same bone hierarchy as the target
// Bones and bone weights will be mapped to the target.
[Serializable]
public class AvatarMeshCombiner
{
public SkinnedMeshRenderer Target;
public Transform TargetRig;
public List<SkinnedMeshRenderer> Inputs = new List<SkinnedMeshRenderer>();
private Dictionary<string, int> BoneIndexMap = new Dictionary<string, int>();
private List<Transform> BonesList = new List<Transform>();
private List<BoneWeight> BoneWeights = new List<BoneWeight>();
private List<CombineInstance> CombineInstances = new List<CombineInstance>();
private List<Matrix4x4> BindPoses = new List<Matrix4x4>();
public void Build()
{
BoneIndexMap.Clear();
BonesList.Clear();
BoneWeights.Clear();
CombineInstances.Clear();
BindPoses.Clear();
BuildBones();
for (int i = 0; i < Inputs.Count; i++)
{
SkinnedMeshRenderer smr = Inputs[i];
if (!smr.gameObject.activeSelf)
{
continue;
}
Transform[] meshBones = smr.bones;
BoneWeight[] meshBoneweight = smr.sharedMesh.boneWeights;
for (int j = 0; j < meshBoneweight.Length; ++j)
{
BoneWeight bWeight = meshBoneweight[j];
bWeight.boneIndex0 = GetBoneIndex(meshBones[bWeight.boneIndex0].name);
bWeight.boneIndex1 = GetBoneIndex(meshBones[bWeight.boneIndex1].name);
bWeight.boneIndex2 = GetBoneIndex(meshBones[bWeight.boneIndex2].name);
bWeight.boneIndex3 = GetBoneIndex(meshBones[bWeight.boneIndex3].name);
BoneWeights.Add(bWeight);
}
for(int j=0;j<smr.sharedMesh.subMeshCount;j++)
{
CombineInstance ci = new CombineInstance();
ci.mesh = smr.sharedMesh;
ci.subMeshIndex = j;
ci.transform = smr.transform.localToWorldMatrix;
CombineInstances.Add(ci);
}
}
foreach(var bone in BonesList)
{
BindPoses.Add(bone.worldToLocalMatrix * Target.transform.worldToLocalMatrix);
}
Target.sharedMesh = new Mesh();
Target.sharedMesh.CombineMeshes(CombineInstances.ToArray(), true, true);
//Debug.LogFormat("boneweights {0} vertices {1} bones {2}", boneWeights.Count, Target.sharedMesh.vertices.Length, BonesList.Count);
Target.bones = BonesList.ToArray();
Target.sharedMesh.boneWeights = BoneWeights.ToArray();
Target.sharedMesh.bindposes = BindPoses.ToArray();
Target.sharedMesh.RecalculateBounds();
}
// Create a unique bone list and a map of bone names to their index. Using the target rig.
// Then for the combined mesh we remap the boneweight bone indexes to the target bones.
private void BuildBones()
{
var targetBones = TargetRig.GetComponentsInChildren<Transform>();
Dictionary<string, Transform> targetMap = new Dictionary<string, Transform>();
foreach (var targetBone in targetBones)
{
targetMap[targetBone.name] = targetBone;
}
int boneIndex = 0;
foreach (var smr in Inputs)
{
var bones = smr.bones;
foreach (var bone in bones)
{
if (!BoneIndexMap.ContainsKey(bone.name))
{
Transform targetBone = targetMap[bone.name];
BonesList.Add(targetBone);
BoneIndexMap[bone.name] = boneIndex;
boneIndex++;
}
}
}
}
private int GetBoneIndex(string bone)
{
if (BoneIndexMap.TryGetValue(bone, out int index))
{
return index;
}
else
{
return 0;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment