Skip to content

Instantly share code, notes, and snippets.

@LudwikJaniuk
Last active August 22, 2017 07:55
Show Gist options
  • Save LudwikJaniuk/dcb19f637dd4cf47eb7246321138a5c0 to your computer and use it in GitHub Desktop.
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.
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