Skip to content

Instantly share code, notes, and snippets.

@twobob
Forked from alexcmd/MeshCut.cs
Created September 7, 2018 08:28
Show Gist options
  • Save twobob/19a9c00dcccaf555a2d74403757ced77 to your computer and use it in GitHub Desktop.
Save twobob/19a9c00dcccaf555a2d74403757ced77 to your computer and use it in GitHub Desktop.

Revisions

  1. @alexcmd alexcmd created this gist Dec 29, 2015.
    653 changes: 653 additions & 0 deletions MeshCut.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,653 @@
    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]);

    }

    }

    }