之前的文章:
- 基础换装
- Mesh合并
效果演示↓
普通换装↓
通过Mesh合并后的换装↓
完成的换装包含以下几步:
- 合并Mesh
- 刷新骨骼
- 替换材质
- 合并贴图
- 刷新UV
下面是每一步的详细介绍
一、合并Mesh
通过Unity中的CombineInstance
对所有需要合并的SkinnedMeshRenderer合并
List<CombineInstance> combineInstancesList = new List<CombineInstance>();
for (int i = 0; i < skinnedMeshRenderers.Length; i++){
CombineInstance combineInstance = new CombineInstance();
combineInstance.mesh = skinnedMeshRenderer.sharedMesh;
combineInstance.transform = skinnedMeshRenderer.localToWorldMatrix;
combineInstancesList.Add(combineInstance);
}
Mesh newMesh = new Mesh();
newMesh.CombineMeshes(combineInstancesList.ToArray(), true, true);
二、刷新骨骼
1. bones
通过各个部件收集骨骼,然后赋值个大的合并后的Mesh,在通过原有的骨骼列表依次刷新骨骼
// 收集各个部件的骨骼
List<Transform> bones = new List<Transform>();
for (int i = 0; i < skinnedMeshRenderers.Length; i++)
{
for (int j = 0; j < skinnedMeshRenderer.bones.Length; j++)
{
bones.Add(skinnedMeshRenderer.bones[j]);
}
}
combinedSkinnedMeshRenderer.bones = bones.ToArray();
// 正确刷新骨骼
List<Transform> newBones = new List<Transform>();
for (int i = 0; i < combinedSkinnedMeshRenderer.bones.Length; i++)
{
string name = combinedSkinnedMeshRenderer.bones[i].name;
if (bonesDic.ContainsKey(name))
{
newBones.Add(bonesDic[name]);
}
}
combinedSkinnedMeshRenderer.bones = newBones.ToArray();
2. boneWeights
刷新骨骼权重,根据原有Mesh的权重+合并后的Mesh偏移
List<BoneWeight> boneWeightsList = new List<BoneWeight>();
int boneOffset = 0;
for (int i = 0; i < skinnedMeshRenderers.Length; i++)
{
SkinnedMeshRenderer skinnedMeshRenderer = skinnedMeshRenderers[i];
skinnedMeshRenderer.gameObject.SetActive(false);
CombineInstance combineInstance = new CombineInstance();
combineInstance.mesh = skinnedMeshRenderer.sharedMesh;
combineInstance.transform = skinnedMeshRenderer.localToWorldMatrix;
combineInstancesList.Add(combineInstance);
// Bone
BoneWeight[] boneWeightsArr = skinnedMeshRenderer.sharedMesh.boneWeights;
for (int j = 0; j < skinnedMeshRenderer.bones.Length; j++)
{
bones.Add(skinnedMeshRenderer.bones[j]);
}
for (int j = 0; j < boneWeightsArr.Length; j++)
{
BoneWeight bw = boneWeightsArr[j];
bw.boneIndex0 += boneOffset;
bw.boneIndex1 += boneOffset;
bw.boneIndex2 += boneOffset;
bw.boneIndex3 += boneOffset;
boneWeightsList.Add(bw);
}
boneOffset += skinnedMeshRenderer.bones.Length;
}
combinedSkinnedMeshRenderer.sharedMesh.boneWeights = boneWeightsList.ToArray();
3. bindposes
T-pose下的顶点位置,用矩阵转换
// bones为收集各个部件后的列表
for(int i = 0; i < bones.Count; i++)
{
bindPosesList.Add(bones[i].worldToLocalMatrix * transform.localToWorldMatrix);
}
combinedSkinnedMeshRenderer.sharedMesh.bindposes = bindPosesList.ToArray();
三、替换材质
将合并后的SkinnedMeshRenderer的material替换
四、合并贴图
合并后的贴图↓
扫描二维码关注公众号,回复:
14669074 查看本文章
PS:如果材中有多个贴图,则分别要按照下面步骤进行合并
1. 收集贴图
在合并Mesh之前,通过每个SkinnedMeshRenderer的material获取
用List<Texture> textureList
存储
2. pack
atlasTexture 就是合并后的图集
Texture2D atlasTexture = new Texture2D(width, height);
Rect[] packRet = atlasTexture.PackTextures(textureList.ToArray(), padding=0f)
3. 修改材质贴图
直接使用Material.SetTexture(name, texture)
修改贴图
五、刷新UV
packRet
中存储了每个原贴图在大的图集中位置信息,需要将每个部件的顶点的uv进行重新计算
// uvCount 是遍历所有部件的定点时候累加得到 smr.sharedMesh.uvs.Length 求和
// uvList(List<Vector2[]>) 是存储了每个部件的顶点的列表
Vector2[] atlasUVs = new Vector2[uvCount]; // 大Mesh的UV列表
int counter = 0;
for (int index = 0; index < uvList.Count; ++index)
{
foreach (Vector2 uv in uvList[index])
{
atlasUVs[counter].x = Mathf.Lerp(packingResults[i][index].xMin, packingResults[i][index].xMax, uv.x);
atlasUVs[counter].y = Mathf.Lerp(packingResults[i][index].yMin, packingResults[i][index].yMax, uv.y);
counter++;
}
}
combinedSkinnedMeshRenderer.sharedMesh.uv = atlasUVs;