Skip to content

Instantly share code, notes, and snippets.

@PopupAsylumUK
Last active April 12, 2017 13:52
Show Gist options
  • Save PopupAsylumUK/aa4c258b9d887f72ab2785909b18311e to your computer and use it in GitHub Desktop.
Save PopupAsylumUK/aa4c258b9d887f72ab2785909b18311e to your computer and use it in GitHub Desktop.
A script that finds meshes in the scene that could probably all be rendered as a single shadow caster and creates a moderately optimized mesh or meshes for shadow rendering. The resulting meshes should be cast shadows only, and the meshes they represent/replace should not cast shadows.
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
public class ShadowMeshBatcher : EditorWindow {
[MenuItem("Window/Shadow Mesh Batcher")]
static void Init() {
// Get existing open window or if none, make a new one:
ShadowMeshBatcher window = (ShadowMeshBatcher)EditorWindow.GetWindow(typeof(ShadowMeshBatcher));
window.Show();
window.position = new Rect(20, 80, 500, 620);
}
private void OnGUI() {
// create batches with a button press
if (GUILayout.Button("Batch Static Shadow Meshes")) {
SaveResults(CombineForShadowCaster(GetStaticMeshes()));
}
}
/// <summary>
/// Returns all the meshes in the scene that can be rendered in 1 shadow batch
/// </summary>
/// <returns></returns>
List<MeshFilter> GetStaticMeshes() {
//find all meshfilters in the scene
MeshFilter[] filtersInScene = FindObjectsOfType<MeshFilter>();
List<MeshFilter> staticFilters = new List<MeshFilter>();
// find the ones marked as static
for (int i = 0; i < filtersInScene.Length; i++) {
if (GameObjectUtility.AreStaticEditorFlagsSet(filtersInScene[i].gameObject, StaticEditorFlags.BatchingStatic)) {
staticFilters.Add(filtersInScene[i]);
}
}
// remove any without a sharedmesh
staticFilters.RemoveAll(obj => obj.sharedMesh == null);
//remove any without renderers, materials or are roughly not in the Geometry queue
staticFilters.RemoveAll(obj => obj.GetComponent<Renderer>() == null || obj.GetComponent<Renderer>().sharedMaterial == null || obj.GetComponent<Renderer>().sharedMaterial.renderQueue > 2100);
return staticFilters;
}
List<Mesh> CombineForShadowCaster(List<MeshFilter> meshes) {
// create a mesh list to return
List<Mesh> results = new List<Mesh>();
//our shadow mesh doesnt need to contain any duplicate/split verts for hard normals etc, using a dictionary makes it easier to remove duplicates
Dictionary<Vector3, int> vertDictionary;
List<Vector3> combinedVerts = new List<Vector3>();
List<int> combinedTris = new List<int>();
for (int i = 0; i < meshes.Count; i++) {
Mesh inputMesh = meshes[i].sharedMesh;
Matrix4x4 matrix = meshes[i].transform.localToWorldMatrix;
int[] tris = inputMesh.triangles;
Vector3[] verts = inputMesh.vertices;
//populate the dictionary with unique verts and thier index
vertDictionary = new Dictionary<Vector3, int>();
int tempCount = 0;
for (int j = 0; j < verts.Length; j++) {
if (!vertDictionary.ContainsKey(verts[j])) {
vertDictionary.Add(verts[j], tempCount);
tempCount++;
}
}
//rewrite the tris array using the dictionary
for (int j = 0; j < tris.Length; j++) {
tris[j] = vertDictionary[verts[tris[j]]];
}
//replace the verts with the nique ones from the dictionary
verts = new Vector3[vertDictionary.Keys.Count];
vertDictionary.Keys.CopyTo(verts, 0);
//transforme them to world space
for (int j = 0; j < verts.Length; j++) {
verts[j] = matrix.MultiplyPoint3x4(verts[j]);
}
//if adding these verts would go over the vertex limit make a mesh from the current data and clear the data for a new one
if (combinedVerts.Count + verts.Length > 65535) {
Mesh mesh = new Mesh();
mesh.SetVertices(combinedVerts);
mesh.SetTriangles(combinedTris, 0);
results.Add(mesh);
combinedVerts.Clear();
combinedTris.Clear();
}
//offset the triangle indicies based on how many other meshes have already been added to the batch
for (int j = 0; j < tris.Length; j++) {
tris[j] += combinedVerts.Count;
}
//add the new verts and tris
combinedVerts.AddRange(verts);
combinedTris.AddRange(tris);
}
//make a mesh from the remaining data
Mesh lastMesh = new Mesh();
lastMesh.SetVertices(combinedVerts);
lastMesh.SetTriangles(combinedTris, 0);
results.Add(lastMesh);
return results;
}
/// <summary>
/// save each mesh as a .asset
/// </summary>
/// <param name="meshes"></param>
void SaveResults(List<Mesh> meshes) {
for (int i = 0; i < meshes.Count; i++) {
AssetDatabase.CreateAsset(meshes[i], "Assets/combinedMesh_" + i.ToString() + ".asset");
}
AssetDatabase.SaveAssets();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment