Last active
October 6, 2016 08:22
-
-
Save PopupAsylum/1a682b336b774d2616e51e08200f090c to your computer and use it in GitHub Desktop.
Combines multiple meshes based on materials and lightmap index
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
using System.Collections.Generic; | |
public class MeshBatcher : MonoBehaviour { | |
class MeshStrip | |
{ | |
public List<int> strip = new List<int>(); | |
} | |
public List<GameObject> batchedResults = new List<GameObject>(); | |
public bool batchOnStart = false; | |
/// <summary> | |
/// Batchs the index of the lightmap. | |
/// Meshes are divided into mesh strips (one strip per material) | |
/// Meshes are given a lightmap index; | |
/// Meshes with the same lightmap index and strips that use the same material can have thier strips batched. | |
/// Lightmapped meshes have a second (full 01) uv set and renderers contain the offset/scale of it in the lightmap | |
/// UV2s must therefore be adjusted to account for the fact that we can only have one offset | |
/// </summary> | |
/// <param name='lightmapIndex'> | |
/// Lightmap index. | |
/// </param> | |
void BatchLightmapIndex(int lightmapIndex) | |
{ | |
MeshRenderer[] meshRenderers = GetComponentsInChildren<MeshRenderer>(); | |
List<Material> materials = UniqueMaterialsForRenderers(meshRenderers); | |
MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>(); | |
meshFilters = RemoveDisabledRenderersAndNoneLightmap(meshFilters, lightmapIndex); | |
List<Vector3> verts = new List<Vector3>(); | |
List<Vector3> norms = new List<Vector3>(); | |
List<Vector2> uv = new List<Vector2>(); | |
List<Vector2> uv1 = new List<Vector2>(); | |
List<Color> col = new List<Color>(); | |
List<MeshStrip> strips = new List<MeshStrip>(); | |
for (int i = 0; i<materials.Count; i++){ | |
strips.Add(new MeshStrip()); | |
} | |
foreach (MeshFilter mf in meshFilters) | |
{ | |
int vertCount = verts.Count; | |
// if the mesh filter has no mesh ignore it | |
if (!mf || !mf.sharedMesh){ | |
break; | |
} | |
//if the result of adding this mesh would produce a mesh with a vertex count thats too high, create the current mesh and begin a new one | |
if (mf.sharedMesh.vertexCount + vertCount > 65000) | |
{ | |
MakeMeshChild("MeshLightmapIndex_" + lightmapIndex + "_" + vertCount, | |
verts.ToArray(), | |
norms.ToArray(), | |
uv.ToArray(), | |
uv1.ToArray(), | |
col.ToArray(), | |
strips.ToArray(), | |
materials.ToArray(), | |
lightmapIndex); | |
verts.Clear(); | |
norms.Clear(); | |
uv.Clear(); | |
uv1.Clear(); | |
col.Clear(); | |
foreach (MeshStrip ms in strips) | |
{ | |
ms.strip.Clear(); | |
} | |
vertCount = 0; | |
} | |
//ADDING VERTICIES | |
Vector3[] newVerts = new Vector3[mf.sharedMesh.vertices.Length]; | |
for (int i = 0; i<mf.sharedMesh.vertices.Length; i++) | |
{ | |
newVerts[i] = transform.InverseTransformPoint(mf.transform.TransformPoint(mf.sharedMesh.vertices[i])); | |
} | |
verts.AddRange(newVerts); | |
//ADDING STANDARD NORMALS | |
norms.AddRange(mf.sharedMesh.normals); | |
//ADDING STANDARD UVS | |
uv.AddRange(mf.sharedMesh.uv); | |
//CONVERTING INDIVIDUAL LIGHTMAP UVS TO THIS RENDERERS LIGHTMAP COORDS | |
if (lightmapIndex>=0 && lightmapIndex<255){ | |
Vector2[] lightmapUVs; | |
if (mf.sharedMesh.uv2 != null && mf.sharedMesh.uv2.Length > 0){ | |
lightmapUVs = mf.sharedMesh.uv2; | |
} else { | |
lightmapUVs = mf.sharedMesh.uv; | |
} | |
Vector4 lightmapTilingOffset = mf.renderer.lightmapTilingOffset; | |
Vector2 uvscale = new Vector2( lightmapTilingOffset.x, lightmapTilingOffset.y ); | |
Vector2 uvoffset = new Vector2( lightmapTilingOffset.z, lightmapTilingOffset.w ); | |
for ( int j = 0; j < lightmapUVs.Length; j++ ) { | |
lightmapUVs[j] = uvoffset + new Vector2( uvscale.x * lightmapUVs[j].x, uvscale.y * lightmapUVs[j].y ); | |
} | |
uv1.AddRange(lightmapUVs); | |
} | |
//ACCOUNTING FOR MESHES WITHOUT VERTEX COLORS | |
if (mf.sharedMesh.colors.Length == 0){ | |
Color[] replacementColors = new Color[mf.sharedMesh.vertexCount]; | |
for (int i = 0; i < mf.sharedMesh.vertexCount; i++){replacementColors[i] = Color.white;} | |
col.AddRange(replacementColors); | |
} | |
else{ | |
col.AddRange(mf.sharedMesh.colors); | |
} | |
//ASSEMBLING TRIANGLE STRIPS | |
for (int subMeshIndex = 0; subMeshIndex < mf.sharedMesh.subMeshCount && subMeshIndex < mf.renderer.sharedMaterials.Length; subMeshIndex++) | |
{ | |
int[] mfStrip = mf.sharedMesh.GetTriangles(subMeshIndex); | |
if (MeshIsFlipped(mf)){ | |
mfStrip = ReverseTriangleWinding(mfStrip); | |
} | |
for(int i = 0; i < mfStrip.Length; i++) | |
{ | |
mfStrip[i] = mfStrip[i] + vertCount; | |
} | |
int stripIndex = materials.IndexOf(mf.renderer.sharedMaterials[subMeshIndex]); | |
strips[stripIndex].strip.AddRange(mfStrip); | |
} | |
} | |
MakeMeshChild("MeshLightmapIndex_" + lightmapIndex + "_" + verts.Count, | |
verts.ToArray(), | |
norms.ToArray(), | |
uv.ToArray(), | |
uv1.ToArray(), | |
col.ToArray(), | |
strips.ToArray(), | |
materials.ToArray(), | |
lightmapIndex); | |
foreach (MeshFilter r in meshFilters){ | |
DestroyImmediate(r.renderer); | |
DestroyImmediate(r); | |
} | |
} | |
void MakeMeshChild(string name, Vector3[] verts, Vector3[] norms, Vector2[] uv0s, Vector2[] uv1s, Color[] cols, MeshStrip[] subMeshes, Material[] materials, int lightmapIndex) | |
{ | |
//MAKE NEW MESH | |
Mesh newMesh = new Mesh(); | |
newMesh.name = name; | |
//APPLYING VERTICIES< NORMALS< UVS< UV2S< COLORS | |
newMesh.vertices = verts; | |
newMesh.normals = norms; | |
newMesh.uv = uv0s; | |
if (uv1s.Length == verts.Length){ | |
newMesh.uv2 = uv1s; | |
} | |
newMesh.colors = cols; | |
//APPLYING SUBMESH TRIANGLE STRIPS | |
newMesh.subMeshCount = materials.Length; | |
for (int i = 0; i<subMeshes.Length; i++) | |
{ | |
newMesh.SetTriangles(subMeshes[i].strip.ToArray(), i); | |
} | |
GameObject childMesh = new GameObject(name); | |
Transform cT = childMesh.transform; | |
cT.parent = transform; | |
cT.localPosition = Vector3.zero; | |
cT.localScale = Vector3.one; | |
cT.localRotation = Quaternion.identity; | |
MeshFilter mfr = childMesh.AddComponent<MeshFilter>(); | |
mfr.sharedMesh = newMesh; | |
MeshRenderer mr = childMesh.AddComponent<MeshRenderer>(); | |
mr.sharedMaterials = materials; | |
mr.lightmapIndex = lightmapIndex; | |
childMesh.layer = this.gameObject.layer; | |
batchedResults.Add(childMesh); | |
} | |
int[] ReverseTriangleWinding(int[] triangleStrip) | |
{ | |
int[] copy = new int[triangleStrip.Length]; | |
for (int i = 0; i<triangleStrip.Length; i+=3) | |
{ | |
copy[i] = triangleStrip[i]; | |
copy[i+1] = triangleStrip[i+2]; | |
copy[i+2] = triangleStrip[i+1]; | |
} | |
return copy; | |
} | |
bool MeshIsFlipped(MeshFilter m) | |
{ | |
Vector3 mScale = m.transform.lossyScale; | |
return (mScale.x*mScale.y*mScale.z < 0); | |
} | |
MeshFilter[] RemoveDisabledRenderersAndNoneLightmap(MeshFilter[] mfs, int removeMeshesWithLightmapIndexesThatDontMatchThisValue) | |
{ | |
List<MeshFilter> filterList = new List<MeshFilter>(); | |
filterList.AddRange(mfs); | |
List<MeshFilter> dupFilterList = new List<MeshFilter>(); | |
dupFilterList.AddRange(mfs); | |
for (int i = 0; i < mfs.Length; i++){ | |
if (dupFilterList[i].renderer.enabled == false || dupFilterList[i].renderer.lightmapIndex!=removeMeshesWithLightmapIndexesThatDontMatchThisValue) { | |
filterList.Remove(dupFilterList[i]); | |
} | |
} | |
return filterList.ToArray(); | |
} | |
List<Material> UniqueMaterialsForRenderers(MeshRenderer[] meshRenderers) | |
{ | |
List<Material> materials = new List<Material>(); | |
foreach (MeshRenderer mr in meshRenderers){ | |
foreach (Material mat in mr.sharedMaterials){ | |
if (mat && !materials.Contains(mat)){ | |
materials.Add(mat); | |
} | |
} | |
} | |
return materials; | |
} | |
/// <summary> | |
/// Gets the lightmap indicies of mesh renderers in children | |
/// </summary> | |
/// <returns> | |
/// The lightmap indicies. | |
/// </returns> | |
int[] GetLightmapIndicies() | |
{ | |
MeshRenderer[] mrs = gameObject.GetComponentsInChildren<MeshRenderer>(); | |
List<int> lightmapIndexes = new List<int>(); | |
foreach (MeshRenderer mr in mrs){ | |
if (!lightmapIndexes.Contains(mr.lightmapIndex)){ | |
lightmapIndexes.Add(mr.lightmapIndex); | |
} | |
} | |
return lightmapIndexes.ToArray(); | |
} | |
public void Start() | |
{ | |
if (batchOnStart && (batchedResults == null || batchedResults.Count == 0)){ | |
Batch(); | |
} | |
} | |
public void Batch() | |
{ | |
//if (DuplicateBatch()){return;} | |
foreach (int index in GetLightmapIndicies()) | |
{ | |
if (index>=-1 && index<=255){ BatchLightmapIndex(index);} | |
} | |
} | |
public bool DuplicateBatch() | |
{ | |
foreach (MeshBatcher mb in FindObjectsOfType(typeof(MeshBatcher)) as MeshBatcher[]) { | |
if (mb.name == gameObject.name && mb.batchedResults.Count>0){ | |
foreach (GameObject r in mb.batchedResults){ | |
GameObject doop = Instantiate(r, this.transform.position, this.transform.rotation) as GameObject; | |
doop.transform.parent = this.transform; | |
} | |
return true; | |
} | |
} | |
return false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment