VoxelMesh.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. using UnityEngine;
  2. namespace Pathfinding.Voxels {
  3. using Pathfinding.Util;
  4. public partial class Voxelize {
  5. /// <summary>
  6. /// Returns T iff (v_i, v_j) is a proper internal
  7. /// diagonal of P.
  8. /// </summary>
  9. public static bool Diagonal (int i, int j, int n, int[] verts, int[] indices) {
  10. return InCone(i, j, n, verts, indices) && Diagonalie(i, j, n, verts, indices);
  11. }
  12. public static bool InCone (int i, int j, int n, int[] verts, int[] indices) {
  13. int pi = (indices[i] & 0x0fffffff) * 4;
  14. int pj = (indices[j] & 0x0fffffff) * 4;
  15. int pi1 = (indices[Next(i, n)] & 0x0fffffff) * 4;
  16. int pin1 = (indices[Prev(i, n)] & 0x0fffffff) * 4;
  17. // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
  18. if (LeftOn(pin1, pi, pi1, verts))
  19. return Left(pi, pj, pin1, verts) && Left(pj, pi, pi1, verts);
  20. // Assume (i-1,i,i+1) not collinear.
  21. // else P[i] is reflex.
  22. return !(LeftOn(pi, pj, pi1, verts) && LeftOn(pj, pi, pin1, verts));
  23. }
  24. /// <summary>
  25. /// Returns true iff c is strictly to the left of the directed
  26. /// line through a to b.
  27. /// </summary>
  28. public static bool Left (int a, int b, int c, int[] verts) {
  29. return Area2(a, b, c, verts) < 0;
  30. }
  31. public static bool LeftOn (int a, int b, int c, int[] verts) {
  32. return Area2(a, b, c, verts) <= 0;
  33. }
  34. public static bool Collinear (int a, int b, int c, int[] verts) {
  35. return Area2(a, b, c, verts) == 0;
  36. }
  37. public static int Area2 (int a, int b, int c, int[] verts) {
  38. return (verts[b] - verts[a]) * (verts[c+2] - verts[a+2]) - (verts[c+0] - verts[a+0]) * (verts[b+2] - verts[a+2]);
  39. }
  40. /// <summary>
  41. /// Returns T iff (v_i, v_j) is a proper internal *or* external
  42. /// diagonal of P, *ignoring edges incident to v_i and v_j*.
  43. /// </summary>
  44. static bool Diagonalie (int i, int j, int n, int[] verts, int[] indices) {
  45. int d0 = (indices[i] & 0x0fffffff) * 4;
  46. int d1 = (indices[j] & 0x0fffffff) * 4;
  47. /*int a = (i+1) % indices.Length;
  48. * if (a == j) a = (i-1 + indices.Length) % indices.Length;
  49. * int a_v = (indices[a] & 0x0fffffff) * 4;
  50. *
  51. * if (a != j && Collinear (d0,a_v,d1,verts)) {
  52. * return false;
  53. * }*/
  54. // For each edge (k,k+1) of P
  55. for (int k = 0; k < n; k++) {
  56. int k1 = Next(k, n);
  57. // Skip edges incident to i or j
  58. if (!((k == i) || (k1 == i) || (k == j) || (k1 == j))) {
  59. int p0 = (indices[k] & 0x0fffffff) * 4;
  60. int p1 = (indices[k1] & 0x0fffffff) * 4;
  61. if (Vequal(d0, p0, verts) || Vequal(d1, p0, verts) || Vequal(d0, p1, verts) || Vequal(d1, p1, verts))
  62. continue;
  63. if (Intersect(d0, d1, p0, p1, verts))
  64. return false;
  65. }
  66. }
  67. return true;
  68. }
  69. // Exclusive or: true iff exactly one argument is true.
  70. // The arguments are negated to ensure that they are 0/1
  71. // values. Then the bitwise Xor operator may apply.
  72. // (This idea is due to Michael Baldwin.)
  73. public static bool Xorb (bool x, bool y) {
  74. return !x ^ !y;
  75. }
  76. // Returns true iff ab properly intersects cd: they share
  77. // a point interior to both segments. The properness of the
  78. // intersection is ensured by using strict leftness.
  79. public static bool IntersectProp (int a, int b, int c, int d, int[] verts) {
  80. // Eliminate improper cases.
  81. if (Collinear(a, b, c, verts) || Collinear(a, b, d, verts) ||
  82. Collinear(c, d, a, verts) || Collinear(c, d, b, verts))
  83. return false;
  84. return Xorb(Left(a, b, c, verts), Left(a, b, d, verts)) && Xorb(Left(c, d, a, verts), Left(c, d, b, verts));
  85. }
  86. // Returns T iff (a,b,c) are collinear and point c lies
  87. // on the closed segement ab.
  88. static bool Between (int a, int b, int c, int[] verts) {
  89. if (!Collinear(a, b, c, verts))
  90. return false;
  91. // If ab not vertical, check betweenness on x; else on y.
  92. if (verts[a+0] != verts[b+0])
  93. return ((verts[a+0] <= verts[c+0]) && (verts[c+0] <= verts[b+0])) || ((verts[a+0] >= verts[c+0]) && (verts[c+0] >= verts[b+0]));
  94. else
  95. return ((verts[a+2] <= verts[c+2]) && (verts[c+2] <= verts[b+2])) || ((verts[a+2] >= verts[c+2]) && (verts[c+2] >= verts[b+2]));
  96. }
  97. // Returns true iff segments ab and cd intersect, properly or improperly.
  98. static bool Intersect (int a, int b, int c, int d, int[] verts) {
  99. if (IntersectProp(a, b, c, d, verts))
  100. return true;
  101. else if (Between(a, b, c, verts) || Between(a, b, d, verts) ||
  102. Between(c, d, a, verts) || Between(c, d, b, verts))
  103. return true;
  104. else
  105. return false;
  106. }
  107. static bool Vequal (int a, int b, int[] verts) {
  108. return verts[a+0] == verts[b+0] && verts[a+2] == verts[b+2];
  109. }
  110. /// <summary>(i-1+n) % n assuming 0 <= i < n</summary>
  111. public static int Prev (int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
  112. /// <summary>(i+1) % n assuming 0 <= i < n</summary>
  113. public static int Next (int i, int n) { return i+1 < n ? i+1 : 0; }
  114. /// <summary>
  115. /// Builds a polygon mesh from a contour set.
  116. ///
  117. /// Warning: Currently locked to 3.
  118. /// </summary>
  119. /// <param name="cset">contour set to build a mesh from.</param>
  120. /// <param name="nvp">Maximum allowed vertices per polygon.</param>
  121. /// <param name="mesh">Results will be written to this mesh.</param>
  122. public void BuildPolyMesh (VoxelContourSet cset, int nvp, out VoxelMesh mesh) {
  123. AstarProfiler.StartProfile("Build Poly Mesh");
  124. nvp = 3;
  125. int maxVertices = 0;
  126. int maxTris = 0;
  127. int maxVertsPerCont = 0;
  128. for (int i = 0; i < cset.conts.Count; i++) {
  129. // Skip null contours.
  130. if (cset.conts[i].nverts < 3) continue;
  131. maxVertices += cset.conts[i].nverts;
  132. maxTris += cset.conts[i].nverts - 2;
  133. maxVertsPerCont = System.Math.Max(maxVertsPerCont, cset.conts[i].nverts);
  134. }
  135. Int3[] verts = ArrayPool<Int3>.Claim(maxVertices);
  136. int[] polys = ArrayPool<int>.Claim(maxTris*nvp);
  137. int[] areas = ArrayPool<int>.Claim(maxTris);
  138. Pathfinding.Util.Memory.MemSet<int>(polys, 0xff, sizeof(int));
  139. int[] indices = ArrayPool<int>.Claim(maxVertsPerCont);
  140. int[] tris = ArrayPool<int>.Claim(maxVertsPerCont*3);
  141. int vertexIndex = 0;
  142. int polyIndex = 0;
  143. int areaIndex = 0;
  144. for (int i = 0; i < cset.conts.Count; i++) {
  145. VoxelContour cont = cset.conts[i];
  146. // Skip degenerate contours
  147. if (cont.nverts < 3) {
  148. continue;
  149. }
  150. for (int j = 0; j < cont.nverts; j++) {
  151. indices[j] = j;
  152. // Convert the z coordinate from the form z*voxelArea.width which is used in other places for performance
  153. cont.verts[j*4+2] /= voxelArea.width;
  154. }
  155. // Triangulate the contour
  156. int ntris = Triangulate(cont.nverts, cont.verts, ref indices, ref tris);
  157. // Assign the correct vertex indices
  158. int startIndex = vertexIndex;
  159. for (int j = 0; j < ntris*3; polyIndex++, j++) {
  160. //@Error sometimes
  161. polys[polyIndex] = tris[j]+startIndex;
  162. }
  163. // Mark all triangles generated by this contour
  164. // as having the area cont.area
  165. for (int j = 0; j < ntris; areaIndex++, j++) {
  166. areas[areaIndex] = cont.area;
  167. }
  168. // Copy the vertex positions
  169. for (int j = 0; j < cont.nverts; vertexIndex++, j++) {
  170. verts[vertexIndex] = new Int3(cont.verts[j*4], cont.verts[j*4+1], cont.verts[j*4+2]);
  171. }
  172. }
  173. mesh = new VoxelMesh {
  174. verts = Memory.ShrinkArray(verts, vertexIndex),
  175. tris = Memory.ShrinkArray(polys, polyIndex),
  176. areas = Memory.ShrinkArray(areas, areaIndex)
  177. };
  178. ArrayPool<Int3>.Release(ref verts);
  179. ArrayPool<int>.Release(ref polys);
  180. ArrayPool<int>.Release(ref areas);
  181. ArrayPool<int>.Release(ref indices);
  182. ArrayPool<int>.Release(ref tris);
  183. AstarProfiler.EndProfile("Build Poly Mesh");
  184. }
  185. int Triangulate (int n, int[] verts, ref int[] indices, ref int[] tris) {
  186. int ntris = 0;
  187. int[] dst = tris;
  188. int dstIndex = 0;
  189. // Debug code
  190. //int on = n;
  191. // The last bit of the index is used to indicate if the vertex can be removed.
  192. for (int i = 0; i < n; i++) {
  193. int i1 = Next(i, n);
  194. int i2 = Next(i1, n);
  195. if (Diagonal(i, i2, n, verts, indices)) {
  196. indices[i1] |= 0x40000000;
  197. }
  198. }
  199. while (n > 3) {
  200. #if ASTARDEBUG
  201. for (int j = 0; j < n; j++) {
  202. DrawLine(Prev(j, n), j, indices, verts, Color.red);
  203. }
  204. #endif
  205. int minLen = -1;
  206. int mini = -1;
  207. for (int q = 0; q < n; q++) {
  208. int q1 = Next(q, n);
  209. if ((indices[q1] & 0x40000000) != 0) {
  210. int p0 = (indices[q] & 0x0fffffff) * 4;
  211. int p2 = (indices[Next(q1, n)] & 0x0fffffff) * 4;
  212. int dx = verts[p2+0] - verts[p0+0];
  213. int dz = verts[p2+2] - verts[p0+2];
  214. #if ASTARDEBUG
  215. DrawLine(q, Next(q1, n), indices, verts, Color.blue);
  216. #endif
  217. //Squared distance
  218. int len = dx*dx + dz*dz;
  219. if (minLen < 0 || len < minLen) {
  220. minLen = len;
  221. mini = q;
  222. }
  223. }
  224. }
  225. if (mini == -1) {
  226. Debug.LogWarning("Degenerate triangles might have been generated.\n" +
  227. "Usually this is not a problem, but if you have a static level, try to modify the graph settings slightly to avoid this edge case.");
  228. // Can't run the debug stuff because we are likely running from a separate thread
  229. //for (int j=0;j<on;j++) {
  230. // DrawLine (Prev(j,on),j,indices,verts,Color.red);
  231. //}
  232. // Should not happen.
  233. /* printf("mini == -1 ntris=%d n=%d\n", ntris, n);
  234. * for (int i = 0; i < n; i++)
  235. * {
  236. * printf("%d ", indices[i] & 0x0fffffff);
  237. * }
  238. * printf("\n");*/
  239. //yield break;
  240. return -ntris;
  241. }
  242. int i = mini;
  243. int i1 = Next(i, n);
  244. int i2 = Next(i1, n);
  245. #if ASTARDEBUG
  246. for (int j = 0; j < n; j++) {
  247. DrawLine(Prev(j, n), j, indices, verts, Color.red);
  248. }
  249. DrawLine(i, i2, indices, verts, Color.magenta);
  250. for (int j = 0; j < n; j++) {
  251. DrawLine(Prev(j, n), j, indices, verts, Color.red);
  252. }
  253. #endif
  254. dst[dstIndex] = indices[i] & 0x0fffffff;
  255. dstIndex++;
  256. dst[dstIndex] = indices[i1] & 0x0fffffff;
  257. dstIndex++;
  258. dst[dstIndex] = indices[i2] & 0x0fffffff;
  259. dstIndex++;
  260. ntris++;
  261. // Removes P[i1] by copying P[i+1]...P[n-1] left one index.
  262. n--;
  263. for (int k = i1; k < n; k++) {
  264. indices[k] = indices[k+1];
  265. }
  266. if (i1 >= n) i1 = 0;
  267. i = Prev(i1, n);
  268. // Update diagonal flags.
  269. if (Diagonal(Prev(i, n), i1, n, verts, indices)) {
  270. indices[i] |= 0x40000000;
  271. } else {
  272. indices[i] &= 0x0fffffff;
  273. }
  274. if (Diagonal(i, Next(i1, n), n, verts, indices)) {
  275. indices[i1] |= 0x40000000;
  276. } else {
  277. indices[i1] &= 0x0fffffff;
  278. }
  279. }
  280. dst[dstIndex] = indices[0] & 0x0fffffff;
  281. dstIndex++;
  282. dst[dstIndex] = indices[1] & 0x0fffffff;
  283. dstIndex++;
  284. dst[dstIndex] = indices[2] & 0x0fffffff;
  285. dstIndex++;
  286. ntris++;
  287. return ntris;
  288. }
  289. }
  290. }