对简单多边形进行三角剖分

作者:追风剑情 发布于:2020-1-6 16:14 分类:Algorithms

示例:通过编辑顶点自定义Mesh形状

1111.png

2222.png

333.png

444.png

CustomMesh.cs

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. /// <summary>
  5. /// 自定义Mesh
  6. /// </summary>
  7. public class CustomMesh : MonoBehaviour
  8. {
  9. public MeshFilter meshFilter;
  10. public Transform[] vertexes;
  11.  
  12. private List<Vector3> vertices;//Mesh顶点
  13. private List<int> triangles; //Mesh三角形
  14. private Mesh mesh = null;
  15. private List<Vector3> points;
  16. //映射: 顶点->顶点序号
  17. private Dictionary<Vector3, int> indexDic = new Dictionary<Vector3, int>();
  18.  
  19. public bool updateMesh = true;
  20.  
  21. private void Update()
  22. {
  23. if (!updateMesh)
  24. return;
  25.  
  26. updateMesh = false;
  27. GenerateMesh();
  28. }
  29.  
  30. private void GenerateMesh()
  31. {
  32. if (vertexes.Length <= 3)
  33. return;
  34.  
  35. if (vertices == null)
  36. vertices = new List<Vector3>();
  37. vertices.Clear();
  38.  
  39. if (triangles == null)
  40. triangles = new List<int>();
  41. triangles.Clear();
  42.  
  43. if (points == null)
  44. points = new List<Vector3>();
  45. points.Clear();
  46.  
  47. Bounds bounds = new Bounds();
  48. int i = 0;
  49. int n = vertexes.Length; //多边形顶点数
  50. for (i = 0; i < n; i++)
  51. {
  52. Vector3 localPosition = vertexes[i].localPosition;
  53. points.Add(localPosition);
  54. vertices.Add(localPosition);
  55. indexDic[localPosition] = i;
  56. bounds.Encapsulate(localPosition);
  57. }
  58.  
  59. //对简单多边形进行三角剖分
  60. i = 0;
  61. Vector3 Q0 = points[i];
  62. Vector3 Q1, Q2;
  63. while(n > 3)
  64. {
  65. Q1 = points[i + 1];
  66. Q2 = points[i + 2];
  67. Debug.LogFormat("Test({0}, {1}, {2})", indexDic[Q0], indexDic[Q1], indexDic[Q2]);
  68. if (Test(Q0, Q1, Q2))
  69. {
  70. //输出三角形Q0Q1Q2
  71. triangles.Add(indexDic[Q0]);
  72. triangles.Add(indexDic[Q1]);
  73. triangles.Add(indexDic[Q2]);
  74. Debug.LogFormat("输出三角形{0},{1},{2}", indexDic[Q0], indexDic[Q1], indexDic[Q2]);
  75. //删除顶点Q1
  76. DeletePoint(Q1);
  77. n--;
  78. }
  79. else
  80. {
  81. Q0 = Q1;
  82. i++;
  83. }
  84. if (i + 2 >= points.Count)
  85. break;
  86. }
  87. //输出最后剩下的一个三角形
  88. triangles.Add(indexDic[points[0]]);
  89. triangles.Add(indexDic[points[1]]);
  90. triangles.Add(indexDic[points[2]]);
  91. Debug.LogFormat("输出三角形{0},{1},{2}", indexDic[points[0]], indexDic[points[1]], indexDic[points[2]]);
  92.  
  93. //创建Mesh
  94. if (mesh == null)
  95. mesh = new Mesh();
  96.  
  97. List<Vector2> uv = new List<Vector2>();
  98. for (i = 0; i < vertices.Count; i++)
  99. {
  100. float u = (vertices[i].x - bounds.min.x) / bounds.size.x;
  101. float v = (vertices[i].y - bounds.min.y) / bounds.size.y;
  102. uv.Add(new Vector2(u, v));
  103. }
  104. mesh.vertices = vertices.ToArray();
  105. mesh.triangles = triangles.ToArray();
  106. mesh.uv = uv.ToArray();
  107.  
  108. if (meshFilter != null)
  109. meshFilter.sharedMesh = mesh;
  110. }
  111.  
  112. private void DeletePoint(Vector3 P)
  113. {
  114. for (int i = 0; i < points.Count; i++)
  115. {
  116. Vector3 p = points[i];
  117. if (p == P)
  118. {
  119. points.RemoveAt(i);
  120. break;
  121. }
  122. }
  123. }
  124.  
  125. /// <summary>
  126. /// 检查Q0Q2是否为完全在原多边形内部的对角线(true: 是)
  127. /// </summary>
  128. /// <param name="Q0">三角形顶点</param>
  129. /// <param name="Q1">三角形顶点</param>
  130. /// <param name="Q2">三角形顶点</param>
  131. /// <returns></returns>
  132. private bool Test(Vector3 Q0, Vector3 Q1, Vector3 Q2)
  133. {
  134. Vector3 P = Q1 - Q0;
  135. Vector3 Q = Q2 - Q0;
  136.  
  137. bool zEqual = (Q0.z == Q1.z && Q1.z == Q2.z);
  138.  
  139. //这里规定顶点都按顺时针方向排列(即,向右转向)
  140. //判断向量转向,排除共线或凹角的情况
  141. if (zEqual && CrossProduct(P, Q) >= 0)
  142. {
  143. return false;//左转或共线
  144. }
  145.  
  146. //是否存在多边形的其他顶点在三角形内部
  147. for (int i=0; i<points.Count; i++)
  148. {
  149. Vector3 p = points[i];
  150. if (p == Q0 || p == Q1 | p == Q2)
  151. continue;
  152. //考虑第三维度
  153. if (p.z != Q0.z || p.z != Q1.z || p.z != Q2.z)
  154. continue;
  155. if (IsPointInTriangle(p, Q0, Q1, Q2))
  156. return false;//存在多边形的其他顶点在三角形内部
  157. }
  158. return true;
  159. }
  160.  
  161. /**
  162. * 点积(内积)
  163. * (P, Q)表示向量P和Q的夹角。
  164. *
  165. * 如果P和Q不共线,则:
  166. * P·Q > 0,则P和Q的夹角是钝角(大于90度)
  167. * P·Q < 0,则P和Q的夹角是锐角(小于90度)
  168. * P·Q = 0,则P和Q的夹角是90度
  169. */
  170. private static float DotProduct(Vector3 P, Vector3 Q)
  171. {
  172. return P.x * Q.x + P.y * Q.y + P.z * Q.z;
  173. }
  174.  
  175. // 判断P是否在三角形Q0Q1Q2内
  176. public static bool IsPointInTriangle(Vector3 P, Vector3 P0, Vector3 P1, Vector3 P2)
  177. {
  178. float s012 = CalculateTriangleArea(P0, P1, P2);
  179. double s01p = CalculateTriangleArea(P0, P1, P);
  180. double s02p = CalculateTriangleArea(P0, P2, P);
  181. double s12p = CalculateTriangleArea(P1, P2, P);
  182. return s01p + s02p + s12p <= s012;
  183. }
  184.  
  185. // 计算三角形面积
  186. public static float CalculateTriangleArea(Vector3 P0, Vector3 P1, Vector3 P2)
  187. {
  188. float s = CrossProduct(P0 - P1, P2 - P1) / 2;
  189. return s;
  190. }
  191.  
  192. /**
  193. * 叉积(外积)
  194. * P×Q = -(Q×P)
  195. *
  196. * 几何意义:
  197. * P×Q为所构成的平行四边行的面积。
  198. *
  199. * 方向:
  200. * P×Q的方向是垂直于P和Q所在的平面(右手坐标系)
  201. *
  202. * 性质:
  203. * 判断两矢量相互之间的位置关系
  204. * P×Q > 0,则Q在P的逆时针方向
  205. * P×Q < 0,则Q在P的顺时针方向
  206. * P×Q = 0,则Q与P共线
  207. */
  208. private static float CrossProduct(Vector3 P, Vector3 Q)
  209. {
  210. return (P.y*Q.z - Q.y*P.z) + (P.z*Q.x - Q.z*P.x) + (P.x*Q.y - Q.x*P.y);
  211. }
  212. }

标签: Algorithms

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号