Slicing triangles of a mesh

I am making a mesh slicer (slice mesh once using a plane, aka point and direction vector).

So far I can sort triangles to left and right of the cut and generate meshes from that. Now I come to the point where I need to actually cut the triangles which are at the cutting plane.

I can calculate the 2 new vertices where the line cuts the triangle, but I cannot wrap my head around how to make 3 triangles out of 1 per kind of cut.

If someone can help that would be really appreciated! Links, code examples, pseudocode all are welcome.
I already tried chatGTP and check out multiple open-source projects, but just cannot wrap my head around it.

Code:

public void BreakAtPosition(Vector3 position, Vector3 normal)
        {
            int[] originalTris = ogMesh.triangles;
            Vector2[] originalUVs = ogMesh.uv;
            Vector3[] originalNormals = ogMesh.normals;

            NativeList<Vector3> rightNormals = new NativeList<Vector3>(newVertices.Length, Allocator.Temp);
            NativeList<Vector3> leftNormals = new NativeList<Vector3>(newVertices.Length, Allocator.Temp);
            NativeList<Vector3> rightVerts = new NativeList<Vector3>(newVertices.Length, Allocator.Temp);
            NativeList<Vector3> leftVerts = new NativeList<Vector3>(newVertices.Length, Allocator.Temp);
            NativeList<Vector2> rightUVs = new NativeList<Vector2>(newVertices.Length, Allocator.Temp);
            NativeList<Vector2> leftUVs = new NativeList<Vector2>(newVertices.Length, Allocator.Temp);
            NativeList<int> rightIndices = new NativeList<int>(newVertices.Length, Allocator.Temp);
            NativeList<int> leftIndices = new NativeList<int>(newVertices.Length, Allocator.Temp);
            NativeList<int> rightTris = new NativeList<int>(originalTris.Length, Allocator.Temp);
            NativeList<int> leftTris = new NativeList<int>(originalTris.Length, Allocator.Temp);

            for (int i = 0; i < originalTris.Length; i += 3)
            {
                int i1 = originalTris[i];
                int i2 = originalTris[i + 1];
                int i3 = originalTris[i + 2];
                Vector3 v1 = newVertices[originalTris[i]];
                Vector3 v2 = newVertices[originalTris[i + 1]];
                Vector3 v3 = newVertices[originalTris[i + 2]];
                Vector2 uv1 = originalUVs[originalTris[i]];
                Vector2 uv2 = originalUVs[originalTris[i + 1]];
                Vector2 uv3 = originalUVs[originalTris[i + 2]];
                Vector3 n1 = originalNormals[originalTris[i]];
                Vector3 n2 = originalNormals[originalTris[i + 1]];
                Vector3 n3 = originalNormals[originalTris[i + 2]];

                float vert1Side = MeshTools.GetVertexSide(v1, position, normal);
                float vert2Side = MeshTools.GetVertexSide(v2, position, normal);
                float vert3Side = MeshTools.GetVertexSide(v3, position, normal);



                if (vert1Side < 0 && vert2Side < 0 && vert3Side < 0) //Verts to left
                {
                    leftVerts.Add(v3);
                    leftVerts.Add(v2);
                    leftVerts.Add(v1);

                    leftIndices.Add(i1);
                    leftIndices.Add(i2);
                    leftIndices.Add(i3);

                    leftNormals.Add(n1);
                    leftNormals.Add(n2);
                    leftNormals.Add(n3);

                    leftUVs.Add(uv3);
                    leftUVs.Add(uv2);
                    leftUVs.Add(uv1);

                    leftTris.Add(leftVerts.Length - 1);
                    leftTris.Add(leftVerts.Length - 2);
                    leftTris.Add(leftVerts.Length - 3);
                }
                else if (vert1Side > 0 && vert2Side > 0 && vert3Side > 0) //Verts to right
                {
                    rightVerts.Add(v3);
                    rightVerts.Add(v2);
                    rightVerts.Add(v1);

                    rightIndices.Add(i1);
                    rightIndices.Add(i2);
                    rightIndices.Add(i3);

                    rightNormals.Add(n1);
                    rightNormals.Add(n2);
                    rightNormals.Add(n3);

                    rightUVs.Add(uv3);
                    rightUVs.Add(uv2);
                    rightUVs.Add(uv1);

                    rightTris.Add(rightVerts.Length - 1);
                    rightTris.Add(rightVerts.Length - 2);
                    rightTris.Add(rightVerts.Length - 3);
                }
                else //Vertes split
                {
                    //v1, v2, v3

                    // Calculate intersection points
                    float3[] intersectionPoints = new float3[2];
                    int numIntersections = 0;

                    if (vert1Side * vert2Side < 0)
                    {
                        MeshTools.IntersectPlane(v1, v2, position, normal, out intersectionPoints[numIntersections]);
                        numIntersections++;
                    }
                    if (vert2Side * vert3Side < 0)
                    {
                        MeshTools.IntersectPlane(v2, v3, position, normal, out intersectionPoints[numIntersections]);
                        numIntersections++;
                    }
                    if (vert3Side * vert1Side < 0)
                    {
                        MeshTools.IntersectPlane(v3, v1, position, normal, out intersectionPoints[numIntersections]);
                        numIntersections++;
                    }

                    if (numIntersections != 2)
                    {
                        Debug.LogError("Unexpected number of intersection points: " + numIntersections);
                        return;
                    }

                    Vector3 v4 = intersectionPoints[0];
                    Vector3 v5 = intersectionPoints[1];
                  
                    //TODO generate triangles
                }
            }

Each vertice point: All three of each triangle, will connect to another triangle in an enclosed mesh. Say, any three given vertices each connect to another triangle if the mesh is fully enclosed. When you cut a triangle in half you should be producing one triangle and one quad, quad can be broken into two triangles for one cut three triangle result.

Think! I have a right angle triangle, I slice 20% down from the hypotenuse, below the intersect I have a rhombus above I have a triangle.

If I intersect from corner to corner vert to vert I only get two triangles.
For the three triangle result from one cut, you do not cut from vert High corner to opposite corner. You slice make a quad below a triad above.

instruction manual
basically grab two verts of your tris get half between them, (average), add a new vert, at position here and symmetrical on same point between the other two verts of this triangle, then divide the quad into two triangles.

hhrab

Peace Pro

the only
AM

I get the theory, and I can also draw it out, but I just cannot get it working in my code.

Here is a bit where I tried to get it working, but now the edges are just a mess of triangles.

else //Vertes split
                {
                    //v1, v2, v3

                    // Calculate intersection points
                    float3[] intersectionPoints = new float3[2];
                    int numIntersections = 0;

                    if (vert1Side * vert2Side < 0)
                    {
                        MeshTools.IntersectPlane(v1, v2, position, normal, out intersectionPoints[numIntersections]);
                        numIntersections++;
                    }
                    if (vert2Side * vert3Side < 0)
                    {
                        MeshTools.IntersectPlane(v2, v3, position, normal, out intersectionPoints[numIntersections]);
                        numIntersections++;
                    }
                    if (vert3Side * vert1Side < 0)
                    {
                        MeshTools.IntersectPlane(v3, v1, position, normal, out intersectionPoints[numIntersections]);
                        numIntersections++;
                    }

                    if (numIntersections != 2)
                    {
                        Debug.LogError("Unexpected number of intersection points: " + numIntersections);
                        return;
                    }

                    Vector3 v4 = intersectionPoints[0];
                    Vector3 v5 = intersectionPoints[1];

                    if (vert1Side < 0 && vert2Side < 0)
                    {
                        leftVerts.Add(v1);
                        leftVerts.Add(v5);
                        leftVerts.Add(v4);

                        leftNormals.Add(n1);
                        leftNormals.Add(n2);
                        leftNormals.Add(n3);

                        leftUVs.Add(uv3);
                        leftUVs.Add(uv2);
                        leftUVs.Add(uv1);

                        leftTris.Add(leftVerts.Length - 1);
                        leftTris.Add(leftVerts.Length - 2);
                        leftTris.Add(leftVerts.Length - 3);

                        leftVerts.Add(v1);
                        leftVerts.Add(v4);
                        leftVerts.Add(v2);

                        leftNormals.Add(n1);
                        leftNormals.Add(n2);
                        leftNormals.Add(n3);

                        leftUVs.Add(uv3);
                        leftUVs.Add(uv2);
                        leftUVs.Add(uv1);

                        leftTris.Add(leftVerts.Length - 1);
                        leftTris.Add(leftVerts.Length - 2);
                        leftTris.Add(leftVerts.Length - 3);

                        rightVerts.Add(v4);
                        rightVerts.Add(v5);
                        rightVerts.Add(v3);

                        rightNormals.Add(n1);
                        rightNormals.Add(n2);
                        rightNormals.Add(n3);

                        rightUVs.Add(uv3);
                        rightUVs.Add(uv2);
                        rightUVs.Add(uv1);

                        rightTris.Add(rightVerts.Length - 1);
                        rightTris.Add(rightVerts.Length - 2);
                        rightTris.Add(rightVerts.Length - 3);
                    }
                    if (vert1Side < 0 && vert3Side < 0)
                    {
                        leftVerts.Add(v5);
                        leftVerts.Add(v4);
                        leftVerts.Add(v1);

                        leftNormals.Add(n1);
                        leftNormals.Add(n2);
                        leftNormals.Add(n3);

                        leftUVs.Add(uv3);
                        leftUVs.Add(uv2);
                        leftUVs.Add(uv1);

                        leftTris.Add(leftVerts.Length - 1);
                        leftTris.Add(leftVerts.Length - 2);
                        leftTris.Add(leftVerts.Length - 3);

                        leftVerts.Add(v3);
                        leftVerts.Add(v5);
                        leftVerts.Add(v1);

                        leftNormals.Add(n1);
                        leftNormals.Add(n2);
                        leftNormals.Add(n3);

                        leftUVs.Add(uv3);
                        leftUVs.Add(uv2);
                        leftUVs.Add(uv1);

                        leftTris.Add(leftVerts.Length - 1);
                        leftTris.Add(leftVerts.Length - 2);
                        leftTris.Add(leftVerts.Length - 3);

                        rightVerts.Add(v5);
                        rightVerts.Add(v2);
                        rightVerts.Add(v4);

                        rightNormals.Add(n1);
                        rightNormals.Add(n2);
                        rightNormals.Add(n3);

                        rightUVs.Add(uv3);
                        rightUVs.Add(uv2);
                        rightUVs.Add(uv1);

                        rightTris.Add(rightVerts.Length - 1);
                        rightTris.Add(rightVerts.Length - 2);
                        rightTris.Add(rightVerts.Length - 3);
                    }
                    else
                    {
                        leftVerts.Add(v5);
                        leftVerts.Add(v4);
                        leftVerts.Add(v1);

                        leftNormals.Add(n1);
                        leftNormals.Add(n2);
                        leftNormals.Add(n3);

                        leftUVs.Add(uv3);
                        leftUVs.Add(uv2);
                        leftUVs.Add(uv1);

                        leftTris.Add(leftVerts.Length - 1);
                        leftTris.Add(leftVerts.Length - 2);
                        leftTris.Add(leftVerts.Length - 3);

                        rightVerts.Add(v3);
                        rightVerts.Add(v4);
                        rightVerts.Add(v5);

                        rightNormals.Add(n1);
                        rightNormals.Add(n2);
                        rightNormals.Add(n3);

                        rightUVs.Add(uv3);
                        rightUVs.Add(uv2);
                        rightUVs.Add(uv1);

                        rightTris.Add(rightVerts.Length - 1);
                        rightTris.Add(rightVerts.Length - 2);
                        rightTris.Add(rightVerts.Length - 3);

                        rightVerts.Add(v3);
                        rightVerts.Add(v2);
                        rightVerts.Add(v4);

                        rightNormals.Add(n1);
                        rightNormals.Add(n2);
                        rightNormals.Add(n3);

                        rightUVs.Add(uv3);
                        rightUVs.Add(uv2);
                        rightUVs.Add(uv1);

                        rightTris.Add(rightVerts.Length - 1);
                        rightTris.Add(rightVerts.Length - 2);
                        rightTris.Add(rightVerts.Length - 3);
                    }

                    //TODO generate triangles
                }
            }

