Last active
August 22, 2017 07:55
-
-
Save LudwikJaniuk/dcb19f637dd4cf47eb7246321138a5c0 to your computer and use it in GitHub Desktop.
Recursively converts one big mesh into smaller ones until they all fall under the Unity vertex amount limit. Computes bounds and always splits along the largest axis.
This file contains hidden or 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 System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEngine; | |
class SplitUnityMesh | |
{ | |
private const int MAX_VERTICES = 65500; | |
// Handles splitting up into several meshes if there are too many vertices | |
public static List<Mesh> generateMeshes(List<Vector3> vertices, List<int> triangles) | |
{ | |
if(vertices.Count > MAX_VERTICES) | |
{ | |
var bounds = new Bounds(vertices[0], Vector3.zero); | |
foreach (var v in vertices) bounds.Encapsulate(v); | |
return splitMeshEnough(vertices, triangles, bounds); | |
} | |
else | |
{ | |
var mesh = new Mesh(); | |
mesh.SetVertices(vertices); | |
mesh.SetTriangles(triangles, 0); | |
return new List<Mesh>() { mesh }; | |
} | |
} | |
private static List<Mesh> splitMeshEnough(List<Vector3> vertices, List<int> triangles, Bounds bounds) | |
{ | |
if(vertices.Count <= MAX_VERTICES) | |
{ | |
return generateMeshes(vertices, triangles); | |
} | |
// In the end, there are almost always more vertices than we started with | |
var a = new List<Vector3>((int)(vertices.Count*0.6f)); | |
var b = new List<Vector3>((int)(vertices.Count*0.6f)); | |
Func<Vector3, bool> compareByX = (v => v.x < bounds.center.x); | |
Func<Vector3, bool> compareByY = (v => v.y < bounds.center.y); | |
Func<Vector3, bool> compareByZ = (v => v.z < bounds.center.z); | |
Func<Vector3, bool> belongsInA = bounds.size.x > Mathf.Max(bounds.size.y, bounds.size.z) ? compareByX // x is largest dimension | |
: bounds.size.y > bounds.size.z ? compareByY // y is largest dimension | |
: compareByZ; // z is largest dimension | |
// The index is positive if in a, otherwise it is added to int.MinValue | |
// So index 1337 in b becomes int.MinValue + 1337 | |
// Earlier solution involved a tuple with a bool, but this hack saved 25% runtime, so it's worth it. | |
var aOrBAndWhatIndex = new List<int>((int)(vertices.Count*1.1f)); | |
int aIndex = 0, bIndex = 0; | |
for(int i = 0; i < vertices.Count; i++) | |
{ | |
var v = vertices[i]; | |
if(belongsInA(v)) | |
{ | |
a.Add(v); | |
aOrBAndWhatIndex.Add(aIndex); | |
aIndex++; | |
} | |
else | |
{ | |
b.Add(v); | |
aOrBAndWhatIndex.Add(int.MinValue + bIndex); | |
bIndex++; | |
} | |
} | |
// In the end, there are almost always more triangles than we started with | |
var trisA = new List<int>((int)(triangles.Count*0.6f)); | |
var trisB = new List<int>((int)(triangles.Count*0.6f)); | |
// Now both sub-vertexlists are filled, and we need to split the triangles up. | |
for(int i = 0; i < triangles.Count - 2; i+=3) | |
{ | |
var p = triangles[i + 0]; | |
var q = triangles[i + 1]; | |
var r = triangles[i + 2]; | |
// Iff the index is non-negative, it's in A | |
var pInA = aOrBAndWhatIndex[p] >= 0; | |
var qInA = aOrBAndWhatIndex[q] >= 0; | |
var rInA = aOrBAndWhatIndex[r] >= 0; | |
// If at least two are in A, put in A, otherwise put in B | |
var putInA = pInA ? qInA || rInA : qInA && rInA; | |
Func<int, bool, int> targetIndexAndRelocateIfNeeded = (ind, indInA) => | |
{ | |
if (!indInA && putInA) | |
{ | |
// We have to make a copy in the target list | |
a.Add(vertices[ind]); | |
aOrBAndWhatIndex.Add(a.Count - 1); // Putting in a, so do nothing with index | |
return a.Count - 1; | |
} | |
else if (indInA && !putInA) | |
{ | |
// We have to make a copy in the target list | |
b.Add(vertices[ind]); | |
aOrBAndWhatIndex.Add(int.MinValue + b.Count - 1); // not putting in a, so adding inex to int.MinValue | |
return b.Count - 1; | |
} | |
else | |
{ | |
// It already is where it belongs | |
var index = aOrBAndWhatIndex[ind]; | |
return index >= 0 ? index : index - int.MinValue; | |
} | |
}; | |
// Now all of these will point to the target | |
p = targetIndexAndRelocateIfNeeded(p, pInA); | |
q = targetIndexAndRelocateIfNeeded(q, qInA); | |
r = targetIndexAndRelocateIfNeeded(r, rInA); | |
var target = (putInA ? trisA : trisB); | |
target.Add(p); | |
target.Add(q); | |
target.Add(r); | |
} | |
Bounds boundsA = bounds.size.x > Mathf.Max(bounds.size.y, bounds.size.z) ? new Bounds(bounds.center + new Vector3(-bounds.extents.x/2, 0, 0), new Vector3(bounds.size.x/2, bounds.size.y, bounds.size.z)) // x is largest dimension | |
: bounds.size.y > bounds.size.z ? new Bounds(bounds.center + new Vector3(0, -bounds.extents.y/2, 0), new Vector3(bounds.size.x, bounds.size.y/2, bounds.size.z)) // y is largest dimension | |
: new Bounds(bounds.center + new Vector3(0, 0, -bounds.extents.z/2), new Vector3(bounds.size.x, bounds.size.y, bounds.size.z/2)); // z is largest dimension | |
Bounds boundsb = bounds.size.x > Mathf.Max(bounds.size.y, bounds.size.z) ? new Bounds(bounds.center + new Vector3(bounds.extents.x/2, 0, 0), new Vector3(bounds.size.x/2, bounds.size.y, bounds.size.z)) // x is largest dimension | |
: bounds.size.y > bounds.size.z ? new Bounds(bounds.center + new Vector3(0, bounds.extents.y/2, 0), new Vector3(bounds.size.x, bounds.size.y/2, bounds.size.z)) // y is largest dimension | |
: new Bounds(bounds.center + new Vector3(0, 0, bounds.extents.z/2), new Vector3(bounds.size.x, bounds.size.y, bounds.size.z/2)); // z is largest dimension | |
var meshes = splitMeshEnough(a, trisA, boundsA); | |
meshes.AddRange(splitMeshEnough(b, trisB, boundsb)); | |
return meshes; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment