Unity 自定义多边形创建

多边形的创建

Unity中使用Mesh类创建多边形,一般是将多边形划分为三角形后逐一拼接构成。

凸多边形的划分较为简单:

在多边形的任意一边上任取一点P,连接P点与其不相邻的其它各顶点的线段,可以把多边形边形分成N个三角形。

凹多边形划分时上述方法不再适用,因此可以使用耳切法进行划分,耳切法较为详细的讲述:

1.原篇:

Triangulation by Ear Clipping (geometrictools.com)

2.翻译:Triangulation by Ear Clipping(耳切法处理多边形三角划分) - 行走_ - 博客园 (cnblogs.com)

耳切法划分多边形为三角形

ref:(21条消息) 判断多边形的顺逆性_Leopard-C的博客-CSDN博客

ref:(21条消息) 使用耳切法将多边形三角化_贪玩的孩纸时代的博客-CSDN博客

using System;
using System.Collections.Generic;
using UnityEngine;

namespace PolygonTool
{
    /// <summary>
    /// 判断凹点,凸点,耳朵的比较轴
    /// </summary>
    public enum CompareAxle
    {
        X,
        YT,
        YF,
        Z
    }

    /// <summary>
    /// 对多边形处理
    /// </summary>
    public class Triangulation
    {
        /// <summary>
        /// 判断凹凸的时候的比对轴
        /// </summary>
        private CompareAxle _compareAxle = CompareAxle.YT;

        /// <summary>
        /// 多边形顶点
        /// </summary>
        private List<Vector3> _polygonVertexs = new List<Vector3>();

        /// <summary>
        /// 顶点序列
        /// </summary>
        private List<int> _vertexsSequence = new List<int>();

        /// <summary>
        /// 节点管理器
        /// </summary>
        private NodeManager _nodeManager = new NodeManager();

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="polygonVertexs">多边形顶点</param>
        public Triangulation(List<Vector3> polygonVertexs)
        {
            this._polygonVertexs = polygonVertexs;
            _nodeManager.Init(polygonVertexs);
        }

        /// <summary>
        /// 设置比较轴
        /// </summary>
        /// <param name="compareAxle"></param>
        public void SetCompareAxle(CompareAxle compareAxle)
        {
            this._compareAxle = compareAxle;
        }

        /// <summary>
        /// 设置比较轴
        /// </summary>
        /// <param name="polygonVertexs"></param>
        public void SetCompareAxle(bool clockwise)
        {
            if (clockwise)
            {
                this._compareAxle = CompareAxle.YT;
            }
            else
            {
                this._compareAxle = CompareAxle.YF;
            }
        }

        /// <summary>
        /// 获取三角形的顶点序列
        /// </summary>
        public int[] GetTriangles()
        {
            while (_nodeManager.LinkedListLength >= 3)
            {
                SplitResult sr = SplitPolygon();
                //
                if (sr == null)
                {
                    Debug.Log("null");
                    return null;
                }
            }

            return _vertexsSequence.ToArray();
        }

        /// <summary>
        /// 计算凹顶点,凸顶点,耳朵
        /// </summary>
        private SplitResult SplitPolygon()
        {
            //凹点
            List<Node> _concaveVertexs = new List<Node>();
            //凸点
            List<Node> _raisedVertexs = new List<Node>();
            //耳朵
            List<Node> _polygonEars = new List<Node>();
            //起始节点
            Node currentNode = _nodeManager.FirstNode;

            #region 计算凹顶点,凸顶点

            for (int i = 0; i < _nodeManager.LinkedListLength; i++)
            {
                Vector3 one = currentNode.vertex - currentNode.lastNode.vertex;
                Vector3 two = currentNode.nextNode.vertex - currentNode.vertex;
                Vector3 crossRes = Vector3.Cross(one, two);

                if (_compareAxle == CompareAxle.YT)
                {
                    if (crossRes.y < 0)
                    {
                        _concaveVertexs.Add(currentNode);
                    }
                    else
                    {
                        _raisedVertexs.Add(currentNode);
                    }
                }

                if (_compareAxle == CompareAxle.YF)
                {
                    if (crossRes.y > 0)
                    {
                        _concaveVertexs.Add(currentNode);
                    }
                    else
                    {
                        _raisedVertexs.Add(currentNode);
                    }
                }

                if (_compareAxle == CompareAxle.X)
                {
                    if (crossRes.x > 0)
                    {
                        _concaveVertexs.Add(currentNode);
                    }
                    else
                    {
                        _raisedVertexs.Add(currentNode);
                    }
                }

                if (_compareAxle == CompareAxle.Z)
                {
                    if (crossRes.z > 0)
                    {
                        _concaveVertexs.Add(currentNode);
                    }
                    else
                    {
                        _raisedVertexs.Add(currentNode);
                    }
                }

                _polygonEars.Add(currentNode);
                currentNode = currentNode.nextNode;
            }

            for (int i = 0; i < _concaveVertexs.Count; i++)
            {
                _polygonEars.Remove(_concaveVertexs[i]);
            }
            #endregion

            #region 计算耳朵
            List<int> needRemoveIdList = new List<int>();
            for (int i = 0; i < _polygonEars.Count; i++)
            {
                Node earNode = _polygonEars[i];
                Node compareNode = earNode.nextNode.nextNode;

                while (compareNode != earNode.lastNode)
                {
                    bool isIn = IsIn(compareNode.vertex, earNode.lastNode.vertex, earNode.vertex,
                        earNode.nextNode.vertex);

                    if (isIn == true)
                    {
                        if (_polygonEars.Contains(_polygonEars[i]))
                        {
                            needRemoveIdList.Add(_polygonEars[i].id);
                        }
                        break;
                    }
                    compareNode = compareNode.nextNode;
                }
            }

            for (int j = 0; j < needRemoveIdList.Count; j++)
            {
                for (int i = 0; i < _polygonEars.Count; i++)
                {
                    if (_polygonEars[i].id == needRemoveIdList[j])
                    {
                        _polygonEars.RemoveAt(i);
                    }
                }
            }

            #endregion

            #region 打印初始化结果
            //Debug.Log("凸点");
            //for (int i = 0; i < _raisedVertexs.Count; i++)
            //{
            //    Debug.Log(_raisedVertexs[i].id);
            //}

            //Debug.Log("凹点");
            //for (int i = 0; i < _concaveVertexs.Count; i++)
            //{
            //    Debug.Log(_concaveVertexs[i].id);
            //}

            //Debug.Log("耳朵");
            //for (int i = 0; i < _polygonEars.Count; i++)
            //{
            //    Debug.Log(_polygonEars[i].id);
            //}

            //Debug.Log("-----------------------------------------------");
            #endregion

            //耳朵为空说明不是简单多边形 多边形三角化失败
            if (_polygonEars.Count == 0)
            {
                return null;
            }

            _vertexsSequence.Add(_polygonEars[0].lastNode.id);
            _vertexsSequence.Add(_polygonEars[0].id);
            _vertexsSequence.Add(_polygonEars[0].nextNode.id);
            _nodeManager.RemoveNode(_polygonEars[0]);


            return new SplitResult(_raisedVertexs, _concaveVertexs, _polygonEars);
        }

        /// <summary>
        /// 判断一点是否在三角形内
        /// </summary>
        /// <param name="p">一点</param>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="c"></param>
        /// <returns></returns>
        public bool IsIn(Vector3 p, Vector3 a, Vector3 b, Vector3 c)
        {
            Vector3 pa = p - a;
            Vector3 pb = p - b;
            Vector3 pc = p - c;

            Vector3 t1 = Vector3.Cross(pa, pb);
            Vector3 t2 = Vector3.Cross(pb, pc);
            Vector3 t3 = Vector3.Cross(pc, pa);

            bool isIn2 = t1.y >= 0 && t2.y >= 0 && t3.y >= 0 || t1.y <= 0 && t2.y <= 0 && t3.y <= 0;

            return isIn2;
        }

        public bool IsClockwise_1(List<Vector3> points)
        {
            List<MYPOINT> pts = new List<MYPOINT>();
            for (int i = 0; i < points.Count; i++)
            {
                MYPOINT m = new MYPOINT(points[i].x, points[i].z);
                pts.Add(m);
            }

            double d = 0;
            int size = pts.Count;
            for (int i = 0; i < size - 1; i++)
            {
                d += -0.5 * (pts[i + 1].y + pts[i].y) * (pts[i + 1].x - pts[i].x);
            }

            // 注意,这个容易忽略!最后一个点与第一个点构成的边
            d += -0.5 * (pts[0].y + pts[size - 1].y) * (pts[0].x - pts[size - 1].x);

            if (d < 0)
                return true;
            else
                return false;
        }

        /// <summary>
        /// 管理多边形 构成一个双向链表
        /// </summary>
        public class NodeManager
        {

            private List<Node> _nodeList = new List<Node>();

            public int LinkedListLength
            {
                get { return _nodeList.Count; }
            }

            public Node FirstNode
            {
                get { return _nodeList[0]; }
            }

            public void Init(List<Vector3> vertexs)
            {

                for (int i = 0; i < vertexs.Count; i++)
                {
                    Node node = new Node(i, vertexs[i]);
                    _nodeList.Add(node);
                }

                for (int i = 0; i < LinkedListLength; i++)
                {
                    if (i == 0)
                    {
                        _nodeList[i].lastNode = _nodeList[LinkedListLength - 1];
                        _nodeList[i].nextNode = _nodeList[1];
                    }
                    else if (i == LinkedListLength - 1)
                    {
                        _nodeList[i].lastNode = _nodeList[LinkedListLength - 2];
                        _nodeList[i].nextNode = _nodeList[0];
                    }
                    else
                    {
                        _nodeList[i].lastNode = _nodeList[i - 1];
                        _nodeList[i].nextNode = _nodeList[i + 1];
                    }
                }
            }

            public void RemoveNode(Node node)
            {
                _nodeList.Remove(node);
                node.lastNode.nextNode = node.nextNode;
                node.nextNode.lastNode = node.lastNode;
            }
        }

        public class Node
        {

            public int id;
            public Vector3 vertex;
            public Node lastNode;
            public Node nextNode;

            public Node(int id, Vector3 vertex)
            {
                this.id = id;
                this.vertex = vertex;
            }

            public Node(int id, Vector3 vertex, Node lastNode, Node nextNode)
            {
                this.id = id;
                this.vertex = vertex;
                this.lastNode = lastNode;
                this.nextNode = nextNode;
            }
        }

        public class SplitResult
        {
            /// <summary>
            /// 凸顶点
            /// </summary>
            public List<Node> raisedVertexs;

            /// <summary>
            /// 凹顶点
            /// </summary>
            public List<Node> concaveVertexs;

            /// <summary>
            /// 耳朵
            /// </summary>
            public List<Node> polygonEars;

            public SplitResult(List<Node> raisedVertexs, List<Node> concaveVertexs, List<Node> polygonEars)
            {
                this.raisedVertexs = raisedVertexs;
                this.concaveVertexs = concaveVertexs;    
                this.polygonEars = polygonEars;
            }
        }

        //自定义MyPoint类  多边形顶点顺逆时针方向判断
        public class MYPOINT
        {
            public double x;
            public double y;
            public MYPOINT(double xx, double yy)
            {
                x = xx;
                y = yy;
            }
        }
    }
}

多边形创建(立方体)

points为多边形的顶点集合链表

vert为多边形顶点位置链表

indices为多边形顶点绘制序号链表

private GameObject CreateMesh()
    {
        #region 底面添加
        Triangulation triangulation = new Triangulation(points);

        bool clockwise = triangulation.IsClockwise_1(points);

        triangulation.SetCompareAxle(clockwise);

        int[] trisIndex_Bottom = triangulation.GetTriangles();

        for (int i = 0; i < trisIndex_Bottom.Length; i++)
        {
            verts.Add(points[trisIndex_Bottom[i]]);
        }

        for (int i = 0; i < trisIndex_Bottom.Length; i += 3)
        {
            if (clockwise)
            {
                indices.Add(i);
                indices.Add(i + 2);
                indices.Add(i + 1);
            }
            else
            {
                indices.Add(i + 1);
                indices.Add(i + 2);
                indices.Add(i);
            }
        }
        #endregion

        #region 顶面添加
        for (int i = 0; i < points.Count; i++)
        {
            points_top.Add(points[i] + new Vector3(0, 1, 0));
        }

        Triangulation triangulation_top = new Triangulation(points_top);

        triangulation_top.SetCompareAxle(clockwise);

        int[] trisIndex_Top = triangulation_top.GetTriangles();

        for (int i = 0; i < trisIndex_Top.Length; i++)
        {
            verts.Add(points_top[trisIndex_Top[i]]);
        }

        for (int i = 0; i < trisIndex_Top.Length; i += 3)
        {
            if (clockwise)
            {
                indices.Add(i + trisIndex_Bottom.Length + 1);
                indices.Add(i + trisIndex_Bottom.Length + 2);
                indices.Add(i + trisIndex_Bottom.Length);
            }
            else
            {
                indices.Add(i + trisIndex_Bottom.Length);
                indices.Add(i + trisIndex_Bottom.Length + 2);
                indices.Add(i + trisIndex_Bottom.Length + 1);
            }
        }
        #endregion

        #region 侧面添加
        for (int i = 0; i < points.Count; i++)
        {
            verts.Add(points[i]);
            verts.Add(points[i + 1 == points.Count ? 0 : i + 1]);
            verts.Add(points_top[i]);
            verts.Add(points_top[i + 1 == points.Count ? 0 : i + 1]);
        }

        for (int i = 0; i < points.Count; i++)
        {
            indices.Add(clockwise? trisIndex_Bottom.Length * 2 + (i * 4) + 1: trisIndex_Bottom.Length * 2 + (i * 4));
            indices.Add(trisIndex_Bottom.Length * 2 + (i * 4) + 3);
            indices.Add(clockwise ? trisIndex_Bottom.Length * 2 + (i * 4): trisIndex_Bottom.Length * 2 + (i * 4) + 1);

            indices.Add(clockwise ? trisIndex_Bottom.Length * 2 + (i * 4) + 3: trisIndex_Bottom.Length * 2 + (i * 4));
            indices.Add(trisIndex_Bottom.Length * 2 + (i * 4) + 2);
            indices.Add(clockwise ? trisIndex_Bottom.Length * 2 + (i * 4): trisIndex_Bottom.Length * 2 + (i * 4) + 3);
        }
        #endregion

        #region 网格数据整理
        Mesh mesh = new Mesh();
        mesh.SetVertices(verts);
        mesh.SetIndices(indices, MeshTopology.Triangles, 0);
        mesh.RecalculateNormals();
        mesh.RecalculateBounds();
        #endregion

        #region 对象创建
        GameObject meshObj = new GameObject("mesh");
        MeshFilter filter = meshObj.AddComponent<MeshFilter>();
        MeshRenderer renderer = meshObj.AddComponent<MeshRenderer>();
        renderer.material = material;
        filter.mesh = mesh;
        return meshObj;
        #endregion 
    }

猜你喜欢

转载自blog.csdn.net/qq_33325776/article/details/127627689