Optimizing Mesh Splitting Code

I have written some code that generates a simple quad, then I borrowed code from the UnifyCommunity that subdivides meshes and keeps the vertices shared, then modified it to subdivide a very specific way, and finally I added another static function that splits the mesh into four separate meshes. The function that splits the subdivided quad runs fine up until the quad has more than 1k vertices, then it’s lack of optimization begins to really show. Basically I want to know how I can get my split function to run far faster than it does as I will have to be splitting several high vertex count meshes frequently. Code to reproduce what I am doing can be found below.

Mesh Controller Script: Add this code to an empty gameObject and assign a material for the objectMaterial variable.
A quad will be generated in play mode and the “u” key can be used to subdivide the quad and the “i” key can be used to split it.

using UnityEngine;
using System.Collections;

public class MeshController : MonoBehaviour {

	public Material objectMaterial; //assign a material here
	
	void Start () {

		if(transform.parent == null){

			gameObject.AddComponent("MeshRenderer");
			gameObject.AddComponent("MeshFilter");
			gameObject.AddComponent("MeshCollider");
			
			Mesh mesh = new Mesh();
			
			Vector3[] vertices = new Vector3[]
			{
				new Vector3(1, 0, 1),
				new Vector3(1, 0, -1),
				new Vector3(-1, 0, 1),
				new Vector3(-1, 0, -1),
			};
			
			Color[] colors = new Color[]
			{
				Color.white,
				Color.white,
				Color.white,
				Color.white,
			};
			
			Vector2[] uv = new Vector2[]
			{
				new Vector2(1, 1),
				new Vector2(1, 0),
				new Vector2(0, 1),
				new Vector2(0, 0),
			};
			
			int[] triangles = new int[]
			{
				0, 1, 2,
				2, 1, 3,
			};
			
			Vector3[] normals = new Vector3[ vertices.Length ];
			for( int n = 0; n < normals.Length; n++ )
				normals[n] = Vector3.up;
			
			mesh.vertices = vertices;
			mesh.normals = normals;
			mesh.colors = colors;
			mesh.uv = uv;
			mesh.triangles = triangles;
			
			GetComponent<MeshFilter>().mesh = mesh;
			GetComponent<MeshCollider>().mesh = mesh;

			gameObject.renderer.material = objectMaterial;

		}
	
	}

	void Update () {

		if(Input.GetKeyDown("u")){

			MeshSubdiv.Subdivide(gameObject.GetComponent<MeshFilter>().mesh);

		}

		if(Input.GetKeyDown("i")){

			if(GetComponent<MeshRenderer>().enabled == true){

				Mesh[] splitFaces = new Mesh[4];
				splitFaces = MeshSubdiv.Split(gameObject.GetComponent<MeshFilter>().mesh);

				for(int i = 0; i < 4; i++){

					GameObject newFace = new GameObject();
					newFace.transform.position = transform.position;
					newFace.transform.rotation = transform.rotation;
					newFace.name = "Face" + (i +1);
					newFace.transform.parent = gameObject.transform;
					newFace.AddComponent("MeshRenderer");
					newFace.AddComponent("MeshFilter");
					
					newFace.GetComponent<MeshFilter>().mesh = splitFaces*;*
  •  			newFace.AddComponent("MeshController");*
    
  •  			newFace.renderer.material = gameObject.renderer.material;*
    
  •  			newFace.AddComponent("MeshCollider");*
    

_ newFace.GetComponent().mesh = splitFaces*;*_

* }*

* GetComponent().enabled = false;*
* GetComponent().enabled = false;*
* //gameObject.tag = (“Untagged”);*

* }*
* }*
* }*
}
MeshSubdiv Script: This script is where all the subdivision and splitting happens.
The “Split” function is where things get slow, after some testing I think that using “list.IndexOf” and “list.Contains” are what’s making it slow…
A quad will be generated in play mode and the “u” key can be used to subdivide the quad and the “i” key can be used to split it.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public static class MeshSubdiv
{
* static List vertices;*
* static List normals;*
* static List colors;*
* static List uv;*
* static List uv1;*
* static List uv2;*
* static List indices;*
* static Dictionary<uint,int> newVectices;*
* static Dictionary<int,int> newVectices2;*

* static void InitArrays(Mesh mesh) //for subdivision*
* {*
* vertices = new List(mesh.vertices);*
* normals = new List(mesh.normals);*
* colors = new List(mesh.colors);*
* uv = new List(mesh.uv);*
* uv1 = new List(mesh.uv1);*
* uv2 = new List(mesh.uv2);*
* indices = new List();*
* }*

* static void InitArrays2(Mesh mesh) //for mesh splitting*
* {*
* vertices = new List(mesh.vertices);*
* normals = new List(mesh.normals);*
* colors = new List(mesh.colors);*
* uv = new List(mesh.uv);*
* uv1 = new List(mesh.uv1);*
* uv2 = new List(mesh.uv2);*
* indices = new List(mesh.triangles);*
* }*

* static void CleanUp()*
* {*
* vertices = null;*
* normals = null;*
* colors = null;*
* uv = null;*
* uv1 = null;*
* uv2 = null;*
* indices = null;*
* }*

* static int GetNewVertex(int i1, int i2)*
* {*
* int newIndex = vertices.Count;*
* uint t1 = ((uint)i1 << 16) | (uint)i2;*
* uint t2 = ((uint)i2 << 16) | (uint)i1;*
* if (newVectices.ContainsKey(t2))*
* return newVectices[t2];*
* if (newVectices.ContainsKey(t1))*
* return newVectices[t1];*

* newVectices.Add(t1,newIndex);*

_ vertices.Add((vertices[i1] + vertices[i2]) * 0.5f);
* if (normals.Count>0)*
* normals.Add((normals[i1] + normals[i2]).normalized);*
* if (colors.Count>0)*
colors.Add((colors[i1] + colors[i2]) * 0.5f);
* if (uv.Count>0)*
uv.Add((uv[i1] + uv[i2])0.5f);_
_
if (uv1.Count>0)_
_ uv1.Add((uv1[i1] + uv1[i2])0.5f);_
_
if (uv2.Count>0)
_
_ uv2.Add((uv2[i1] + uv2[i2])*0.5f);_

* return newIndex;*
* }*

* public static void Subdivide(Mesh mesh)*
* {*
* newVectices = new Dictionary<uint,int>();*

* InitArrays(mesh);*

* int[] triangles = mesh.triangles;*
* for (int i = 0; i < triangles.Length; i += 6)*
* {*
* int i0 = triangles[i + 0];*
* int i1 = triangles[i + 1];*
* int i2 = triangles[i + 2];*
* int i3 = triangles[i + 3];*
* int i4 = triangles[i + 4];*
* int i5 = triangles[i + 5];*

* int m0 = GetNewVertex(i0, i1);*
* int m1 = GetNewVertex(i1, i2);*
* int m2 = GetNewVertex(i0, i2);*
* int m3 = GetNewVertex(i3, i4);*
* int m4 = GetNewVertex(i4, i5);*
* int m5 = GetNewVertex(i3, i5);*

* indices.Add(m0); indices.Add(i1); indices.Add(m1);*
* indices.Add(m3); indices.Add(i4); indices.Add(m4);*

* indices.Add(m3); indices.Add(m4); indices.Add(m5);*
* indices.Add(m5); indices.Add(m4); indices.Add(i5);*

* indices.Add(i0); indices.Add(m0); indices.Add(m2);*
* indices.Add(m2); indices.Add(m0); indices.Add(m1);*

* indices.Add(m2); indices.Add(m1); indices.Add(i2);*
* indices.Add(i3); indices.Add(m3); indices.Add(m5);*

* }*
* mesh.vertices = vertices.ToArray();*
* if (normals.Count > 0)*
* mesh.normals = normals.ToArray();*
* if (colors.Count>0)*
* mesh.colors = colors.ToArray();*
* if (uv.Count>0)*
* mesh.uv = uv.ToArray();*
* if (uv1.Count>0)*
* mesh.uv1 = uv1.ToArray();*
* if (uv2.Count>0)*
* mesh.uv2 = uv2.ToArray();*

* mesh.triangles = indices.ToArray();*

* CleanUp();*
* }*

* //Mesh Split Function*

* public static Mesh[] Split(Mesh mesh)*
* {*
* InitArrays2(mesh);*
* Mesh[] meshList = new Mesh[4];*

* for(int n = 0; n < 4; n++){*

* List newVertices = new List();*
* List newNormals = new List();*
* List newColors = new List();*
* List newUv = new List();*
* List newUv1 = new List();*
* List newUv2 = new List();*
* List newIndices = new List();*

* newVectices2 = new Dictionary<int,int>();*

_ for(int i = 0 + (n * (indices.Count/4)); i < ((indices.Count/4) * (n + 1)); i += 6){_

* for(int x = 0; x < 6; x++){*

* int curIndex = indices[i+x];*

* if(!newVectices2.ContainsKey(curIndex)){*

* newVertices.Add (vertices[indices[i+x]]);*
* newNormals.Add (normals[indices[i+x]]);*
* //newColors.Add (colors[indices[i+x]]); no mesh colors*
* newUv.Add (uv[indices[i+x]]);*
* //newUv1.Add (uv1[indices[i+x]]); no uv1*
* //newUv2.Add (uv2[indices[i+x]]); no uv2*
* newIndices.Add (newVertices.Count-1);*
* newVectices2.Add (curIndex, newVertices.Count-1);*

* }else{*

* newIndices.Add ((int)newVectices2[curIndex]);*

* }*

* }*

* }*

* Mesh newMesh = new Mesh();*

* newMesh.vertices = newVertices.ToArray();*

* if(newNormals.Count > 0)*
* newMesh.normals = newNormals.ToArray();*
* if(newColors.Count > 0)*
* newMesh.colors = newColors.ToArray();*
* if(newUv.Count > 0)*
* newMesh.uv = newUv.ToArray();*
* if(newUv1.Count > 0)*
* newMesh.uv1 = newUv1.ToArray();*
* if(newUv2.Count > 0)*
* newMesh.uv2 = newUv2.ToArray();*

* newMesh.triangles = newIndices.ToArray();*

* meshList[n] = newMesh;*

* }*

* CleanUp();*

* return meshList;*

* }*

* //Mesh Split Function*

* public static Mesh DuplicateMesh(Mesh mesh)*
* {*
* return (Mesh)UnityEngine.Object.Instantiate(mesh);*
* }*

}