Edit: here is my intersection code:

public static void IntersectPlane(in float3 v1, in float3 v2, in float3 position, in float3 normal, out float3 result)
        {
            float3 dir = v2 - v1;
            float dist = math.dot(position - v1, normal) / math.dot(dir, normal);
            result = v1 + dir * dist;
        }

You might actually be wanting the final result 4 triangles from 1 triangle true subdivision.

What is the benefit of 4 tris?
Again, I understand the theory and I can draw it out, I just cannot seem to get it working in code

Update:
I have most of the logic down, but getting stuck on the part of getting the new triangles to display. It works fine on a cylinder, but not on a cube. I use the default cube and it works on all faces except one.

Code tested:

if (vert1Side < 0 && vert3Side < 0)
                    {
                        leftVerts.Add(v5);
                        leftVerts.Add(v4);
                        leftVerts.Add(v1);

                        leftNormals.Add(n1);
                        leftNormals.Add(n2);
                        leftNormals.Add(n3);

                        leftUVs.Add(uv3);
                        leftUVs.Add(uv2);
                        leftUVs.Add(uv1);

                        leftTris.Add(leftVerts.Length - 1);
                        leftTris.Add(leftVerts.Length - 2);
                        leftTris.Add(leftVerts.Length - 3);

                        leftVerts.Add(v3);
                        leftVerts.Add(v5);
                        leftVerts.Add(v1);

                        leftNormals.Add(n1);
                        leftNormals.Add(n2);
                        leftNormals.Add(n3);

                        leftUVs.Add(uv3);
                        leftUVs.Add(uv2);
                        leftUVs.Add(uv1);

                        leftTris.Add(leftVerts.Length - 1);
                        leftTris.Add(leftVerts.Length - 2);
                        leftTris.Add(leftVerts.Length - 3);

                        rightVerts.Add(v5);
                        rightVerts.Add(v2);
                        rightVerts.Add(v4);

                        rightNormals.Add(n1);
                        rightNormals.Add(n2);
                        rightNormals.Add(n3);

                        rightUVs.Add(uv3);
                        rightUVs.Add(uv2);
                        rightUVs.Add(uv1);

                        rightTris.Add(rightVerts.Length - 1);
                        rightTris.Add(rightVerts.Length - 2);
                        rightTris.Add(rightVerts.Length - 3);
                    }

Seems that the 1 triangle gives different vertex sides (like it’s on the other wise of the plane)
Will continue experimenting tomorrow. If someone has any tips let me know

Well, as you said yourself the plain logic is usually straight forward. However common issues are if vertices or even a whole edge lies in the cutting plane itself. Those can be quite annoying edge cases. If an edge or vertex is exactly on the plane it would not be necessary to split the edge but just to duplicate it, one for each side.

Your approach is quite confusing and hard to follow. Especially since you handle all the vertex attributes manually. Have you tried nudging your cutting plane a bit to see if it makes a difference? For debugging it’s useful if you can do the cutting every update to get live feedback. A few years ago I made a small script that “only” creates a wireframe model of a mesh that is cut by a single cutting plane (script on DB, animated gif in case UA is gone some day). Of course this ignores the other vertex attributes and doesn’t really create actual triangles (only edges). However the overall approach may be useful.

It works. I had to also check if 2 verts are to the right of the plane and not just left. Not the most optimized, but it works :stuck_out_tongue: