Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions EzySlice/Framework/SlicerSourceMesh.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static EzySlice.Slicer;

namespace EzySlice
{
/**
* Caches mesh data to eliminate allocations from retrieving it.
*/
public class SlicerSourceMesh
{
public readonly Mesh mesh;

public readonly Vector3[] vertices;
public readonly Vector2[] uv;
public readonly Vector3[] normals;
public readonly Vector4[] tangents;
public readonly int[][] submeshTriangles;

// Recycled buffers used for Slice. NOT thread-safe.

internal readonly SlicedSubmesh[] slices;
internal readonly List<Vector3> crossHull;
internal readonly IntersectionResult intersectionResult;

public SlicerSourceMesh(Mesh mesh)
{
this.mesh = mesh;

vertices = mesh.vertices;
uv = mesh.uv;
normals = mesh.normals;
tangents = mesh.tangents;

int submeshCount = mesh.subMeshCount;

submeshTriangles = new int[submeshCount][];
slices = new SlicedSubmesh[submeshCount];
for (int i = 0; i < submeshCount; i++)
{
slices[i] = new SlicedSubmesh();
submeshTriangles[i] = mesh.GetTriangles(i);
}

crossHull = new List<Vector3>();
intersectionResult = new IntersectionResult();
}

public void ResetBuffers()
{
foreach (SlicedSubmesh submesh in slices)
{
submesh.upperHull.Clear();
submesh.lowerHull.Clear();
}
crossHull.Clear();
intersectionResult.Clear();
}
}

/**
* Caches SlicerSourceMesh objects for shared meshes.
* You can use this for simple, easy caching, or create your own SlicerSourceMesh objects instead.
*/
public static class SlicerMeshCache
{
private static Dictionary<Mesh, SlicerSourceMesh> cache = new Dictionary<Mesh, SlicerSourceMesh>();

public static SlicerSourceMesh Get(Mesh mesh)
{
//TODO: detection for cache running away (e.g. not using sharedMesh)
if (cache.TryGetValue(mesh, out SlicerSourceMesh cachedMesh))
{
return cachedMesh;
}
else
{
SlicerSourceMesh newCache = new SlicerSourceMesh(mesh);
cache.Add(mesh, newCache);
return newCache;
}
}
}
}
14 changes: 13 additions & 1 deletion EzySlice/SlicedHull.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,19 @@ public sealed class SlicedHull {
private Mesh upper_hull;
private Mesh lower_hull;

public SlicedHull(Mesh upperHull, Mesh lowerHull) {
public SlicedHull(bool useUpper, bool useLower) {
if (useUpper) {
upper_hull = new Mesh();
upper_hull.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
}

if (useLower) {
lower_hull = new Mesh();
lower_hull.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
}
}

public SlicedHull(Mesh upperHull, Mesh lowerHull) {
this.upper_hull = upperHull;
this.lower_hull = lowerHull;
}
Expand Down
92 changes: 61 additions & 31 deletions EzySlice/Slicer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,33 +122,57 @@ public static SlicedHull Slice(GameObject obj, Plane pl, TextureRegion crossRegi
return Slice(mesh, pl, crossRegion, crossIndex);
}

/**
/**
* Slice the gameobject mesh (if any) using the Plane, which will generate
* a maximum of 2 other Meshes.
* This function will recalculate new UV coordinates to ensure textures are applied
* properly.
* Returns null if no intersection has been found or the GameObject does not contain
* a valid mesh to cut.
* The overload that takes a SlicerSourceMesh because it will generate fewer allocations
* allocations if that object is re-used.
*/
public static SlicedHull Slice(Mesh sharedMesh, Plane pl, TextureRegion region, int crossIndex)
{
if (sharedMesh == null)
{
return null;
}

SlicerSourceMesh sourceMesh = new SlicerSourceMesh(sharedMesh);
return Slice(sourceMesh, pl, region, crossIndex);
}

/**
* Slice the gameobject mesh (if any) using the Plane, which will generate
* a maximum of 2 other Meshes.
* This function will recalculate new UV coordinates to ensure textures are applied
* properly.
* Returns null if no intersection has been found or the GameObject does not contain
* a valid mesh to cut.
* If an existing SlicedHull buffer is passed, the Mesh objects in it will be re-used.
*/
public static SlicedHull Slice(Mesh sharedMesh, Plane pl, TextureRegion region, int crossIndex) {
if (sharedMesh == null) {
public static SlicedHull Slice(SlicerSourceMesh sourceMesh, Plane pl, TextureRegion region, int crossIndex, SlicedHull buffer = null) {
if (sourceMesh == null) {
return null;
}

Vector3[] verts = sharedMesh.vertices;
Vector2[] uv = sharedMesh.uv;
Vector3[] norm = sharedMesh.normals;
Vector4[] tan = sharedMesh.tangents;
sourceMesh.ResetBuffers();

Vector3[] verts = sourceMesh.vertices;
Vector2[] uv = sourceMesh.uv;
Vector3[] norm = sourceMesh.normals;
Vector4[] tan = sourceMesh.tangents;

int submeshCount = sharedMesh.subMeshCount;
int submeshCount = sourceMesh.mesh.subMeshCount;

// each submesh will be sliced and placed in its own array structure
SlicedSubmesh[] slices = new SlicedSubmesh[submeshCount];
SlicedSubmesh[] slices = sourceMesh.slices;
// the cross section hull is common across all submeshes
List<Vector3> crossHull = new List<Vector3>();
List<Vector3> crossHull = sourceMesh.crossHull;

// we reuse this object for all intersection tests
IntersectionResult result = new IntersectionResult();
IntersectionResult result = sourceMesh.intersectionResult;

// see if we would like to split the mesh using uv, normals and tangents
bool genUV = verts.Length == uv.Length;
Expand All @@ -158,10 +182,10 @@ public static SlicedHull Slice(Mesh sharedMesh, Plane pl, TextureRegion region,
// iterate over all the submeshes individually. vertices and indices
// are all shared within the submesh
for (int submesh = 0; submesh < submeshCount; submesh++) {
int[] indices = sharedMesh.GetTriangles(submesh);
int[] indices = sourceMesh.submeshTriangles[submesh];
int indicesCount = indices.Length;

SlicedSubmesh mesh = new SlicedSubmesh();
SlicedSubmesh mesh = slices[submesh];

// loop through all the mesh vertices, generating upper and lower hulls
// and all intersection points
Expand Down Expand Up @@ -235,17 +259,20 @@ public static SlicedHull Slice(Mesh sharedMesh, Plane pl, TextureRegion region,
}
}
}

// register into the index
slices[submesh] = mesh;
}

if (buffer == null)
{
buffer = new SlicedHull(true, true);
}

// check if slicing actually occured
for (int i = 0; i < slices.Length; i++) {
// check if at least one of the submeshes was sliced. If so, stop checking
// because we need to go through the generation step
if (slices[i] != null && slices[i].isValid) {
return CreateFrom(slices, CreateFrom(crossHull, pl.normal, region), crossIndex);
CreateFrom(slices, CreateFrom(crossHull, pl.normal, region), crossIndex, buffer);
return buffer;
}
}

Expand All @@ -256,7 +283,7 @@ public static SlicedHull Slice(Mesh sharedMesh, Plane pl, TextureRegion region,
/**
* Generates a single SlicedHull from a set of cut submeshes
*/
private static SlicedHull CreateFrom(SlicedSubmesh[] meshes, List<Triangle> cross, int crossSectionIndex) {
private static void CreateFrom(SlicedSubmesh[] meshes, List<Triangle> cross, int crossSectionIndex, SlicedHull buffer) {
int submeshCount = meshes.Length;

int upperHullCount = 0;
Expand All @@ -268,34 +295,35 @@ private static SlicedHull CreateFrom(SlicedSubmesh[] meshes, List<Triangle> cros
lowerHullCount += meshes[submesh].lowerHull.Count;
}

Mesh upperHull = CreateUpperHull(meshes, upperHullCount, cross, crossSectionIndex);
Mesh lowerHull = CreateLowerHull(meshes, lowerHullCount, cross, crossSectionIndex);

return new SlicedHull(upperHull, lowerHull);
if (buffer.upperHull)
{
CreateUpperHull(meshes, buffer.upperHull, upperHullCount, cross, crossSectionIndex);
}
if (buffer.lowerHull)
{
CreateLowerHull(meshes, buffer.lowerHull, lowerHullCount, cross, crossSectionIndex);
}
}

private static Mesh CreateUpperHull(SlicedSubmesh[] mesh, int total, List<Triangle> crossSection, int crossSectionIndex) {
return CreateHull(mesh, total, crossSection, crossSectionIndex, true);
private static Mesh CreateUpperHull(SlicedSubmesh[] submeshes, Mesh mesh, int total, List<Triangle> crossSection, int crossSectionIndex) {
return CreateHull(submeshes, mesh, total, crossSection, crossSectionIndex, true);
}

private static Mesh CreateLowerHull(SlicedSubmesh[] mesh, int total, List<Triangle> crossSection, int crossSectionIndex) {
return CreateHull(mesh, total, crossSection, crossSectionIndex, false);
private static Mesh CreateLowerHull(SlicedSubmesh[] submeshes, Mesh mesh, int total, List<Triangle> crossSection, int crossSectionIndex) {
return CreateHull(submeshes, mesh, total, crossSection, crossSectionIndex, false);
}

/**
* Generate a single Mesh HULL of either the UPPER or LOWER hulls.
*/
private static Mesh CreateHull(SlicedSubmesh[] meshes, int total, List<Triangle> crossSection, int crossIndex, bool isUpper) {
private static Mesh CreateHull(SlicedSubmesh[] meshes, Mesh newMesh, int total, List<Triangle> crossSection, int crossIndex, bool isUpper) {
if (total <= 0) {
return null;
}

int submeshCount = meshes.Length;
int crossCount = crossSection != null ? crossSection.Count : 0;

Mesh newMesh = new Mesh();
newMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;

int arrayLen = (total + crossCount) * 3;

bool hasUV = meshes[0].hasUV;
Expand Down Expand Up @@ -446,7 +474,9 @@ private static Mesh CreateHull(SlicedSubmesh[] meshes, int total, List<Triangle>

int totalTriangles = triangles.Count;

newMesh.subMeshCount = totalTriangles;
newMesh.Clear();

newMesh.subMeshCount = totalTriangles;
// fill the mesh structure
newMesh.vertices = newVertices;

Expand Down