using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; [RequireComponent(typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer))] [RequireComponent(typeof(MeshCollider))] public class Polygon : MonoBehaviour { private MeshCollider meshCollider; private Material material; private Mesh mesh; void Awake() { material = GetComponent().material; mesh = GetComponent().mesh; meshCollider = GetComponent(); } public void SetVertices(Vector3[] vertices) { mesh.Clear(); mesh.vertices = vertices; //mesh.triangles = GetTriangles(vertices); List indexes = new List(); int index = 0; for (int i=0;i< vertices.Length;i++) { indexes.Add(index++); } mesh.triangles=WidelyTriangleIndex(vertices.ToList(), indexes).ToArray(); // 实时调整碰撞器形状,以便可以正确响应碰撞 meshCollider.sharedMesh = mesh; } public Color Color { get { return material.color; } set { material.color = value; } } /// /// 计算多边形重心 /// /// /// public Vector3 getCenterOfGravityPoint(List mPoints) { float area = 0.0f;//多边形面积 float Gx = 0.0f, Gz = 0.0f;// 重心的x、y for (int i = 1; i <= mPoints.Count; i++) { float iLat = mPoints[(i % mPoints.Count())].x; float iLng = mPoints[(i % mPoints.Count())].z; float nextLat = mPoints[(i - 1)].x; float nextLng = mPoints[(i - 1)].z; float temp = (iLat * nextLng - iLng * nextLat) / 2.0f; area += temp; Gx += temp * (iLat + nextLat) / 3.0f; Gz += temp * (iLng + nextLng) / 3.0f; } Gx = Gx / area; Gz = Gz / area; return new Vector3(Gx, mPoints[0].y, Gz); } //FIXME: 这个算法有缺陷,能较好地处理凸多边形,但在处理复杂多边形(比如重复点,自交,带洞,反向折叠)时会有问题 private int[] GetTriangles(Vector3[] vertices) { var count = vertices.Length - 2; var triangles = new int[count * 3]; var front = 0; var back = vertices.Length - 1; for (var i = 1; i <= count; i++) { if (i % 2 == 1) { triangles[i * 3 - 3] = front++; triangles[i * 3 - 2] = front; triangles[i * 3 - 1] = back; } else { triangles[i * 3 - 1] = back--; triangles[i * 3 - 2] = back; triangles[i * 3 - 3] = front; } } return triangles; } /// /// 三角剖分 /// 1.寻找一个可划分顶点 /// 2.分割出新的多边形和三角形 /// 3.新多边形若为凸多边形,结束;否则继续剖分 /// /// 寻找可划分顶点 /// 1.顶点是否为凸顶点:顶点在剩余顶点组成的图形外 /// 2.新的多边形没有顶点在分割的三角形内 /// /// 顺时针排列的顶点列表 /// 顶点索引列表 /// 三角形列表 public List WidelyTriangleIndex(List verts, List indexes) { int len = verts.Count; if (len <= 3) return ConvexTriangleIndex(verts, indexes); int searchIndex = 0; List covexIndex = new List(); bool isCovexPolygon = true;//判断多边形是否是凸多边形 for (searchIndex = 0; searchIndex < len; searchIndex++) { List polygon = new List(verts.ToArray()); polygon.RemoveAt(searchIndex); if (IsPointInsidePolygon(verts[searchIndex], polygon)) { isCovexPolygon = false; break; } else { covexIndex.Add(searchIndex); } } if (isCovexPolygon) return ConvexTriangleIndex(verts, indexes); //查找可划分顶点 int canFragementIndex = -1;//可划分顶点索引 for (int i = 0; i < len; i++) { if (i > searchIndex) { List polygon = new List(verts.ToArray()); polygon.RemoveAt(i); if (!IsPointInsidePolygon(verts[i], polygon) && IsFragementIndex(i, verts)) { canFragementIndex = i; break; } } else { if (covexIndex.IndexOf(i) != -1 && IsFragementIndex(i, verts)) { canFragementIndex = i; break; } } } if (canFragementIndex < 0) { Debug.Log($"->{"数据有误找不到可划分顶点"}"); return new List(); } //用可划分顶点将凹多边形划分为一个三角形和一个多边形 List tTriangles = new List(); int next = (canFragementIndex == len - 1) ? 0 : canFragementIndex + 1; int prev = (canFragementIndex == 0) ? len - 1 : canFragementIndex - 1; tTriangles.Add(indexes[prev]); tTriangles.Add(indexes[canFragementIndex]); tTriangles.Add(indexes[next]); //剔除可划分顶点及索引 verts.RemoveAt(canFragementIndex); indexes.RemoveAt(canFragementIndex); //递归划分 List leaveTriangles = WidelyTriangleIndex(verts, indexes); tTriangles.AddRange(leaveTriangles); return tTriangles; } /// /// 凸多边形,顺时针序列,以第1个点来剖分三角形,如下: /// 0---1 /// | | /// 3---2 --> (0, 1, 2)、(0, 2, 3) /// /// 顺时针排列的顶点列表 /// 顶点索引列表 /// 三角形列表 public List ConvexTriangleIndex(List verts, List indexes) { int len = verts.Count; //若是闭环去除最后一点 if (len > 1 && Vector3Equal(verts[0], verts[len - 1])) { len--; } int triangleNum = len - 2; List triangles = new List(triangleNum * 3); for (int i = 0; i < triangleNum; i++) { triangles.Add(indexes[0]); triangles.Add(indexes[i + 1]); triangles.Add(indexes[i + 2]); } return triangles; } /// /// 点与多边形的位置关系 /// /// 判定点 /// 剩余顶点按顺序排列的多边形 /// true:点在多边形之内,false:相反 private bool IsPointInsidePolygon(Vector3 point, List polygonVerts) { int len = polygonVerts.Count; Ray ray = new Ray(point, new Vector3(0, 0, 1)); //y方向射线 int interNum = 0; for (int i = 1; i < len; i++) { if (IsDetectIntersect(ray, polygonVerts[i - 1], polygonVerts[i])) { interNum++; } } //不是闭环 if (!Vector3Equal(polygonVerts[0], polygonVerts[len - 1])) { if (IsDetectIntersect(ray, polygonVerts[len - 1], polygonVerts[0])) { interNum++; } } int remainder = interNum % 2; return remainder == 1; } /// /// 是否是可划分顶点:新的多边形没有顶点在分割的三角形内 /// private bool IsFragementIndex(int index, List verts) { int len = verts.Count; List triangleVert = new List(); int next = (index == len - 1) ? 0 : index + 1; int prev = (index == 0) ? len - 1 : index - 1; triangleVert.Add(verts[prev]); triangleVert.Add(verts[index]); triangleVert.Add(verts[next]); for (int i = 0; i < len; i++) { if (i != index && i != prev && i != next) { if (IsPointInsidePolygon(verts[i], triangleVert)) { return false; } } } return true; } /// /// 射线与线段相交性判断 /// /// 射线 /// 线段头 /// 线段尾 /// private bool IsDetectIntersect(Ray ray, Vector3 p1, Vector3 p2) { float pointZ;//交点z坐标,x固定值 if (floatEqual(p1.x, p2.x)) { return false; } else if (floatEqual(p1.z, p2.z)) { pointZ = p1.z; } else { //直线两点式方程:(y-y2)/(y1-y2) = (x-x2)/(x1-x2) float a = p1.x - p2.x; float b = p1.z - p2.z; float c = p2.z / b - p2.x / a; pointZ = b / a * ray.origin.x + b * c; } if (floatLess(pointZ, ray.origin.z)) { //交点y小于射线起点y return false; } else { Vector3 leftP = floatLess(p1.x, p2.x) ? p1 : p2;//左端点 Vector3 rightP = floatLess(p1.x, p2.x) ? p2 : p1;//右端点 //交点x位于线段两个端点x之外,相交与线段某个端点时,仅将射线L与左侧多边形一边的端点记为焦点(即就是:只将右端点记为交点) if (!floatGreat(ray.origin.x, leftP.x) || floatGreat(ray.origin.x, rightP.x)) { return false; } } return true; } const double epsilon = 1e-7; bool floatLess(float value, float other) { return (other - value) > epsilon; } bool floatGreat(float value, float other) { return (value - other) > epsilon; } bool floatEqual(float value, float other) { return Mathf.Abs(value - other) < epsilon; } bool Vector3Equal(Vector3 a, Vector3 b) { return floatEqual(a.x, b.x) && floatEqual(a.y, b.y) && floatEqual(a.z, b.z); } }