273 lines
11 KiB
C#
273 lines
11 KiB
C#
/*
|
||
********************************************************************************
|
||
*Copyright(C),coolae.net
|
||
*Author: chenbin
|
||
*Version: 2.0
|
||
*Date: 2017-01-09
|
||
*Description: 合并mesh,达到合并drawcall目的
|
||
*Others:
|
||
*History:
|
||
*********************************************************************************
|
||
*/
|
||
|
||
using UnityEngine;
|
||
using System.Collections;
|
||
|
||
#if UNITY_EDITOR
|
||
using UnityEditor;
|
||
#endif
|
||
/*
|
||
Attach this script as a parent to some game objects. The script will then combine the meshes at startup.
|
||
This is useful as a performance optimization since it is faster to render one big mesh than many small meshes. See the docs on graphics performance optimization for more info.
|
||
Different materials will cause multiple meshes to be created, thus it is useful to share as many textures/material as you can.
|
||
*/
|
||
namespace Coolape
|
||
{
|
||
[AddComponentMenu("Mesh/Combine Children")]
|
||
public class CombineChildren : MonoBehaviour
|
||
{
|
||
|
||
/// Usually rendering with triangle strips is faster.
|
||
/// However when combining objects with very low triangle counts, it can be faster to use triangles.
|
||
/// Best is to try out which value is faster in practice.
|
||
public bool generateTriangleStrips = false;
|
||
public bool applyOnstart = false;
|
||
public bool iscombined = false;
|
||
public UnityEngine.Rendering.ShadowCastingMode MeshRenderCastShadow = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||
public bool isMeshRenderReciveShadow = false;
|
||
public bool useSharedMesh = true;
|
||
public bool meshKeyWithName = true;
|
||
public static Hashtable __meshMap = new Hashtable();
|
||
Transform tr;
|
||
|
||
public Transform transform
|
||
{
|
||
get
|
||
{
|
||
if (tr == null)
|
||
{
|
||
tr = gameObject.transform;
|
||
}
|
||
return tr;
|
||
}
|
||
}
|
||
|
||
|
||
/// This option has a far longer preprocessing time at startup but leads to better runtime performance.
|
||
void Start()
|
||
{
|
||
if (applyOnstart)
|
||
{
|
||
combineChildren();
|
||
}
|
||
}
|
||
|
||
Component[] filters = null;
|
||
//合并
|
||
public void combineChildren(bool onlyCreateMesh = false)
|
||
{
|
||
if (iscombined)
|
||
return;
|
||
if (!onlyCreateMesh)
|
||
{
|
||
iscombined = true;
|
||
}
|
||
filters = GetComponentsInChildren(typeof(MeshFilter));
|
||
Matrix4x4 myTransform = transform.worldToLocalMatrix;
|
||
Hashtable materialToMesh = new Hashtable();
|
||
|
||
Hashtable materialMap = new Hashtable();
|
||
|
||
for (int i = 0; i < filters.Length; i++)
|
||
{
|
||
MeshFilter filter = (MeshFilter)filters[i];
|
||
Renderer curRenderer = filters[i].GetComponent<Renderer>();
|
||
MeshCombineUtility.MeshInstance instance = new MeshCombineUtility.MeshInstance();
|
||
instance.mesh = filter.sharedMesh;
|
||
|
||
if (curRenderer != null && curRenderer.gameObject.activeInHierarchy
|
||
&& curRenderer.gameObject.activeSelf
|
||
&& curRenderer.enabled && instance.mesh != null)
|
||
{
|
||
instance.transform = myTransform * filter.transform.localToWorldMatrix;
|
||
|
||
Material[] materials = curRenderer.sharedMaterials;
|
||
for (int m = 0; m < materials.Length; m++)
|
||
{
|
||
if (materials[m] == null)
|
||
continue;
|
||
instance.subMeshIndex = System.Math.Min(m, instance.mesh.subMeshCount - 1);
|
||
|
||
// ArrayList objects = (ArrayList)materialToMesh[materials[m]];
|
||
// ArrayList objects = (ArrayList)(materialMap[materials[m].mainTexture.GetInstanceID()]);
|
||
ArrayList objects = (ArrayList)(materialMap[materials[m].name]);
|
||
if (objects != null)
|
||
{
|
||
objects.Add(instance);
|
||
}
|
||
else
|
||
{
|
||
objects = new ArrayList();
|
||
objects.Add(instance);
|
||
// materialMap[materials[m].mainTexture.GetInstanceID()] = objects;
|
||
materialMap[materials[m].name] = objects;
|
||
materialToMesh.Add(materials[m], objects);
|
||
}
|
||
}
|
||
if (!onlyCreateMesh)
|
||
{
|
||
curRenderer.enabled = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
foreach (DictionaryEntry de in materialToMesh)
|
||
{
|
||
ArrayList elements = (ArrayList)de.Value;
|
||
MeshCombineUtility.MeshInstance[] instances = (MeshCombineUtility.MeshInstance[])elements.ToArray(typeof(MeshCombineUtility.MeshInstance));
|
||
// We have a maximum of one material, so just attach the mesh to our own game object
|
||
if (materialToMesh.Count == 1)
|
||
{
|
||
string meshKey = de.Key.GetHashCode().ToString();
|
||
if (meshKeyWithName) {
|
||
meshKey += "_" + gameObject.name;
|
||
}
|
||
Mesh mesh = __meshMap[meshKey] as Mesh;
|
||
if (mesh == null || !useSharedMesh)
|
||
{
|
||
mesh = MeshCombineUtility.Combine(instances, generateTriangleStrips);
|
||
__meshMap[meshKey] = mesh;
|
||
}
|
||
|
||
if (onlyCreateMesh)
|
||
{
|
||
saveMesh(gameObject.name, mesh);
|
||
}
|
||
else
|
||
{
|
||
// mesh = setuv2(mesh);
|
||
// Make sure we have a mesh filter & renderer
|
||
MeshFilter filter = (MeshFilter)GetComponent(typeof(MeshFilter));
|
||
if (filter == null)
|
||
filter = gameObject.AddComponent(typeof(MeshFilter)) as MeshFilter;
|
||
|
||
MeshRenderer newrender = gameObject.GetComponent<MeshRenderer>();
|
||
if (newrender == null)
|
||
newrender = gameObject.AddComponent<MeshRenderer>();
|
||
|
||
filter.mesh = mesh;
|
||
newrender.material = (Material)de.Key;
|
||
// renderer.material.shader = Shader.Find("Unlit/Transparent Cutout");
|
||
newrender.enabled = true;
|
||
newrender.shadowCastingMode = MeshRenderCastShadow;
|
||
newrender.receiveShadows = isMeshRenderReciveShadow;
|
||
}
|
||
}
|
||
// We have multiple materials to take care of, build one mesh / gameobject for each material
|
||
// and parent it to this object
|
||
else
|
||
{
|
||
|
||
string meshKey = de.Key.GetHashCode().ToString();
|
||
if (meshKeyWithName)
|
||
{
|
||
meshKey += "_" + gameObject.name;
|
||
}
|
||
Mesh mesh = __meshMap[meshKey] as Mesh;
|
||
if (mesh == null || !useSharedMesh)
|
||
{
|
||
mesh = MeshCombineUtility.Combine(instances, generateTriangleStrips);
|
||
__meshMap[meshKey] = mesh;
|
||
}
|
||
if (onlyCreateMesh)
|
||
{
|
||
saveMesh(gameObject.name, mesh);
|
||
}
|
||
else
|
||
{
|
||
// mesh = setuv2(mesh);
|
||
GameObject go = new GameObject("Combined mesh");
|
||
go.transform.parent = transform;
|
||
go.transform.localScale = Vector3.one;
|
||
go.transform.localRotation = Quaternion.identity;
|
||
go.transform.localPosition = Vector3.zero;
|
||
MeshFilter filter = go.AddComponent(typeof(MeshFilter)) as MeshFilter;
|
||
MeshRenderer newrender = go.AddComponent<MeshRenderer>();
|
||
newrender.material = (Material)de.Key;
|
||
newrender.shadowCastingMode = MeshRenderCastShadow;
|
||
newrender.receiveShadows = isMeshRenderReciveShadow;
|
||
// renderer.material.shader = Shader.Find("Unlit/Transparent Cutout");
|
||
filter.mesh = mesh;
|
||
newCombineds.Add(go);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// [SerializeField]
|
||
// Vector2[] combineUv2;
|
||
void saveMesh(string name, Mesh mesh)
|
||
{
|
||
#if UNITY_EDITOR
|
||
string dir = Application.dataPath;
|
||
|
||
string path = EditorUtility.SaveFilePanel("Save Mesh", dir, name, "asset");
|
||
if (!string.IsNullOrEmpty(path))
|
||
{
|
||
path = "Assets" + path.Replace(Application.dataPath, "");
|
||
Unwrapping.GenerateSecondaryUVSet(mesh);
|
||
// combineUv2 = mesh.uv2;
|
||
// mesh = setuv2(mesh);
|
||
AssetDatabase.CreateAsset(mesh, path);
|
||
|
||
}
|
||
#endif
|
||
}
|
||
|
||
Mesh setuv2(Mesh mesh)
|
||
{
|
||
// mesh.uv2 = combineUv2;
|
||
return mesh;
|
||
}
|
||
|
||
ArrayList newCombineds = new ArrayList();
|
||
//分解
|
||
public void explainChildren()
|
||
{
|
||
if (!iscombined)
|
||
return;
|
||
iscombined = false;
|
||
int count = 0;
|
||
if (filters != null && filters.Length > 0)
|
||
{
|
||
count = filters.Length;
|
||
Renderer curRenderer = null;
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
if (filters[i] == null)
|
||
continue;
|
||
curRenderer = filters[i].GetComponent<Renderer>();
|
||
if (curRenderer != null)
|
||
{
|
||
curRenderer.enabled = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
MeshRenderer mr = gameObject.GetComponent<MeshRenderer>();
|
||
if (mr != null)
|
||
{
|
||
mr.enabled = false;
|
||
#if UNITY_EDITOR
|
||
DestroyImmediate(mr);
|
||
#endif
|
||
}
|
||
|
||
count = newCombineds.Count;
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
GameObject.Destroy((GameObject)(newCombineds[i]));
|
||
}
|
||
newCombineds.Clear();
|
||
}
|
||
}
|
||
} |