You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
329 lines
10 KiB
329 lines
10 KiB
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<MeshRenderer>().material; |
|
mesh = GetComponent<MeshFilter>().mesh; |
|
meshCollider = GetComponent<MeshCollider>(); |
|
} |
|
|
|
public void SetVertices(Vector3[] vertices) |
|
{ |
|
mesh.Clear(); |
|
mesh.vertices = vertices; |
|
//mesh.triangles = GetTriangles(vertices); |
|
List<int> indexes = new List<int>(); |
|
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; } |
|
} |
|
/// <summary> |
|
/// 计算多边形重心 |
|
/// </summary> |
|
/// <param name="mPoints"></param> |
|
/// <returns></returns> |
|
public Vector3 getCenterOfGravityPoint(List<Vector3> 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; |
|
} |
|
/// <summary> |
|
/// 三角剖分 |
|
/// 1.寻找一个可划分顶点 |
|
/// 2.分割出新的多边形和三角形 |
|
/// 3.新多边形若为凸多边形,结束;否则继续剖分 |
|
/// |
|
/// 寻找可划分顶点 |
|
/// 1.顶点是否为凸顶点:顶点在剩余顶点组成的图形外 |
|
/// 2.新的多边形没有顶点在分割的三角形内 |
|
/// </summary> |
|
/// <param name="verts">顺时针排列的顶点列表</param> |
|
/// <param name="indexes">顶点索引列表</param> |
|
/// <returns>三角形列表</returns> |
|
public List<int> WidelyTriangleIndex(List<Vector3> verts, List<int> indexes) |
|
{ |
|
int len = verts.Count; |
|
if (len <= 3) return ConvexTriangleIndex(verts, indexes); |
|
|
|
int searchIndex = 0; |
|
List<int> covexIndex = new List<int>(); |
|
bool isCovexPolygon = true;//判断多边形是否是凸多边形 |
|
|
|
for (searchIndex = 0; searchIndex < len; searchIndex++) |
|
{ |
|
List<Vector3> polygon = new List<Vector3>(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<Vector3> polygon = new List<Vector3>(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($"-><color=#CD8500>{"数据有误找不到可划分顶点"}</color>"); |
|
return new List<int>(); |
|
} |
|
|
|
//用可划分顶点将凹多边形划分为一个三角形和一个多边形 |
|
List<int> tTriangles = new List<int>(); |
|
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<int> leaveTriangles = WidelyTriangleIndex(verts, indexes); |
|
tTriangles.AddRange(leaveTriangles); |
|
|
|
return tTriangles; |
|
} |
|
/// <summary> |
|
/// 凸多边形,顺时针序列,以第1个点来剖分三角形,如下: |
|
/// 0---1 |
|
/// | | |
|
/// 3---2 --> (0, 1, 2)、(0, 2, 3) |
|
/// </summary> |
|
/// <param name="verts">顺时针排列的顶点列表</param> |
|
/// <param name="indexes">顶点索引列表</param> |
|
/// <returns>三角形列表</returns> |
|
public List<int> ConvexTriangleIndex(List<Vector3> verts, List<int> indexes) |
|
{ |
|
int len = verts.Count; |
|
//若是闭环去除最后一点 |
|
if (len > 1 && Vector3Equal(verts[0], verts[len - 1])) |
|
{ |
|
len--; |
|
} |
|
int triangleNum = len - 2; |
|
List<int> triangles = new List<int>(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; |
|
} |
|
/// <summary> |
|
/// 点与多边形的位置关系 |
|
/// </summary> |
|
/// <param name="point">判定点</param> |
|
/// <param name="polygonVerts">剩余顶点按顺序排列的多边形</param> |
|
/// <returns>true:点在多边形之内,false:相反</returns> |
|
private bool IsPointInsidePolygon(Vector3 point, List<Vector3> 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; |
|
} |
|
/// <summary> |
|
/// 是否是可划分顶点:新的多边形没有顶点在分割的三角形内 |
|
/// </summary> |
|
private bool IsFragementIndex(int index, List<Vector3> verts) |
|
{ |
|
int len = verts.Count; |
|
List<Vector3> triangleVert = new List<Vector3>(); |
|
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; |
|
} |
|
/// <summary> |
|
/// 射线与线段相交性判断 |
|
/// </summary> |
|
/// <param name="ray">射线</param> |
|
/// <param name="p1">线段头</param> |
|
/// <param name="p2">线段尾</param> |
|
/// <returns></returns> |
|
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); |
|
} |
|
} |