效果图:
做法及原理可参考此链接:http://www.cnblogs.com/xignzou/p/3721494.html
代码:
using System;
using System.Collections.Generic;
using UnityEngine;
namespace PolygonTool
{
#region 耳切法对简单多边形进行三角形化
///
/// 判断凹点,凸点,耳朵的比较轴
///
public enum CompareAxle
{
X,
Y,
Z
}
///
/// 对多边形处理
///
public class Triangulation
{
///
/// 判断凹凸的时候的比对轴
///
private CompareAxle _compareAxle = CompareAxle.Y;
///
/// 多边形顶点
///
private List _polygonVertexs = new List();
///
/// 顶点序列
///
private List _vertexsSequence = new List();
///
/// 节点管理器
///
private NodeManager _nodeManager = new NodeManager();
///
/// 初始化
///
/// 多边形顶点
public Triangulation(List polygonVertexs)
{
this._polygonVertexs = polygonVertexs;
_nodeManager.Init(polygonVertexs);
}
///
/// 设置比较轴
///
///
public void SetCompareAxle(CompareAxle compareAxle)
{
this._compareAxle = compareAxle;
}
///
/// 获取三角形的顶点序列
///
public int[] GetTriangles()
{
while (_nodeManager.LinkedListLength >= 3)
{
SplitResult sr = SplitPolygon();
//
if (sr == null)
{
Debug.Log("null");
return null;
}
}
return _vertexsSequence.ToArray();
}
///
/// 计算凹顶点,凸顶点,耳朵
///
private SplitResult SplitPolygon()
{
//凹点
List _concaveVertexs = new List();
//凸点
List _raisedVertexs = new List();
//耳朵
List _polygonEars = new List();
//起始节点
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.Y)
{
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 needRemoveIdList = new List();
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);
}
///
/// 判断一点是否在三角形内
///
/// 一点
///
///
///
///
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 class NodeManager
{
private List _nodeList = new List();
public int LinkedListLength
{
get { return _nodeList.Count; }
}
public Node FirstNode
{
get { return _nodeList[0]; }
}
public void Init(List 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
{
///
/// 凸顶点
///
public List raisedVertexs;
///
/// 凹顶点
///
public List concaveVertexs;
///
/// 耳朵
///
public List polygonEars;
public SplitResult(List raisedVertexs, List concaveVertexs, List polygonEars)
{
this.raisedVertexs = raisedVertexs;
this.concaveVertexs = concaveVertexs;
this.polygonEars = polygonEars;
}
}
}
#endregion
}
接下来是测试代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using PolygonTool;
public class T : MonoBehaviour
{
//用物体的坐标来代替点
public List tList;
//计算得到的三角形序列下标
private List resultList = new List();
private Triangulation triangulation;
private void Start()
{
tList.Reverse();
List posList = new List();
for (int i = 0; i < tList.Count; i++)
{
posList.Add(tList[i].position);
}
triangulation = new Triangulation(posList);
triangulation.SetCompareAxle(CompareAxle.Y);
int[] a = triangulation.GetTriangles();
if (a != null)
{
for (int i = 0; i < a.Length; i++)
{
Debug.Log("===:" + a[i]);
resultList.Add(a[i]);
}
}
GameObject go = new GameObject();
MeshFilter mf = go.AddComponent();
go.AddComponent();
Mesh m = new Mesh();
Vector3[] vertexs = new Vector3[a.Length];
for (int i = 0; i < vertexs.Length; i++)
{
Vector3 v = tList[a[i]].position;
vertexs[i] = v;
}
m.vertices = vertexs;
int[] tri = new int[a.Length];
for (int i = 0; i < tri.Length; i += 3)
{
tri[i] = i;
tri[i + 1] = i + 2;
tri[i + 2] = i + 1;
}
m.triangles = tri;
mf.mesh = m;
}
private void OnDrawGizmos()
{
for (int i = 0; i < tList.Count; i++)
{
if (i < tList.Count - 1)
{
Gizmos.DrawLine(tList[i].position, tList[i + 1].position);
}
else
{
Gizmos.DrawLine(tList[i].position, tList[0].position);
}
}
Gizmos.color = Color.black;
for (int i = 0; i < resultList.Count; i+=3)
{
int startIndex = resultList[i];
int endIndex = resultList[i + 2];
Gizmos.DrawLine(tList[startIndex].position, tList[endIndex].position);
}
}
}
github地址:https://github.com/yiwei151/PolygonTriangulation