最近几天研究了一下换装系统的实现 主要实现了mesh的合并,贴图的合并处理 ,meshUV信息的重新计算等
先上效果图
模型要经过预处理功能将模型的Mesh与Texture贴图从模型中分离处理,以便可以方便的进行合并
实现了一个预处理的工具类可以将资源导出与优化模型
工具用于实现模型优化将模型的那些特效挂节点暴露出来 最后制作成预制体
主要的逻辑代码如下
void ChangeRole(string[] Parts)
{
list.Clear();
oldUV = new List<Vector2[]>();
Renderer = transform.GetComponent<SkinnedMeshRenderer>();
for (int i = 0; i < Parts.Length; i++)
{
//加载Mesh
Mesh mesh = Resources.Load<Mesh>("Equipments/" + Parts[i]);
Meshes[i] = mesh;
//加载贴图
Texture2D texture2D = Resources.Load<Texture2D>("Equipments/" + Parts[i]);
list.Add(texture2D);
CombineInstance instance = new CombineInstance();
instance.mesh = mesh;
CombineInstances[i] = instance;
}
if (Renderer.sharedMesh != null)
{
Renderer.sharedMesh.Clear();
}
//合并贴图文件减少DrawCall
var newtexture2D = new Texture2D(1024, 1024, TextureFormat.RGBA32, true);
Rect[] uvs = newtexture2D.PackTextures(list.ToArray(), 0);
Vector2[] uva, uvb;
//根据新合并的贴图文件修改Mesh的UV坐标 使之确保采样正确性
for (int i = 0; i < CombineInstances.Length; i++)
{
uva = CombineInstances[i].mesh.uv;
// 0.0 0.9 (x:0.00, y:0.50, width:0.25, height:0.50)
uvb = new Vector2[uva.Length];
for (int j = 0; j < uva.Length; j++)
{
uvb[j] = new Vector2((uva[j].x * uvs[i].width) + uvs[i].x, (uva[j].y * uvs[i].height) + uvs[i].y);
}
oldUV.Add(uva);
CombineInstances[i].mesh.uv = uvb;
}
var tempMesh = new Mesh();
Renderer.enabled = false;
tempMesh.CombineMeshes(CombineInstances,true,false);
Renderer.sharedMesh = tempMesh;
Renderer.enabled = true;
Material material = Renderer.material;
material.mainTexture = newtexture2D;
//重置Mesh的UV信息
for (int i = 0; i < CombineInstances.Length; i++)
{
CombineInstances[i].mesh.uv = oldUV[i];
}
}
部分编辑器代码
[MenuItem("FbxTools/保存Mash&Texture2")]
public static void SaveMesh()
{
Object[] fbxs = Selection.GetFiltered<Object>(SelectionMode.DeepAssets);
if (fbxs != null)
{
for (int i = 0; i < fbxs.Length; i++)
{
GameObject fbx = fbxs[i] as GameObject;
string path = "";
if (fbx != null)
{
path = AssetDatabase.GetAssetPath(fbx);
ModelImporter modelImporter = AssetImporter.GetAtPath(path) as ModelImporter;
if (modelImporter != null)
{
string saveRootPath = "Assets/Resources/Equipments/";
if (!Directory.Exists(saveRootPath))
{
Directory.CreateDirectory(saveRootPath);
}
modelImporter.isReadable = true;
GameObject go = Object.Instantiate(fbx);
SkinnedMeshRenderer[] smrs = go.GetComponentsInChildren<SkinnedMeshRenderer>();
for (int j = 0; j < smrs.Length; j++)
{
//创建Mesh
Mesh mesh = Object.Instantiate(smrs[j].sharedMesh);
mesh.name = smrs[j].sharedMesh.name;
SaveMeshAsset(mesh,smrs[j].sharedMaterial.mainTexture as Texture2D,saveRootPath );
}
modelImporter.isReadable = false;
Object.DestroyImmediate(go);
}
}
}
}
}
public static void CleanMesh(Mesh mesh)
{
mesh.uv2 = null;
mesh.uv3 = null;
mesh.uv4 = null;
mesh.colors = null;
mesh.colors32 = null;
mesh.tangents = null;
}
public static void SaveMeshAsset(Mesh mesh, Texture2D tex, string path)
{
CleanMesh(mesh);
string meshPath = path + mesh.name + ".asset";
AssetDatabase.CreateAsset(mesh,meshPath);
if (tex != null)
{
string srcPath = AssetDatabase.GetAssetPath(tex);
string desPath = "Assets/Resources/Equipments/" + tex.name + ".tga";
AssetDatabase.CopyAsset(srcPath, desPath);
}
AssetDatabase.SaveAssets();
}
[MenuItem("FbxTools/优化Fbx")]
private static void OptmizeFbx()
{
Rect rect = new Rect(0,0,600,800);
SelectFbxEditor window =
(SelectFbxEditor) EditorWindow.GetWindowWithRect(typeof(SelectFbxEditor), rect, true, "Fbx面板");
window.Init();
window.Show();
}
如有疑问请评论 或联系本人 邮箱tongwuwei@163.com