There are a couple of things you can do to improve your performance.

  1. You are creating new lists with InitArrays.
    You should replace this with List.Clear(). This prevents further memory allocations every time you initialize.
    Also you should not destroy then on cleanup if you want to reuse them.

  2. You can replace the ContainsKey with TryGetValue. Its a slightly faster, but can have huge impact on performance depending on the number of hit/misses, and number of calls.
    More info here: c# - What is more efficient: Dictionary TryGetValue or ContainsKey+Item? - Stack Overflow

  3. newFace.name = “Face” + (i +1);
    Probably unnoticeable, but since every tick counts… contatenating strings takes some toll on memory allocation.
    (that statement is actually creating 2 extra strings)
    You can replace with: String.Format(“Face{0}”, (i+1))

  4. for(int i = 0 + (n * (indices.Count/4)); i < ((indices.Count/4) * (n + 1)); i += 6)
    You should remove the calculations from the loop:

int fromVal = (n * (indices.Count/4));
int toVal = ((indices.Count/4) * (n + 1));
for(int i = fromVal; i < toVal; i += 6)…

  1. On your Split function, you have: int curIndex = indices[i+x];
    but you are not using it in every indices[i+x]

EDIT: just noticed that every time you: (something[i1] + something[i2]) * 0.5f
can be replaced by the much faster: (something[i1] + something[i2]) >> 1

These all add up on your performance.
I hope this helps!