Skip to content

Instantly share code, notes, and snippets.

@aunpyz
Forked from alexcmd/MeshCut.cs
Created August 9, 2018 18:08
Show Gist options
  • Save aunpyz/009881dfaaf41ce8577a4780cad88cc7 to your computer and use it in GitHub Desktop.
Save aunpyz/009881dfaaf41ce8577a4780cad88cc7 to your computer and use it in GitHub Desktop.
Mesh cut algorithm for Unity
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class MeshCut
{
private static Plane blade;
private static Transform victim_transform;
private static Mesh victim_mesh;
private static bool[] sides = new bool[3];
// Gathering values
private static List<int>[] left_Gather_subIndices = new List<int>[] { new List<int>(), new List<int>() };
private static List<int>[] right_Gather_subIndices = new List<int>[] { new List<int>(), new List<int>() };
private static List<Vector3>[] left_Gather_added_Points = new List<Vector3>[] { new List<Vector3>(), new List<Vector3>() };
private static List<Vector2>[] left_Gather_added_uvs = new List<Vector2>[] { new List<Vector2>(), new List<Vector2>() };
private static List<Vector3>[] left_Gather_added_normals = new List<Vector3>[] { new List<Vector3>(), new List<Vector3>() };
private static List<Vector3>[] right_Gather_added_Points = new List<Vector3>[] { new List<Vector3>(), new List<Vector3>() };
private static List<Vector2>[] right_Gather_added_uvs = new List<Vector2>[] { new List<Vector2>(), new List<Vector2>() };
private static List<Vector3>[] right_Gather_added_normals = new List<Vector3>[] { new List<Vector3>(), new List<Vector3>() };
// face cutting temps
private static Vector3 leftPoint1 = Vector3.zero;
private static Vector3 leftPoint2 = Vector3.zero;
private static Vector3 rightPoint1 = Vector3.zero;
private static Vector3 rightPoint2 = Vector3.zero;
private static Vector2 left_uv1 = Vector3.zero;
private static Vector2 left_uv2 = Vector3.zero;
private static Vector2 right_uv1 = Vector3.zero;
private static Vector2 right_uv2 = Vector3.zero;
private static Vector3 left_normal1 = Vector3.zero;
private static Vector3 left_normal2 = Vector3.zero;
private static Vector3 right_normal1 = Vector3.zero;
private static Vector3 right_normal2 = Vector3.zero;
// final arrays
private static List<int>[] left_Final_subIndices = new List<int>[] { new List<int>(), new List<int>() };
private static List<Vector3> left_Final_vertices = new List<Vector3>();
private static List<Vector3> left_Final_normals = new List<Vector3>();
private static List<Vector2> left_Final_uvs = new List<Vector2>();
private static List<int>[] right_Final_subIndices = new List<int>[] { new List<int>(), new List<int>() };
private static List<Vector3> right_Final_vertices = new List<Vector3>();
private static List<Vector3> right_Final_normals = new List<Vector3>();
private static List<Vector2> right_Final_uvs = new List<Vector2>();
// capping stuff
private static List<Vector3> createdVertexPoints = new List<Vector3>();
public static GameObject[] Cut(GameObject victim, Vector3 anchorPoint, Vector3 normalDirection, Material capMaterial)
{
victim_transform = victim.transform;
blade = new Plane(victim.transform.InverseTransformDirection(-normalDirection),
victim.transform.InverseTransformPoint(anchorPoint));
victim_mesh = victim.GetComponent<MeshFilter>().mesh;
victim_mesh.subMeshCount = 2;
ResetGatheringValues();
int p1 = 0;
int p2 = 0;
int p3 = 0;
sides = new bool[3];
int sub = 0;
int[] indices = victim_mesh.triangles;
int[] secondIndices = victim_mesh.GetIndices(1);
for (int i = 0; i < indices.Length; i += 3)
{
p1 = indices[i];
p2 = indices[i + 1];
p3 = indices[i + 2];
sides[0] = blade.GetSide(victim_mesh.vertices[p1]);
sides[1] = blade.GetSide(victim_mesh.vertices[p2]);
sides[2] = blade.GetSide(victim_mesh.vertices[p3]);
sub = 0;
for (int k = 0; k < secondIndices.Length; k++)
{
if (secondIndices[k] == p1)
{
sub = 1;
break;
}
}
if (sides[0] == sides[1] && sides[0] == sides[2])
{ // whole face
if (sides[0])
{ // left side
left_Gather_subIndices[sub].Add(p1);
left_Gather_subIndices[sub].Add(p2);
left_Gather_subIndices[sub].Add(p3);
}
else
{
right_Gather_subIndices[sub].Add(p1);
right_Gather_subIndices[sub].Add(p2);
right_Gather_subIndices[sub].Add(p3);
}
}
else
{ // cut face
ResetFaceCuttingTemps();
Cut_this_Face(sub, p1, p2, p3);
}
}
// set final arrays
ResetFinalArrays();
SetFinalArrays_withOriginals();
AddNewTriangles_toFinalArrays();
MakeCaps();
Mesh left_HalfMesh = new Mesh();
left_HalfMesh.name = "Split Mesh Left";
left_HalfMesh.vertices = left_Final_vertices.ToArray();
left_HalfMesh.subMeshCount = 2;
left_HalfMesh.SetIndices(left_Final_subIndices[0].ToArray(), MeshTopology.Triangles, 0);
left_HalfMesh.SetIndices(left_Final_subIndices[1].ToArray(), MeshTopology.Triangles, 1);
left_HalfMesh.normals = left_Final_normals.ToArray();
left_HalfMesh.uv = left_Final_uvs.ToArray();
Mesh right_HalfMesh = new Mesh();
right_HalfMesh.name = "Split Mesh Right";
right_HalfMesh.vertices = right_Final_vertices.ToArray();
right_HalfMesh.subMeshCount = 2;
right_HalfMesh.SetIndices(right_Final_subIndices[0].ToArray(), MeshTopology.Triangles, 0);
right_HalfMesh.SetIndices(right_Final_subIndices[1].ToArray(), MeshTopology.Triangles, 1);
right_HalfMesh.normals = right_Final_normals.ToArray();
right_HalfMesh.uv = right_Final_uvs.ToArray();
victim.name = "leftSide";
victim.GetComponent<MeshFilter>().mesh = left_HalfMesh;
Material[] mats = new Material[] { victim.GetComponent<MeshRenderer>().material, capMaterial };
GameObject leftSideObj = victim;
GameObject rightSideObj = new GameObject("rightSide", typeof(MeshFilter), typeof(MeshRenderer));
rightSideObj.transform.position = victim_transform.position;
rightSideObj.transform.rotation = victim_transform.rotation;
rightSideObj.GetComponent<MeshFilter>().mesh = right_HalfMesh;
leftSideObj.GetComponent<MeshRenderer>().materials = mats;
rightSideObj.GetComponent<MeshRenderer>().materials = mats;
return new GameObject[] { leftSideObj, rightSideObj };
}
static void ResetGatheringValues()
{
left_Gather_subIndices[0].Clear();
left_Gather_subIndices[1].Clear();
left_Gather_added_Points[0].Clear();
left_Gather_added_Points[1].Clear();
left_Gather_added_uvs[0].Clear();
left_Gather_added_uvs[1].Clear();
left_Gather_added_normals[0].Clear();
left_Gather_added_normals[1].Clear();
right_Gather_subIndices[0].Clear();
right_Gather_subIndices[1].Clear();
right_Gather_added_Points[0].Clear();
right_Gather_added_Points[1].Clear();
right_Gather_added_uvs[0].Clear();
right_Gather_added_uvs[1].Clear();
right_Gather_added_normals[0].Clear();
right_Gather_added_normals[1].Clear();
createdVertexPoints.Clear();
}
static void ResetFaceCuttingTemps()
{
leftPoint1 = Vector3.zero;
leftPoint2 = Vector3.zero;
rightPoint1 = Vector3.zero;
rightPoint2 = Vector3.zero;
left_uv1 = Vector3.zero;
left_uv2 = Vector3.zero;
right_uv1 = Vector3.zero;
right_uv2 = Vector3.zero;
left_normal1 = Vector3.zero;
left_normal2 = Vector3.zero;
right_normal1 = Vector3.zero;
right_normal2 = Vector3.zero;
}
static void Cut_this_Face(int submesh, int index1, int index2, int index3)
{
int p = index1;
for (int side = 0; side < 3; side++)
{
switch (side)
{
case 0:
p = index1;
break;
case 1:
p = index2;
break;
case 2:
p = index3;
break;
}
if (sides[side])
{
if (leftPoint1 == Vector3.zero)
{
leftPoint1 = victim_mesh.vertices[p];
leftPoint2 = leftPoint1;
left_uv1 = victim_mesh.uv[p];
left_uv2 = left_uv1;
left_normal1 = victim_mesh.normals[p];
left_normal2 = left_normal1;
}
else
{
leftPoint2 = victim_mesh.vertices[p];
left_uv2 = victim_mesh.uv[p];
left_normal2 = victim_mesh.normals[p];
}
}
else
{
if (rightPoint1 == Vector3.zero)
{
rightPoint1 = victim_mesh.vertices[p];
rightPoint2 = rightPoint1;
right_uv1 = victim_mesh.uv[p];
right_uv2 = right_uv1;
right_normal1 = victim_mesh.normals[p];
right_normal2 = right_normal1;
}
else
{
rightPoint2 = victim_mesh.vertices[p];
right_uv2 = victim_mesh.uv[p];
right_normal2 = victim_mesh.normals[p];
}
}
}
float normalizedDistance = 0.0f;
float distance = 0;
blade.Raycast(new Ray(leftPoint1, (rightPoint1 - leftPoint1).normalized), out distance);
normalizedDistance = distance / (rightPoint1 - leftPoint1).magnitude;
Vector3 newVertex1 = Vector3.Lerp(leftPoint1, rightPoint1, normalizedDistance);
Vector2 newUv1 = Vector2.Lerp(left_uv1, right_uv1, normalizedDistance);
Vector3 newNormal1 = Vector3.Lerp(left_normal1, right_normal1, normalizedDistance);
createdVertexPoints.Add(newVertex1);
blade.Raycast(new Ray(leftPoint2, (rightPoint2 - leftPoint2).normalized), out distance);
normalizedDistance = distance / (rightPoint2 - leftPoint2).magnitude;
Vector3 newVertex2 = Vector3.Lerp(leftPoint2, rightPoint2, normalizedDistance);
Vector2 newUv2 = Vector2.Lerp(left_uv2, right_uv2, normalizedDistance);
Vector3 newNormal2 = Vector3.Lerp(left_normal2, right_normal2, normalizedDistance);
createdVertexPoints.Add(newVertex2);
// first triangle
Add_Left_triangle(submesh, newNormal1, new Vector3[] { leftPoint1, newVertex1, newVertex2 },
new Vector2[] { left_uv1, newUv1, newUv2 },
new Vector3[] { left_normal1, newNormal1, newNormal2 });
// second triangle
Add_Left_triangle(submesh, newNormal2, new Vector3[] { leftPoint1, leftPoint2, newVertex2 },
new Vector2[] { left_uv1, left_uv2, newUv2 },
new Vector3[] { left_normal1, left_normal2, newNormal2 });
// first triangle
Add_Right_triangle(submesh, newNormal1, new Vector3[] { rightPoint1, newVertex1, newVertex2 },
new Vector2[] { right_uv1, newUv1, newUv2 },
new Vector3[] { right_normal1, newNormal1, newNormal2 });
// second triangle
Add_Right_triangle(submesh, newNormal2, new Vector3[] { rightPoint1, rightPoint2, newVertex2 },
new Vector2[] { right_uv1, right_uv2, newUv2 },
new Vector3[] { right_normal1, right_normal2, newNormal2 });
}
static void Add_Left_triangle(int submesh, Vector3 faceNormal, Vector3[] points, Vector2[] uvs, Vector3[] normals)
{
int p1 = 0;
int p2 = 1;
int p3 = 2;
Vector3 calculated_normal = Vector3.Cross((points[1] - points[0]).normalized, (points[2] - points[0]).normalized);
if (Vector3.Dot(calculated_normal, faceNormal) < 0)
{
p1 = 2;
p2 = 1;
p3 = 0;
}
left_Gather_added_Points[submesh].Add(points[p1]);
left_Gather_added_Points[submesh].Add(points[p2]);
left_Gather_added_Points[submesh].Add(points[p3]);
left_Gather_added_uvs[submesh].Add(uvs[p1]);
left_Gather_added_uvs[submesh].Add(uvs[p2]);
left_Gather_added_uvs[submesh].Add(uvs[p3]);
left_Gather_added_normals[submesh].Add(normals[p1]);
left_Gather_added_normals[submesh].Add(normals[p2]);
left_Gather_added_normals[submesh].Add(normals[p3]);
}
static void Add_Right_triangle(int submesh, Vector3 faceNormal, Vector3[] points, Vector2[] uvs, Vector3[] normals)
{
int p1 = 0;
int p2 = 1;
int p3 = 2;
Vector3 calculated_normal = Vector3.Cross((points[1] - points[0]).normalized, (points[2] - points[0]).normalized);
if (Vector3.Dot(calculated_normal, faceNormal) < 0)
{
p1 = 2;
p2 = 1;
p3 = 0;
}
right_Gather_added_Points[submesh].Add(points[p1]);
right_Gather_added_Points[submesh].Add(points[p2]);
right_Gather_added_Points[submesh].Add(points[p3]);
right_Gather_added_uvs[submesh].Add(uvs[p1]);
right_Gather_added_uvs[submesh].Add(uvs[p2]);
right_Gather_added_uvs[submesh].Add(uvs[p3]);
right_Gather_added_normals[submesh].Add(normals[p1]);
right_Gather_added_normals[submesh].Add(normals[p2]);
right_Gather_added_normals[submesh].Add(normals[p3]);
}
static void ResetFinalArrays()
{
left_Final_subIndices[0].Clear();
left_Final_subIndices[1].Clear();
left_Final_vertices.Clear();
left_Final_normals.Clear();
left_Final_uvs.Clear();
right_Final_subIndices[0].Clear();
right_Final_subIndices[1].Clear();
right_Final_vertices.Clear();
right_Final_normals.Clear();
right_Final_uvs.Clear();
}
static void SetFinalArrays_withOriginals()
{
int p = 0;
for (int submesh = 0; submesh < 2; submesh++)
{
for (int i = 0; i < left_Gather_subIndices[submesh].Count; i++)
{
p = left_Gather_subIndices[submesh][i];
left_Final_vertices.Add(victim_mesh.vertices[p]);
left_Final_subIndices[submesh].Add(left_Final_vertices.Count - 1);
left_Final_normals.Add(victim_mesh.normals[p]);
left_Final_uvs.Add(victim_mesh.uv[p]);
}
for (int i = 0; i < right_Gather_subIndices[submesh].Count; i++)
{
p = right_Gather_subIndices[submesh][i];
right_Final_vertices.Add(victim_mesh.vertices[p]);
right_Final_subIndices[submesh].Add(right_Final_vertices.Count - 1);
right_Final_normals.Add(victim_mesh.normals[p]);
right_Final_uvs.Add(victim_mesh.uv[p]);
}
}
}
static void AddNewTriangles_toFinalArrays()
{
for (int submesh = 0; submesh < 2; submesh++)
{
int count = left_Final_vertices.Count;
// add the new ones
for (int i = 0; i < left_Gather_added_Points[submesh].Count; i++)
{
left_Final_vertices.Add(left_Gather_added_Points[submesh][i]);
left_Final_subIndices[submesh].Add(i + count);
left_Final_uvs.Add(left_Gather_added_uvs[submesh][i]);
left_Final_normals.Add(left_Gather_added_normals[submesh][i]);
}
count = right_Final_vertices.Count;
for (int i = 0; i < right_Gather_added_Points[submesh].Count; i++)
{
right_Final_vertices.Add(right_Gather_added_Points[submesh][i]);
right_Final_subIndices[submesh].Add(i + count);
right_Final_uvs.Add(right_Gather_added_uvs[submesh][i]);
right_Final_normals.Add(right_Gather_added_normals[submesh][i]);
}
}
}
private static List<Vector3> capVertTracker = new List<Vector3>();
private static List<Vector3> capVertpolygon = new List<Vector3>();
static void MakeCaps()
{
capVertTracker.Clear();
for (int i = 0; i < createdVertexPoints.Count; i++)
if (!capVertTracker.Contains(createdVertexPoints[i]))
{
capVertpolygon.Clear();
capVertpolygon.Add(createdVertexPoints[i]);
capVertpolygon.Add(createdVertexPoints[i + 1]);
capVertTracker.Add(createdVertexPoints[i]);
capVertTracker.Add(createdVertexPoints[i + 1]);
bool isDone = false;
while (!isDone)
{
isDone = true;
for (int k = 0; k < createdVertexPoints.Count; k += 2)
{ // go through the pairs
if (createdVertexPoints[k] == capVertpolygon[capVertpolygon.Count - 1] && !capVertTracker.Contains(createdVertexPoints[k + 1]))
{ // if so add the other
isDone = false;
capVertpolygon.Add(createdVertexPoints[k + 1]);
capVertTracker.Add(createdVertexPoints[k + 1]);
}
else if (createdVertexPoints[k + 1] == capVertpolygon[capVertpolygon.Count - 1] && !capVertTracker.Contains(createdVertexPoints[k]))
{// if so add the other
isDone = false;
capVertpolygon.Add(createdVertexPoints[k]);
capVertTracker.Add(createdVertexPoints[k]);
}
}
}
FillCap(capVertpolygon);
}
}
static void FillCap(List<Vector3> vertices)
{
List<int> triangles = new List<int>();
List<Vector2> uvs = new List<Vector2>();
List<Vector3> normals = new List<Vector3>();
Vector3 center = Vector3.zero;
foreach (Vector3 point in vertices)
center += point;
center = center / vertices.Count;
Vector3 upward = Vector3.zero;
// 90 degree turn
upward.x = blade.normal.y;
upward.y = -blade.normal.x;
upward.z = blade.normal.z;
Vector3 left = Vector3.Cross(blade.normal, upward);
Vector3 displacement = Vector3.zero;
Vector3 relativePosition = Vector3.zero;
for (int i = 0; i < vertices.Count; i++)
{
displacement = vertices[i] - center;
relativePosition = Vector3.zero;
relativePosition.x = 0.5f + Vector3.Dot(displacement, left);
relativePosition.y = 0.5f + Vector3.Dot(displacement, upward);
relativePosition.z = 0.5f + Vector3.Dot(displacement, blade.normal);
uvs.Add(new Vector2(relativePosition.x, relativePosition.y));
normals.Add(blade.normal);
}
vertices.Add(center);
normals.Add(blade.normal);
uvs.Add(new Vector2(0.5f, 0.5f));
Vector3 calculated_normal = Vector3.zero;
int otherIndex = 0;
for (int i = 0; i < vertices.Count; i++)
{
otherIndex = (i + 1) % (vertices.Count - 1);
calculated_normal = Vector3.Cross((vertices[otherIndex] - vertices[i]).normalized,
(vertices[vertices.Count - 1] - vertices[i]).normalized);
if (Vector3.Dot(calculated_normal, blade.normal) < 0)
{
triangles.Add(vertices.Count - 1);
triangles.Add(otherIndex);
triangles.Add(i);
}
else
{
triangles.Add(i);
triangles.Add(otherIndex);
triangles.Add(vertices.Count - 1);
}
}
int index = 0;
for (int i = 0; i < triangles.Count; i++)
{
index = triangles[i];
right_Final_vertices.Add(vertices[index]);
right_Final_subIndices[1].Add(right_Final_vertices.Count - 1);
right_Final_normals.Add(normals[index]);
right_Final_uvs.Add(uvs[index]);
}
for (int i = 0; i < normals.Count; i++)
{
normals[i] = -normals[i];
}
int temp1, temp2;
for (int i = 0; i < triangles.Count; i += 3)
{
temp1 = triangles[i + 2];
temp2 = triangles[i];
triangles[i] = temp1;
triangles[i + 2] = temp2;
}
for (int i = 0; i < triangles.Count; i++)
{
index = triangles[i];
left_Final_vertices.Add(vertices[index]);
left_Final_subIndices[1].Add(left_Final_vertices.Count - 1);
left_Final_normals.Add(normals[index]);
left_Final_uvs.Add(uvs[index]);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment