Skip to content

Instantly share code, notes, and snippets.

@kalineh
Created April 9, 2018 23:44
Show Gist options
  • Save kalineh/7ffa5c9c84ee498b898b068d43d17491 to your computer and use it in GitHub Desktop.
Save kalineh/7ffa5c9c84ee498b898b068d43d17491 to your computer and use it in GitHub Desktop.
MeshCombinerNew.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
[InitializeOnLoad()]
class MeshCombinerHelperTag
{
static MeshCombinerHelperTag()
{
icon = EditorGUIUtility.ObjectContent(null, typeof(Mesh)).image;
EditorApplication.hierarchyWindowItemOnGUI -= OnHierarchyItem;
EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyItem;
EditorApplication.RepaintHierarchyWindow();
}
static Texture icon;
static void OnHierarchyItem(int instanceID, Rect instanceRect)
{
var obj = EditorUtility.InstanceIDToObject(instanceID);
var gameObject = obj as GameObject;
if (!gameObject)
return;
var combiner = gameObject.GetComponent<MeshCombinerHelper>();
if (!combiner)
return;
var width = 16;
var rightAlignedRect = new Rect(
instanceRect.xMax - width,
instanceRect.yMin,
width,
instanceRect.height + 1
);
GUI.Label(rightAlignedRect, icon);
}
}
[CustomEditor(typeof(MeshCombinerHelper))]
[CanEditMultipleObjects()]
public class MeshCombinerHelperEditor
: Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("Save Combined Mesh"))
{
foreach (var t in targets)
{
Undo.RecordObject(t, "Combine Mesh");
(t as MeshCombinerHelper).DoCombine();
}
}
if (GUILayout.Button("Revert Mesh Combine (DANGER)"))
{
foreach (var t in targets)
{
Undo.RecordObject(t, "Revert Mesh Combine");
(t as MeshCombinerHelper).DoUncombine();
}
}
}
}
#endif
[DisallowMultipleComponent]
public class MeshCombinerHelper
: MonoBehaviour
{
public bool autoCombineOnStart = false;
public bool autoLODPre = false;
public bool autoLODPost = false;
public bool autoStatic = true;
[Range(0.0f, 8.0f)]
public float autoLODPreWeight = 0.0f;
[Range(0.0f, 8.0f)]
public float autoLODPostWeight = 0.0f;
public bool overrideShadowCast = false;
public bool overrideShadowReceive = false;
public bool shadowCast = false;
public bool shadowReceive = false;
public void Start()
{
// NOTE: disabling just in case something is going on
//if (autoCombineOnStart)
//DoCombine();
}
public string GetGeneratedName(int index, Material mat)
{
return string.Format("MeshCombine{0}.{1}.{2}", index, gameObject.name, mat.name);
}
public void DoCombine()
{
var hadShadowReceivers = false;
var hadShadowCasters = false;
var meshFilters = GetComponentsInChildren<MeshFilter>();
var perMaterialCombines = new Dictionary<Material, List<CombineInstance>>();
var index = 0;
while (index < meshFilters.Length)
{
var meshRenderer = meshFilters[index].gameObject.GetComponent<MeshRenderer>();
var meshMaterial = meshRenderer.sharedMaterial;
if (!perMaterialCombines.ContainsKey(meshMaterial))
perMaterialCombines.Add(meshMaterial, new List<CombineInstance>());
var combines = perMaterialCombines[meshMaterial];
var combine = new CombineInstance();
combine.mesh = meshFilters[index].sharedMesh;
combine.transform = Matrix4x4.Inverse(transform.localToWorldMatrix) * meshFilters[index].transform.localToWorldMatrix;
combines.Add(combine);
meshFilters[index].gameObject.SetActive(false);
index++;
if (meshRenderer.shadowCastingMode == UnityEngine.Rendering.ShadowCastingMode.On)
hadShadowCasters = true;
if (meshRenderer.receiveShadows)
hadShadowReceivers = true;
}
if (overrideShadowCast)
hadShadowCasters = shadowCast;
if (overrideShadowReceive)
hadShadowReceivers = shadowReceive;
var count = 0;
foreach (var entry in perMaterialCombines)
{
var obj = gameObject;
obj = new GameObject(GetGeneratedName(count, entry.Key));
obj.transform.SetParent(transform);
obj.transform.ResetLocalTransforms();
var combineInstancesRaw = entry.Value.ToArray();
var combineInstancesProcessed = new CombineInstance[combineInstancesRaw.Length];
for (int i = 0; i < combineInstancesRaw.Length; ++i)
{
if (autoLODPre)
{
combineInstancesProcessed[i] = new CombineInstance();
combineInstancesProcessed[i].mesh = LODMaker.MakeLODMesh(combineInstancesRaw[i].mesh, autoLODPreWeight);
combineInstancesProcessed[i].subMeshIndex = combineInstancesRaw[i].subMeshIndex;
combineInstancesProcessed[i].transform = combineInstancesRaw[i].transform;
}
else
{
combineInstancesProcessed[i] = combineInstancesRaw[i];
}
}
if (autoLODPost)
{
var lod0Mesh = new Mesh();
lod0Mesh.CombineMeshes(combineInstancesRaw);
var lod1Mesh = LODMaker.MakeLODMesh(lod0Mesh, autoLODPostWeight);
lod0Mesh.RecalculateBounds();
lod0Mesh.RecalculateNormals();
lod1Mesh.RecalculateBounds();
lod1Mesh.RecalculateNormals();
var lod0obj = new GameObject();
var lod1obj = new GameObject();
lod0obj.transform.SetParent(obj.transform, false);
lod1obj.transform.SetParent(obj.transform, false);
var lod0MeshFilter = lod0obj.AddComponent<MeshFilter>();
var lod1MeshFilter = lod1obj.AddComponent<MeshFilter>();
var lod0MeshRenderer = lod0obj.AddComponent<MeshRenderer>();
var lod1MeshRenderer = lod1obj.AddComponent<MeshRenderer>();
lod0MeshRenderer.sharedMaterial = entry.Key;
lod1MeshRenderer.sharedMaterial = entry.Key;
lod0obj.name = "LOD0";
lod1obj.name = "LOD1";
lod0MeshFilter.mesh = lod0Mesh;
lod1MeshFilter.mesh = lod1Mesh;
var lodGroup = obj.AddComponent<LODGroup>();
var lods = new LOD[] {
new LOD(),
new LOD(),
};
lods[0].screenRelativeTransitionHeight = 0.7f;
lods[1].screenRelativeTransitionHeight = 0.02f;
lods[0].renderers = new Renderer[] { lod0MeshRenderer };
lods[1].renderers = new Renderer[] { lod1MeshRenderer };
lodGroup.SetLODs(lods);
lodGroup.RecalculateBounds();
}
else
{
var meshFilter = obj.AddComponent<MeshFilter>();
var meshRenderer = obj.AddComponent<MeshRenderer>();
meshRenderer.sharedMaterial = entry.Key;
meshFilter.sharedMesh = new Mesh();
meshFilter.sharedMesh.CombineMeshes(combineInstancesProcessed);
meshFilter.sharedMesh.RecalculateBounds();
meshFilter.sharedMesh.RecalculateNormals();
meshRenderer.receiveShadows = hadShadowReceivers;
meshRenderer.shadowCastingMode = hadShadowCasters ? UnityEngine.Rendering.ShadowCastingMode.On : UnityEngine.Rendering.ShadowCastingMode.Off;
}
if (autoStatic)
obj.isStatic = true;
obj.SetActive(true);
count++;
}
}
private static List<Transform> eraseList;
public void DoUncombine()
{
Debug.LogFormat("Undo Mesh Combine...");
if (eraseList == null)
eraseList = new List<Transform>();
eraseList.Clear();
for (int i = 0; i < transform.childCount; ++i)
{
var child = transform.GetChild(i);
if (child.name.Contains("MeshCombine"))
eraseList.Add(child);
}
foreach (var tfm in eraseList)
{
Debug.LogFormat("> destroy combined child '{0}'", tfm.name);
tfm.SetParent(null);
DestroyImmediate(tfm.gameObject);
}
eraseList.Clear();
var meshFilters = GetComponentsInChildren<MeshFilter>(true);
Debug.LogFormat("> found {0} meshFilters...", meshFilters.Length);
foreach (var mf in meshFilters)
{
if (mf.gameObject.activeSelf == false)
{
Debug.LogFormat("> * re-enable mesh filter child '{0}'", mf.name);
mf.gameObject.SetActive(true);
}
}
Debug.LogFormat("> destroying mesh filter / mesh renderer");
DestroyImmediate(GetComponent<MeshFilter>());
DestroyImmediate(GetComponent<MeshRenderer>());
Debug.LogFormat("Undo Mesh Combine complete.");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment