關於3d模型任意切割的思路

3d模型的任意切割一直是遊戲開發裏的一個很大的問題,主要的問題是在切面的紋理上,然而如果是單純的切面片則並不存在切面紋理的問題,關於切面紋理的解決方案在下一篇文章中探討,總之本文權當研究吧。先看看我切出來的效果


下面先看看切出平整切痕的原理吧,本文需要圖形學的基礎紮實。

如上圖所示,當切割模型時,對於切面上的三角面,無非是如圖中3種情況(正好切在三角形的某個頂點上幾乎不可能,不過也可以考慮在內,這裏就不畫出來了),所以每個三角形正好被切到的時候,其自身內部應該生成新的頂點(圖中-1,-2點)。生成處新的頂點之後,我們需要將原來的一個三角形重新分割爲如圖綠色的數字標誌的三個三角形,也就是原來一個三角形被分爲三個三角形。

所以我在代碼中做的,就是將正好被切割的三角形重新劃分爲三個三角形。代碼如下

/*
 * @authors: liangjian
 * @desc:	
*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class ClipMesh : MonoBehaviour {

	// Use this for initialization
	void Start () {
        MeshFilter mf = this.gameObject.GetComponent<MeshFilter>();

        //頂點數組轉頂點容器
        List<Vector3> verticeList = new List<Vector3>();
        int verticeCount = mf.mesh.vertices.Length;
        for (int verticeIndex = 0; verticeIndex < verticeCount; ++verticeIndex)
        {
            verticeList.Add(mf.mesh.vertices[verticeIndex]);
        }
        //三角形數組轉三角形容器
        List<int> triangleList = new List<int>();
        int triangleCount = mf.mesh.triangles.Length;
        for (int triangleIndex = 0; triangleIndex < triangleCount; ++triangleIndex)
        {
            triangleList.Add(mf.mesh.triangles[triangleIndex]);
        }
        //uv座標數組轉uv座標容器
        List<Vector2> uvList = new List<Vector2>();
        int uvCount = mf.mesh.uv.Length;
        for (int uvIndex = 0; uvIndex < uvCount; ++uvIndex)
        {
            uvList.Add(mf.mesh.uv[uvIndex]);
        }
        //頂點顏色數組轉頂點顏色容器
        List<Vector3> normalList = new List<Vector3>();
        int normalCount = mf.mesh.normals.Length;
        for (int normalIndex = 0; normalIndex < normalCount; ++normalIndex)
        {
            normalList.Add(mf.mesh.normals[normalIndex]);
        }

        //檢查每個三角面,是否存在兩個頂點連接正好在直線上
        for (int triangleIndex = 0; triangleIndex < triangleList.Count;)
        {
            int trianglePoint0 = triangleList[triangleIndex];
            int trianglePoint1 = triangleList[triangleIndex + 1];
            int trianglePoint2 = triangleList[triangleIndex + 2];
            
            Vector3 point0 = verticeList[trianglePoint0];
            Vector3 point1 = verticeList[trianglePoint1];
            Vector3 point2 = verticeList[trianglePoint2];

            float planeY = 0.3f;
            //0-1,1-2相連線段被切割
            if ((point0.y - planeY)* (point1.y - planeY) < 0 && (point1.y - planeY) * (point2.y - planeY) < 0)
            {
                //截斷0-1之間的頂點
                float k01 = (point1.y - point0.y) / (planeY - point0.y);
                float newPointX01 = (point1.x - point0.x) / k01 + point0.x;
                float newPointZ01 = (point1.z - point0.z) / k01 + point0.z;
                Vector3 newPoint0_1 = new Vector3(newPointX01, planeY, newPointZ01);
                verticeList.Add(newPoint0_1);
                //uv
                if(uvList.Count > 0)
                {
                    Vector2 uv0 = uvList[trianglePoint0];
                    Vector2 uv1 = uvList[trianglePoint1];
                    float newUV_x = (uv1.x - uv0.x) / k01 + uv0.x;
                    float newUV_y = (uv1.y - uv0.y) / k01 + uv0.y;
                    uvList.Add(new Vector2(newUV_x, newUV_y));
                }
                //法向量
                Vector3 normalX0 = normalList[trianglePoint0];
                Vector3 normalX1 = normalList[trianglePoint1];
                Vector3 normalX2 = normalList[trianglePoint2];
                float newNoramlX01 = (normalX1.x - normalX0.x) / k01 + normalX0.x;
                float newNoramlY01 = (normalX1.y - normalX0.y) / k01 + normalX0.y;
                float newNoramlZ01 = (normalX1.z - normalX0.z) / k01 + normalX0.z;
                normalList.Add(new Vector3(newNoramlX01, newNoramlY01, newNoramlZ01));
                //截斷1-2之間的頂點
                float k12 = (point2.y - point1.y) / (planeY - point1.y);
                float newPointX12 = (point2.x - point1.x) / k12 + point1.x;
                float newPointZ12 = (point2.z - point1.z) / k12 + point1.z;
                Vector3 newPoint1_2 = new Vector3(newPointX12, planeY, newPointZ12);
                verticeList.Add(newPoint1_2);
                if (uvList.Count > 0)
                {
                    Vector2 uv1 = uvList[trianglePoint1];
                    Vector2 uv2 = uvList[trianglePoint2];
                    float newUV_x = (uv2.x - uv1.x) / k12 + uv1.x;
                    float newUV_y = (uv2.y - uv1.y) / k12 + uv1.y;
                    uvList.Add(new Vector2(newUV_x, newUV_y));
                }
                //法向量
                float newNoramlX12 = (normalX2.x - normalX1.x) / k12 + normalX1.x;
                float newNoramlY12 = (normalX2.y - normalX1.y) / k12 + normalX1.y;
                float newNoramlZ12 = (normalX2.z - normalX1.z) / k12 + normalX1.z;
                normalList.Add(new Vector3(newNoramlX12, newNoramlY12, newNoramlZ12));

                int newVerticeCount = verticeList.Count;
                //插入頂點索引,以此構建新三角形
                triangleList.Insert(triangleIndex + 1, newVerticeCount - 2);
                triangleList.Insert(triangleIndex + 2, newVerticeCount - 1);

                triangleList.Insert(triangleIndex + 3, newVerticeCount - 1);
                triangleList.Insert(triangleIndex + 4, newVerticeCount - 2);

                triangleList.Insert(triangleIndex + 6, trianglePoint0);
                triangleList.Insert(triangleIndex + 7, newVerticeCount - 1);
            }
            //1-2,2-0相連線段被切割
            else if ((point1.y - planeY) * (point2.y - planeY) < 0 && (point2.y - planeY) * (point0.y - planeY) < 0)
            {
                //截斷1-2之間的頂點
                float k12 = (point2.y - point1.y) / (planeY - point1.y);
                float newPointX12 = (point2.x - point1.x) / k12 + point1.x;
                float newPointZ12 = (point2.z - point1.z) / k12 + point1.z;
                Vector3 newPoint1_2 = new Vector3(newPointX12, planeY, newPointZ12);
                verticeList.Add(newPoint1_2);
                if (uvList.Count > 0)
                {
                    Vector2 uv1 = uvList[trianglePoint1];
                    Vector2 uv2 = uvList[trianglePoint2];
                    float newUV_x = (uv2.x - uv1.x) / k12 + uv1.x;
                    float newUV_y = (uv2.y - uv1.y) / k12 + uv1.y;
                    uvList.Add(new Vector2(newUV_x, newUV_y));
                }
                //法向量
                Vector3 normalX0 = normalList[trianglePoint0];
                Vector3 normalX1 = normalList[trianglePoint1];
                Vector3 normalX2 = normalList[trianglePoint2];
                float newNoramlX12 = (normalX2.x - normalX1.x) / k12 + normalX1.x;
                float newNoramlY12 = (normalX2.y - normalX1.y) / k12 + normalX1.y;
                float newNoramlZ12 = (normalX2.z - normalX1.z) / k12 + normalX1.z;
                normalList.Add(new Vector3(newNoramlX12, newNoramlY12, newNoramlZ12));

                //截斷0-2之間的頂點
                float k02 = (point2.y - point0.y) / (planeY - point0.y);
                float newPointX02 = (point2.x - point0.x) / k02 + point0.x;
                float newPointZ02 = (point2.z - point0.z) / k02 + point0.z;
                Vector3 newPoint0_2 = new Vector3(newPointX02, planeY, newPointZ02);
                verticeList.Add(newPoint0_2);
                //uv
                if (uvList.Count > 0)
                {
                    Vector2 uv0 = uvList[trianglePoint0];
                    Vector2 uv2 = uvList[trianglePoint2];
                    float newUV_x = (uv2.x - uv0.x) / k02 + uv0.x;
                    float newUV_y = (uv2.y - uv0.y) / k02 + uv0.y;
                    uvList.Add(new Vector2(newUV_x, newUV_y));
                }
                //法向量
                float newNoramlX02 = (normalX1.x - normalX0.x) / k02 + normalX0.x;
                float newNoramlY02 = (normalX1.y - normalX0.y) / k02 + normalX0.y;
                float newNoramlZ02 = (normalX1.z - normalX0.z) / k02 + normalX0.z;
                normalList.Add(new Vector3(newNoramlX02, newNoramlY02, newNoramlZ02));
                
                int newVerticeCount = verticeList.Count;
                //插入頂點索引,以此構建新三角形

                //{0}
                //{1}
                triangleList.Insert(triangleIndex + 2, newVerticeCount - 2);

                triangleList.Insert(triangleIndex + 3, newVerticeCount - 1);
                triangleList.Insert(triangleIndex + 4, newVerticeCount - 2);
                //{2}
                
                triangleList.Insert(triangleIndex + 6, newVerticeCount - 1);
                triangleList.Insert(triangleIndex + 7, trianglePoint0);
                triangleList.Insert(triangleIndex + 8, newVerticeCount - 2);
            }
            //0-1,2-0相連線段被切割
            else if((point0.y - planeY) * (point1.y - planeY) < 0 && (point2.y - planeY) * (point0.y - planeY) < 0)
            {
                //截斷0-1之間的頂點
                float k01 = (point1.y - point0.y) / (planeY - point0.y);
                float newPointX01 = (point1.x - point0.x) / k01 + point0.x;
                float newPointZ01 = (point1.z - point0.z) / k01 + point0.z;
                Vector3 newPoint0_1 = new Vector3(newPointX01, planeY, newPointZ01);
                verticeList.Add(newPoint0_1);
                //uv
                if (uvList.Count > 0)
                {
                    Vector2 uv0 = uvList[trianglePoint0];
                    Vector2 uv1 = uvList[trianglePoint1];
                    float newUV_x = (uv1.x - uv0.x) / k01 + uv0.x;
                    float newUV_y = (uv1.y - uv0.y) / k01 + uv0.y;
                    uvList.Add(new Vector2(newUV_x, newUV_y));
                }
                //法向量
                Vector3 normalX0 = normalList[trianglePoint0];
                Vector3 normalX1 = normalList[trianglePoint1];
                Vector3 normalX2 = normalList[trianglePoint2];
                float newNoramlX01 = (normalX1.x - normalX0.x) / k01 + normalX0.x;
                float newNoramlY01 = (normalX1.y - normalX0.y) / k01 + normalX0.y;
                float newNoramlZ01 = (normalX1.z - normalX0.z) / k01 + normalX0.z;
                normalList.Add(new Vector3(newNoramlX01, newNoramlY01, newNoramlZ01));

                //截斷0-2之間的頂點
                float k02 = (point2.y - point0.y) / (planeY - point0.y);
                float newPointX02 = (point2.x - point0.x) / k02 + point0.x;
                float newPointZ02 = (point2.z - point0.z) / k02 + point0.z;
                Vector3 newPoint0_2 = new Vector3(newPointX02, planeY, newPointZ02);
                verticeList.Add(newPoint0_2);
                //uv
                if (uvList.Count > 0)
                {
                    Vector2 uv0 = uvList[trianglePoint0];
                    Vector2 uv2 = uvList[trianglePoint2];
                    float newUV_x = (uv2.x - uv0.x) / k02 + uv0.x;
                    float newUV_y = (uv2.y - uv0.y) / k02 + uv0.y;
                    uvList.Add(new Vector2(newUV_x, newUV_y));
                }
                //法向量
                float newNoramlX02 = (normalX1.x - normalX0.x) / k02 + normalX0.x;
                float newNoramlY02 = (normalX1.y - normalX0.y) / k02 + normalX0.y;
                float newNoramlZ02 = (normalX1.z - normalX0.z) / k02 + normalX0.z;
                normalList.Add(new Vector3(newNoramlX02, newNoramlY02, newNoramlZ02));
                
                int newVerticeCount = verticeList.Count;
                //插入頂點索引,以此構建新三角形

                //{0}
                triangleList.Insert(triangleIndex + 1, newVerticeCount - 2);
                triangleList.Insert(triangleIndex + 2, newVerticeCount - 1);

                triangleList.Insert(triangleIndex + 3, newVerticeCount - 2);
                //{1}
                //{2}
                
                triangleList.Insert(triangleIndex + 6, trianglePoint2);
                triangleList.Insert(triangleIndex + 7, newVerticeCount - 1);
                triangleList.Insert(triangleIndex + 8, newVerticeCount - 2);
            }
            //只有0-1被切
            else if((point0.y - planeY) * (point1.y - planeY) < 0)
            {
                Debug.Log("只有01被切");
            }
            //只有1-2被切
            else if ((point1.y - planeY) * (point2.y - planeY) < 0)
            {
                Debug.Log("只有12被切");
            }
            //只有2-0被切
            else if ((point2.y - planeY) * (point0.y - planeY) < 0)
            {
                Debug.Log("只有02被切");
            }
            triangleIndex += 3;
        }

        //篩選出切割面兩側的頂點索引
        List<int> triangles1 = new List<int>();
        List<int> triangles2 = new List<int>();
        for (int triangleIndex = 0; triangleIndex < triangleList.Count; triangleIndex += 3)
        {
            int trianglePoint0 = triangleList[triangleIndex];
            int trianglePoint1 = triangleList[triangleIndex + 1];
            int trianglePoint2 = triangleList[triangleIndex + 2];
            
            Vector3 point0 = verticeList[trianglePoint0];
            Vector3 point1 = verticeList[trianglePoint1];
            Vector3 point2 = verticeList[trianglePoint2];
            //切割面
            float planeY = 0.3f;
            if(point0.y > planeY || point1.y > planeY || point2.y > planeY)
            {
                triangles1.Add(trianglePoint0);
                triangles1.Add(trianglePoint1);
                triangles1.Add(trianglePoint2);
            }
            else
            {
                triangles2.Add(trianglePoint0);
                triangles2.Add(trianglePoint1);
                triangles2.Add(trianglePoint2);
            }
        }

        //縫合切口
        //for (int verticeIndex = verticeCount; verticeIndex < verticeList.Count - 2; ++verticeIndex)
        //{
        //    triangles1.Add(verticeIndex + 2);
        //    triangles1.Add(verticeIndex);
        //    triangles1.Add(verticeCount);

        //    triangles2.Add(verticeCount);
        //    triangles2.Add(verticeIndex);
        //    triangles2.Add(verticeIndex + 2);
        //}


        mf.mesh.vertices = verticeList.ToArray();
        mf.mesh.triangles = triangles1.ToArray();
        if (uvList.Count > 0)
        {
            mf.mesh.uv = uvList.ToArray();
        }
        mf.mesh.normals = normalList.ToArray();


        //分割模型
        GameObject newModel = new GameObject("New Model");
        MeshFilter meshFilter = newModel.AddComponent<MeshFilter>();
        meshFilter.mesh.vertices = mf.mesh.vertices;
        meshFilter.mesh.triangles = triangles2.ToArray();
        meshFilter.mesh.uv = mf.mesh.uv;
        meshFilter.mesh.normals = mf.mesh.normals;
        Renderer newRenderer = newModel.AddComponent<MeshRenderer>();
        newRenderer.material = this.gameObject.GetComponent<MeshRenderer>().material;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章