Unity3D: Detailed explanation of Mesh cutting algorithm

Unity3D is a very popular game development engine that supports multiple platforms and multiple languages. In Unity3D, Mesh is the most commonly used 3D model representation method in games, which consists of a series of points, lines, and surfaces. In the game, we often need to perform some special operations on Mesh, such as cutting. At this time, we need to use the Mesh cutting algorithm. This article will introduce the principle and code implementation of the Mesh cutting algorithm in detail.

Yes, there is a game development exchange group here, I hope everyone can click in and share development experience together

1. Mesh cutting algorithm principle

The principle of the Mesh cutting algorithm is to cut a Mesh into multiple sub-Mesh, and these sub-Mesh can be operated independently, such as moving, rotating, scaling, etc. The Mesh cutting algorithm is widely used in games. For example, in shooting games, when a bullet hits an object, the object will be cut into multiple fragments, increasing the realism and fun of the game.

The implementation process of the Mesh cutting algorithm can be divided into the following steps:

1. Calculate the cutting plane

Cutting plane is an important parameter of Mesh cutting, which determines the direction and position of Mesh cutting. In Unity3D, the Plane class can be used to represent a plane, and its constructor can receive a point and a normal vector as parameters, indicating the position and direction of the plane. To facilitate calculation, we can normalize the normal vector of the cutting plane.

2. Calculate the cut point

Cutting point is another important parameter of Mesh cutting, which determines the position of Mesh cutting. When calculating the cutting point, we can intersect the cutting plane with each face of the Mesh to get all the cutting points. When intersecting, you can use the Raycast or Plane.Raycast method. If two adjacent faces are on the same side of the cutting plane, they will not produce a cutting point.

3. Generate a new Mesh

According to the cutting point, we can cut the original Mesh into multiple sub-Mesh, and each sub-Mesh is composed of a part of the original surface. For the convenience of operation, we can generate a new Mesh object for each sub-Mesh and add it to the scene.

4. Recalculate UV and normal vectors

Since the Mesh is cut into multiple sub-Mesh, each sub-Mesh needs to recalculate the UV and normal vector. In Unity3D, you can use the Mesh.RecalculateNormals and Mesh.RecalculateUV2 methods to calculate the normal vector and UV.

2. Mesh cutting algorithm code implementation

The following is a code implementation of a simple Mesh cutting algorithm:

public static void Cut(GameObject obj, Plane plane)
{
    Mesh mesh = obj.GetComponent<MeshFilter>().mesh;
    Vector3[] vertices = mesh.vertices;
    int[] triangles = mesh.triangles;

    List<Vector3> frontVertices = new List<Vector3>();
    List<Vector3> backVertices = new List<Vector3>();
    List<int> frontTriangles = new List<int>();
    List<int> backTriangles = new List<int>();

    for (int i = 0; i < triangles.Length; i += 3)
    {
        int i1 = triangles[i];
        int i2 = triangles[i + 1];
        int i3 = triangles[i + 2];

        Vector3 v1 = vertices[i1];
        Vector3 v2 = vertices[i2];
        Vector3 v3 = vertices[i3];

        bool front1 = plane.GetSide(v1);
        bool front2 = plane.GetSide(v2);
        bool front3 = plane.GetSide(v3);

        if (front1 && front2 && front3)
        {
            frontVertices.Add(v1);
            frontVertices.Add(v2);
            frontVertices.Add(v3);
            frontTriangles.Add(frontVertices.Count - 3);
            frontTriangles.Add(frontVertices.Count - 2);
            frontTriangles.Add(frontVertices.Count - 1);
        }
        else if (!front1 && !front2 && !front3)
        {
            backVertices.Add(v1);
            backVertices.Add(v2);
            backVertices.Add(v3);
            backTriangles.Add(backVertices.Count - 3);
            backTriangles.Add(backVertices.Count - 2);
            backTriangles.Add(backVertices.Count - 1);
        }
        else
        {
            Vector3 point1 = Vector3.zero;
            Vector3 point2 = Vector3.zero;
            Vector3 normal = plane.normal;

            if (front1)
            {
                point1 = v1;
                if (front2)
                {
                    point2 = v2;
                }
                else if (front3)
                {
                    point2 = v3;
                }
            }
            else if (front2)
            {
                point1 = v2;
                if (front1)
                {
                    point2 = v1;
                }
                else if (front3)
                {
                    point2 = v3;
                }
            }
            else if (front3)
            {
                point1 = v3;
                if (front1)
                {
                    point2 = v1;
                }
                else if (front2)
                {
                    point2 = v2;
                }
            }

            float t = (plane.distance - Vector3.Dot(normal, point1)) / Vector3.Dot(normal, point2 - point1);
            Vector3 cutPoint = point1 + t * (point2 - point1);

            frontVertices.Add(point1);
            frontVertices.Add(cutPoint);
            frontVertices.Add(v1 + t * (v2 - v1));

            frontVertices.Add(point2);
            frontVertices.Add(cutPoint);
            frontVertices.Add(v1 + t * (v3 - v1));

            backVertices.Add(cutPoint);
            backVertices.Add(point1);
            backVertices.Add(v1 + t * (v2 - v1));

            backVertices.Add(cutPoint);
            backVertices.Add(point2);
            backVertices.Add(v1 + t * (v3 - v1));

            if (front1)
            {
                frontTriangles.Add(frontVertices.Count - 6);
                frontTriangles.Add(frontVertices.Count - 5);
                frontTriangles.Add(frontVertices.Count - 4);

                backTriangles.Add(backVertices.Count - 6);
                backTriangles.Add(backVertices.Count - 5);
                backTriangles.Add(backVertices.Count - 4);
            }
            else
            {
                frontTriangles.Add(frontVertices.Count - 4);
                frontTriangles.Add(frontVertices.Count - 5);
                frontTriangles.Add(frontVertices.Count - 6);

                backTriangles.Add(backVertices.Count - 4);
                backTriangles.Add(backVertices.Count - 5);
                backTriangles.Add(backVertices.Count - 6);
            }

            if (front2)
            {
                frontTriangles.Add(frontVertices.Count - 5);
                frontTriangles.Add(frontVertices.Count - 2);
                frontTriangles.Add(frontVertices.Count - 4);

                backTriangles.Add(backVertices.Count - 5);
                backTriangles.Add(backVertices.Count - 2);
                backTriangles.Add(backVertices.Count - 4);
            }
            else
            {
                frontTriangles.Add(frontVertices.Count - 4);
                frontTriangles.Add(frontVertices.Count - 2);
                frontTriangles.Add(frontVertices.Count - 5);

                backTriangles.Add(backVertices.Count - 4);
                backTriangles.Add(backVertices.Count - 2);
                backTriangles.Add(backVertices.Count - 5);
            }

            if (front3)
            {
                frontTriangles.Add(frontVertices.Count - 6);
                frontTriangles.Add(frontVertices.Count - 4);
                frontTriangles.Add(frontVertices.Count - 3);

                backTriangles.Add(backVertices.Count - 6);
                backTriangles.Add(backVertices.Count - 4);
                backTriangles.Add(backVertices.Count - 3);
            }
            else
            {
                frontTriangles.Add(frontVertices.Count - 3);
                frontTriangles.Add(frontVertices.Count - 4);
                frontTriangles.Add(frontVertices.Count - 6);

                backTriangles.Add(backVertices.Count - 3);
                backTriangles.Add(backVertices.Count - 4);
                backTriangles.Add(backVertices.Count - 6);
            }
        }
    }

    Mesh frontMesh = new Mesh();
    frontMesh.vertices = frontVertices.ToArray();
    frontMesh.triangles = frontTriangles.ToArray();
    frontMesh.RecalculateNormals();
    frontMesh.RecalculateUV2();
    frontMesh.RecalculateBounds();

    Mesh backMesh = new Mesh();
    backMesh.vertices = backVertices.ToArray();
    backMesh.triangles = backTriangles.ToArray();
    backMesh.RecalculateNormals();
    backMesh.RecalculateUV2();
    backMesh.RecalculateBounds();

    GameObject frontObj = new GameObject("Front");
    frontObj.transform.SetParent(obj.transform.parent);
    frontObj.transform.localPosition = obj.transform.localPosition;
    frontObj.transform.localRotation = obj.transform.localRotation;
    frontObj.transform.localScale = obj.transform.localScale;
    frontObj.AddComponent<MeshFilter>().mesh = frontMesh;
    frontObj.AddComponent<MeshRenderer>().sharedMaterial = obj.GetComponent<MeshRenderer>().sharedMaterial;

    GameObject backObj = new GameObject("Back");
    backObj.transform.SetParent(obj.transform.parent);
    backObj.transform.localPosition = obj.transform.localPosition;
    backObj.transform.localRotation = obj.transform.localRotation;
    backObj.transform.localScale = obj.transform.localScale;
    backObj.AddComponent<MeshFilter>().mesh = backMesh;
    backObj.AddComponent<MeshRenderer>().sharedMaterial = obj.GetComponent<MeshRenderer>().sharedMaterial;

    Object.Destroy(obj);
}

In the above code, we first obtain the vertex and triangle information of the Mesh object, and then cut the Mesh into two sub-Mesh according to the cutting plane. When cutting, we use the Plane.GetSide method to determine which side of a point is on the cutting plane, and use the Plane.Raycast method to find the intersection point. Finally, we generated two new Mesh objects and added them to the scene.

3. Summary

Mesh cutting algorithm is one of the very important algorithms in game development. It can cut a Mesh into multiple sub-Mesh, making the game more realistic and interesting. In Unity3D, we can use the Plane class to represent a plane, and use the Mesh.RecalculateNormals and Mesh.RecalculateUV2 methods to recalculate the normal vector and UV. Through the introduction of this article, I believe that readers have mastered the principle and code implementation of the Mesh cutting algorithm.

Attachment: More teaching videos

Guess you like

Origin blog.csdn.net/weixin_49669470/article/details/130011645