// Creates a character based on the currentConfiguration recycling a
// character base, this way the position and animation of the character
// are not changed.
// 这个函数实际上并没有将各部分的子网格合并成一张网,而只是将他们合并到
// 同一个Mesh下作为sub mesh。因为一张网格只能用一个材质,只有所有子网格
// 都共享同一个材质时,合并成一张网才能保证材质应用正确。
public GameObject Generate(GameObject root)
{
// The SkinnedMeshRenderers that will make up a character will be
// combined into one SkinnedMeshRenderers using multiple materials.
// This will speed up rendering the resulting character.
List<CombineInstance> combineInstances = new List<CombineInstance>();
List<Material> materials = new List<Material>();
List<Transform> bones = new List<Transform>();
//一次处理构成身体的各部分
foreach (CharacterElement element in currentConfiguration.Values)
{
//GetSkinnedMeshRenderer()内部Instantiat了一个由该部分肢体Assets构成的
//GameObject,并返回Unity自动为其创建SinkedMeshRender。
SkinnedMeshRenderer smr = element.GetSkinnedMeshRenderer();
//注意smr.materials中包含的材质数量和顺序与下面的sub mesh是对应的
materials.AddRange(smr.materials);
for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++)
{
CombineInstance ci = new CombineInstance();
ci.mesh = smr.sharedMesh;
ci.subMeshIndex = sub;
combineInstances.Add(ci);
}
// As the SkinnedMeshRenders are stored in assetbundles that do not
// contain their bones (those are stored in the characterbase assetbundles)
// we need to collect references to the bones we are using
// 网格点与骨骼的对应关系是通过Mesh数据结构中的BoneWeight数组来实现的。该数组
// 与网格顶点数组对应,记录了每个网格点受骨骼(骨骼记录在SinkedMeshRender的bones
// 数组中,按下标索引)影响的权重。
// 而此处,示例程序提供的肢体Assets并不包含骨骼,而是返回骨骼名称。因此,推断
// GetBoneNames()返回的骨骼名称应该与实际骨骼数组的顺序相同。
foreach (string bone in element.GetBoneNames())
{
foreach (Transform transform in transforms)
{
//通过名字找到实际的骨骼
if (transform.name != bone) continue;
bones.Add(transform);
break;
}
}
Object.Destroy(smr.gameObject);
}
// Obtain and configure the SkinnedMeshRenderer attached to
// the character base.
// 至此,combineInstances、bones和materials三个数组中的数据对应关系是正确的。
// 合并时,第二个参数是fals,表示保持子网格不变,只不过将它们统一到一个Mesh里
// 来管理,这样只需采用一个SkinedMeshRender绘制,效率较高。
SkinnedMeshRenderer r = root.GetComponent<SkinnedMeshRenderer>();
r.sharedMesh = new Mesh();
r.sharedMesh.CombineMeshes(combineInstances.ToArray(), false, false);
r.bones = bones.ToArray();
r.materials = materials.ToArray();