RecastMesh.cs 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370
  1. /*
  2. Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
  3. recast4j copyright (c) 2015-2019 Piotr Piastucki piotr@jtilia.org
  4. DotRecast Copyright (c) 2023 Choi Ikpil ikpil@naver.com
  5. This software is provided 'as-is', without any express or implied
  6. warranty. In no event will the authors be held liable for any damages
  7. arising from the use of this software.
  8. Permission is granted to anyone to use this software for any purpose,
  9. including commercial applications, and to alter it and redistribute it
  10. freely, subject to the following restrictions:
  11. 1. The origin of this software must not be misrepresented; you must not
  12. claim that you wrote the original software. If you use this software
  13. in a product, an acknowledgment in the product documentation would be
  14. appreciated but is not required.
  15. 2. Altered source versions must be plainly marked as such, and must not be
  16. misrepresented as being the original software.
  17. 3. This notice may not be removed or altered from any source distribution.
  18. */
  19. using System;
  20. using DotRecast.Core;
  21. namespace DotRecast.Recast
  22. {
  23. using static RcConstants;
  24. public static class RecastMesh
  25. {
  26. public const int MAX_MESH_VERTS_POLY = 0xffff;
  27. public const int VERTEX_BUCKET_COUNT = (1 << 12);
  28. private static void BuildMeshAdjacency(int[] polys, int npolys, int nverts, int vertsPerPoly)
  29. {
  30. // Based on code by Eric Lengyel from:
  31. // http://www.terathon.com/code/edges.php
  32. int maxEdgeCount = npolys * vertsPerPoly;
  33. int[] firstEdge = new int[nverts + maxEdgeCount];
  34. int nextEdge = nverts;
  35. int edgeCount = 0;
  36. RcEdge[] edges = new RcEdge[maxEdgeCount];
  37. for (int i = 0; i < nverts; i++)
  38. firstEdge[i] = RC_MESH_NULL_IDX;
  39. for (int i = 0; i < npolys; ++i)
  40. {
  41. int t = i * vertsPerPoly * 2;
  42. for (int j = 0; j < vertsPerPoly; ++j)
  43. {
  44. if (polys[t + j] == RC_MESH_NULL_IDX)
  45. break;
  46. int v0 = polys[t + j];
  47. int v1 = (j + 1 >= vertsPerPoly || polys[t + j + 1] == RC_MESH_NULL_IDX)
  48. ? polys[t + 0]
  49. : polys[t + j + 1];
  50. if (v0 < v1)
  51. {
  52. RcEdge edge = new RcEdge();
  53. edges[edgeCount] = edge;
  54. edge.vert[0] = v0;
  55. edge.vert[1] = v1;
  56. edge.poly[0] = i;
  57. edge.polyEdge[0] = j;
  58. edge.poly[1] = i;
  59. edge.polyEdge[1] = 0;
  60. // Insert edge
  61. firstEdge[nextEdge + edgeCount] = firstEdge[v0];
  62. firstEdge[v0] = edgeCount;
  63. edgeCount++;
  64. }
  65. }
  66. }
  67. for (int i = 0; i < npolys; ++i)
  68. {
  69. int t = i * vertsPerPoly * 2;
  70. for (int j = 0; j < vertsPerPoly; ++j)
  71. {
  72. if (polys[t + j] == RC_MESH_NULL_IDX)
  73. break;
  74. int v0 = polys[t + j];
  75. int v1 = (j + 1 >= vertsPerPoly || polys[t + j + 1] == RC_MESH_NULL_IDX)
  76. ? polys[t + 0]
  77. : polys[t + j + 1];
  78. if (v0 > v1)
  79. {
  80. for (int e = firstEdge[v1]; e != RC_MESH_NULL_IDX; e = firstEdge[nextEdge + e])
  81. {
  82. RcEdge edge = edges[e];
  83. if (edge.vert[1] == v0 && edge.poly[0] == edge.poly[1])
  84. {
  85. edge.poly[1] = i;
  86. edge.polyEdge[1] = j;
  87. break;
  88. }
  89. }
  90. }
  91. }
  92. }
  93. // Store adjacency
  94. for (int i = 0; i < edgeCount; ++i)
  95. {
  96. RcEdge e = edges[i];
  97. if (e.poly[0] != e.poly[1])
  98. {
  99. int p0 = e.poly[0] * vertsPerPoly * 2;
  100. int p1 = e.poly[1] * vertsPerPoly * 2;
  101. polys[p0 + vertsPerPoly + e.polyEdge[0]] = e.poly[1];
  102. polys[p1 + vertsPerPoly + e.polyEdge[1]] = e.poly[0];
  103. }
  104. }
  105. }
  106. private static int ComputeVertexHash(int x, int y, int z)
  107. {
  108. uint h1 = 0x8da6b343; // Large multiplicative constants;
  109. uint h2 = 0xd8163841; // here arbitrarily chosen primes
  110. uint h3 = 0xcb1ab31f;
  111. uint n = h1 * (uint)x + h2 * (uint)y + h3 * (uint)z;
  112. return (int)(n & (VERTEX_BUCKET_COUNT - 1));
  113. }
  114. private static int AddVertex(int x, int y, int z, int[] verts, int[] firstVert, int[] nextVert, ref int nv)
  115. {
  116. int bucket = ComputeVertexHash(x, 0, z);
  117. int i = firstVert[bucket];
  118. while (i != -1)
  119. {
  120. int v = i * 3;
  121. if (verts[v + 0] == x && (Math.Abs(verts[v + 1] - y) <= 2) && verts[v + 2] == z)
  122. return i;
  123. i = nextVert[i]; // next
  124. }
  125. // Could not find, create new.
  126. i = nv;
  127. nv++;
  128. int v2 = i * 3;
  129. verts[v2 + 0] = x;
  130. verts[v2 + 1] = y;
  131. verts[v2 + 2] = z;
  132. nextVert[i] = firstVert[bucket];
  133. firstVert[bucket] = i;
  134. return i;
  135. }
  136. public static int Prev(int i, int n)
  137. {
  138. return i - 1 >= 0 ? i - 1 : n - 1;
  139. }
  140. public static int Next(int i, int n)
  141. {
  142. return i + 1 < n ? i + 1 : 0;
  143. }
  144. private static int Area2(int[] verts, int a, int b, int c)
  145. {
  146. return (verts[b + 0] - verts[a + 0]) * (verts[c + 2] - verts[a + 2])
  147. - (verts[c + 0] - verts[a + 0]) * (verts[b + 2] - verts[a + 2]);
  148. }
  149. // Returns true iff c is strictly to the left of the directed
  150. // line through a to b.
  151. public static bool Left(int[] verts, int a, int b, int c)
  152. {
  153. return Area2(verts, a, b, c) < 0;
  154. }
  155. public static bool LeftOn(int[] verts, int a, int b, int c)
  156. {
  157. return Area2(verts, a, b, c) <= 0;
  158. }
  159. private static bool Collinear(int[] verts, int a, int b, int c)
  160. {
  161. return Area2(verts, a, b, c) == 0;
  162. }
  163. // Returns true iff ab properly intersects cd: they share
  164. // a point interior to both segments. The properness of the
  165. // intersection is ensured by using strict leftness.
  166. private static bool IntersectProp(int[] verts, int a, int b, int c, int d)
  167. {
  168. // Eliminate improper cases.
  169. if (Collinear(verts, a, b, c) || Collinear(verts, a, b, d) || Collinear(verts, c, d, a)
  170. || Collinear(verts, c, d, b))
  171. return false;
  172. return (Left(verts, a, b, c) ^ Left(verts, a, b, d)) && (Left(verts, c, d, a) ^ Left(verts, c, d, b));
  173. }
  174. // Returns T iff (a,b,c) are collinear and point c lies
  175. // on the closed segment ab.
  176. private static bool Between(int[] verts, int a, int b, int c)
  177. {
  178. if (!Collinear(verts, a, b, c))
  179. return false;
  180. // If ab not vertical, check betweenness on x; else on y.
  181. if (verts[a + 0] != verts[b + 0])
  182. return ((verts[a + 0] <= verts[c + 0]) && (verts[c + 0] <= verts[b + 0])) ||
  183. ((verts[a + 0] >= verts[c + 0]) && (verts[c + 0] >= verts[b + 0]));
  184. return ((verts[a + 2] <= verts[c + 2]) && (verts[c + 2] <= verts[b + 2])) ||
  185. ((verts[a + 2] >= verts[c + 2]) && (verts[c + 2] >= verts[b + 2]));
  186. }
  187. // Returns true iff segments ab and cd intersect, properly or improperly.
  188. public static bool Intersect(int[] verts, int a, int b, int c, int d)
  189. {
  190. if (IntersectProp(verts, a, b, c, d))
  191. return true;
  192. if (Between(verts, a, b, c) || Between(verts, a, b, d) ||
  193. Between(verts, c, d, a) || Between(verts, c, d, b))
  194. return true;
  195. return false;
  196. }
  197. public static bool VEqual(int[] verts, int a, int b)
  198. {
  199. return verts[a + 0] == verts[b + 0] && verts[a + 2] == verts[b + 2];
  200. }
  201. // Returns T iff (v_i, v_j) is a proper internal *or* external
  202. // diagonal of P, *ignoring edges incident to v_i and v_j*.
  203. private static bool Diagonalie(int i, int j, int n, int[] verts, int[] indices)
  204. {
  205. int d0 = (indices[i] & 0x0fffffff) * 4;
  206. int d1 = (indices[j] & 0x0fffffff) * 4;
  207. // For each edge (k,k+1) of P
  208. for (int k = 0; k < n; k++)
  209. {
  210. int k1 = Next(k, n);
  211. // Skip edges incident to i or j
  212. if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
  213. {
  214. int p0 = (indices[k] & 0x0fffffff) * 4;
  215. int p1 = (indices[k1] & 0x0fffffff) * 4;
  216. if (VEqual(verts, d0, p0) || VEqual(verts, d1, p0) || VEqual(verts, d0, p1) || VEqual(verts, d1, p1))
  217. continue;
  218. if (Intersect(verts, d0, d1, p0, p1))
  219. return false;
  220. }
  221. }
  222. return true;
  223. }
  224. // Returns true iff the diagonal (i,j) is strictly internal to the
  225. // polygon P in the neighborhood of the i endpoint.
  226. private static bool InCone(int i, int j, int n, int[] verts, int[] indices)
  227. {
  228. int pi = (indices[i] & 0x0fffffff) * 4;
  229. int pj = (indices[j] & 0x0fffffff) * 4;
  230. int pi1 = (indices[Next(i, n)] & 0x0fffffff) * 4;
  231. int pin1 = (indices[Prev(i, n)] & 0x0fffffff) * 4;
  232. // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
  233. if (LeftOn(verts, pin1, pi, pi1))
  234. {
  235. return Left(verts, pi, pj, pin1) && Left(verts, pj, pi, pi1);
  236. }
  237. // Assume (i-1,i,i+1) not collinear.
  238. // else P[i] is reflex.
  239. return !(LeftOn(verts, pi, pj, pi1) && LeftOn(verts, pj, pi, pin1));
  240. }
  241. // Returns T iff (v_i, v_j) is a proper internal
  242. // diagonal of P.
  243. private static bool Diagonal(int i, int j, int n, int[] verts, int[] indices)
  244. {
  245. return InCone(i, j, n, verts, indices) && Diagonalie(i, j, n, verts, indices);
  246. }
  247. private static bool DiagonalieLoose(int i, int j, int n, int[] verts, int[] indices)
  248. {
  249. int d0 = (indices[i] & 0x0fffffff) * 4;
  250. int d1 = (indices[j] & 0x0fffffff) * 4;
  251. // For each edge (k,k+1) of P
  252. for (int k = 0; k < n; k++)
  253. {
  254. int k1 = Next(k, n);
  255. // Skip edges incident to i or j
  256. if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
  257. {
  258. int p0 = (indices[k] & 0x0fffffff) * 4;
  259. int p1 = (indices[k1] & 0x0fffffff) * 4;
  260. if (VEqual(verts, d0, p0) || VEqual(verts, d1, p0) || VEqual(verts, d0, p1) || VEqual(verts, d1, p1))
  261. continue;
  262. if (IntersectProp(verts, d0, d1, p0, p1))
  263. return false;
  264. }
  265. }
  266. return true;
  267. }
  268. private static bool InConeLoose(int i, int j, int n, int[] verts, int[] indices)
  269. {
  270. int pi = (indices[i] & 0x0fffffff) * 4;
  271. int pj = (indices[j] & 0x0fffffff) * 4;
  272. int pi1 = (indices[Next(i, n)] & 0x0fffffff) * 4;
  273. int pin1 = (indices[Prev(i, n)] & 0x0fffffff) * 4;
  274. // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
  275. if (LeftOn(verts, pin1, pi, pi1))
  276. return LeftOn(verts, pi, pj, pin1) && LeftOn(verts, pj, pi, pi1);
  277. // Assume (i-1,i,i+1) not collinear.
  278. // else P[i] is reflex.
  279. return !(LeftOn(verts, pi, pj, pi1) && LeftOn(verts, pj, pi, pin1));
  280. }
  281. private static bool DiagonalLoose(int i, int j, int n, int[] verts, int[] indices)
  282. {
  283. return InConeLoose(i, j, n, verts, indices) && DiagonalieLoose(i, j, n, verts, indices);
  284. }
  285. private static int Triangulate(int n, int[] verts, int[] indices, int[] tris)
  286. {
  287. int ntris = 0;
  288. // The last bit of the index is used to indicate if the vertex can be removed.
  289. for (int i = 0; i < n; i++)
  290. {
  291. int i1 = Next(i, n);
  292. int i2 = Next(i1, n);
  293. if (Diagonal(i, i2, n, verts, indices))
  294. {
  295. indices[i1] |= int.MinValue; // TODO : 체크 필요
  296. }
  297. }
  298. while (n > 3)
  299. {
  300. int minLen = -1;
  301. int mini = -1;
  302. for (int minIdx = 0; minIdx < n; minIdx++)
  303. {
  304. int nextIdx1 = Next(minIdx, n);
  305. if ((indices[nextIdx1] & 0x80000000) != 0)
  306. {
  307. int p0 = (indices[minIdx] & 0x0fffffff) * 4;
  308. int p2 = (indices[Next(nextIdx1, n)] & 0x0fffffff) * 4;
  309. int dx = verts[p2 + 0] - verts[p0 + 0];
  310. int dy = verts[p2 + 2] - verts[p0 + 2];
  311. int len = dx * dx + dy * dy;
  312. if (minLen < 0 || len < minLen)
  313. {
  314. minLen = len;
  315. mini = minIdx;
  316. }
  317. }
  318. }
  319. if (mini == -1)
  320. {
  321. // We might get here because the contour has overlapping segments, like this:
  322. //
  323. // A o-o=====o---o B
  324. // / |C D| \
  325. // o o o o
  326. // : : : :
  327. // We'll try to recover by loosing up the inCone test a bit so that a diagonal
  328. // like A-B or C-D can be found and we can continue.
  329. minLen = -1;
  330. mini = -1;
  331. for (int minIdx = 0; minIdx < n; minIdx++)
  332. {
  333. int nextIdx1 = Next(minIdx, n);
  334. int nextIdx2 = Next(nextIdx1, n);
  335. if (DiagonalLoose(minIdx, nextIdx2, n, verts, indices))
  336. {
  337. int p0 = (indices[minIdx] & 0x0fffffff) * 4;
  338. int p2 = (indices[Next(nextIdx2, n)] & 0x0fffffff) * 4;
  339. int dx = verts[p2 + 0] - verts[p0 + 0];
  340. int dy = verts[p2 + 2] - verts[p0 + 2];
  341. int len = dx * dx + dy * dy;
  342. if (minLen < 0 || len < minLen)
  343. {
  344. minLen = len;
  345. mini = minIdx;
  346. }
  347. }
  348. }
  349. if (mini == -1)
  350. {
  351. // The contour is messed up. This sometimes happens
  352. // if the contour simplification is too aggressive.
  353. return -ntris;
  354. }
  355. }
  356. int i = mini;
  357. int i1 = Next(i, n);
  358. int i2 = Next(i1, n);
  359. tris[ntris * 3] = indices[i] & 0x0fffffff;
  360. tris[ntris * 3 + 1] = indices[i1] & 0x0fffffff;
  361. tris[ntris * 3 + 2] = indices[i2] & 0x0fffffff;
  362. ntris++;
  363. // Removes P[i1] by copying P[i+1]...P[n-1] left one index.
  364. n--;
  365. for (int k = i1; k < n; k++)
  366. indices[k] = indices[k + 1];
  367. if (i1 >= n)
  368. i1 = 0;
  369. i = Prev(i1, n);
  370. // Update diagonal flags.
  371. if (Diagonal(Prev(i, n), i1, n, verts, indices))
  372. indices[i] |= int.MinValue;
  373. else
  374. indices[i] &= 0x0fffffff;
  375. if (Diagonal(i, Next(i1, n), n, verts, indices))
  376. indices[i1] |= int.MinValue;
  377. else
  378. indices[i1] &= 0x0fffffff;
  379. }
  380. // Append the remaining triangle.
  381. tris[ntris * 3] = indices[0] & 0x0fffffff;
  382. tris[ntris * 3 + 1] = indices[1] & 0x0fffffff;
  383. tris[ntris * 3 + 2] = indices[2] & 0x0fffffff;
  384. ntris++;
  385. return ntris;
  386. }
  387. private static int CountPolyVerts(int[] p, int j, int nvp)
  388. {
  389. for (int i = 0; i < nvp; ++i)
  390. if (p[i + j] == RC_MESH_NULL_IDX)
  391. return i;
  392. return nvp;
  393. }
  394. private static bool Uleft(int[] verts, int a, int b, int c)
  395. {
  396. return (verts[b + 0] - verts[a + 0]) * (verts[c + 2] - verts[a + 2])
  397. - (verts[c + 0] - verts[a + 0]) * (verts[b + 2] - verts[a + 2]) < 0;
  398. }
  399. private static int GetPolyMergeValue(int[] polys, int pa, int pb, int[] verts, out int ea, out int eb, int nvp)
  400. {
  401. ea = 0;
  402. eb = 0;
  403. int na = CountPolyVerts(polys, pa, nvp);
  404. int nb = CountPolyVerts(polys, pb, nvp);
  405. // If the merged polygon would be too big, do not merge.
  406. if (na + nb - 2 > nvp)
  407. return -1;
  408. ea = -1;
  409. eb = -1;
  410. // Check if the polygons share an edge.
  411. for (int i = 0; i < na; ++i)
  412. {
  413. int va0 = polys[pa + i];
  414. int va1 = polys[pa + (i + 1) % na];
  415. if (va0 > va1)
  416. {
  417. (va0, va1) = (va1, va0);
  418. }
  419. for (int j = 0; j < nb; ++j)
  420. {
  421. int vb0 = polys[pb + j];
  422. int vb1 = polys[pb + (j + 1) % nb];
  423. if (vb0 > vb1)
  424. {
  425. (vb0, vb1) = (vb1, vb0);
  426. }
  427. if (va0 == vb0 && va1 == vb1)
  428. {
  429. ea = i;
  430. eb = j;
  431. break;
  432. }
  433. }
  434. }
  435. // No common edge, cannot merge.
  436. if (ea == -1 || eb == -1)
  437. return -1;
  438. // Check to see if the merged polygon would be convex.
  439. int va, vb, vc;
  440. va = polys[pa + (ea + na - 1) % na];
  441. vb = polys[pa + ea];
  442. vc = polys[pb + (eb + 2) % nb];
  443. if (!Uleft(verts, va * 3, vb * 3, vc * 3))
  444. return -1;
  445. va = polys[pb + (eb + nb - 1) % nb];
  446. vb = polys[pb + eb];
  447. vc = polys[pa + (ea + 2) % na];
  448. if (!Uleft(verts, va * 3, vb * 3, vc * 3))
  449. return -1;
  450. va = polys[pa + ea];
  451. vb = polys[pa + (ea + 1) % na];
  452. int dx = verts[va * 3 + 0] - verts[vb * 3 + 0];
  453. int dy = verts[va * 3 + 2] - verts[vb * 3 + 2];
  454. return (dx * dx) + (dy * dy);
  455. }
  456. private static void MergePolyVerts(int[] polys, int pa, int pb, int ea, int eb, int tmp, int nvp)
  457. {
  458. int na = CountPolyVerts(polys, pa, nvp);
  459. int nb = CountPolyVerts(polys, pb, nvp);
  460. // Merge polygons.
  461. Array.Fill(polys, RC_MESH_NULL_IDX, tmp, (tmp + nvp) - (tmp));
  462. int n = 0;
  463. // Add pa
  464. for (int i = 0; i < na - 1; ++i)
  465. {
  466. polys[tmp + n] = polys[pa + (ea + 1 + i) % na];
  467. n++;
  468. }
  469. // Add pb
  470. for (int i = 0; i < nb - 1; ++i)
  471. {
  472. polys[tmp + n] = polys[pb + (eb + 1 + i) % nb];
  473. n++;
  474. }
  475. Array.Copy(polys, tmp, polys, pa, nvp);
  476. }
  477. private static int PushFront(int v, int[] arr, int an)
  478. {
  479. an++;
  480. for (int i = an - 1; i > 0; --i)
  481. arr[i] = arr[i - 1];
  482. arr[0] = v;
  483. return an;
  484. }
  485. private static int PushBack(int v, int[] arr, int an)
  486. {
  487. arr[an] = v;
  488. an++;
  489. return an;
  490. }
  491. private static bool CanRemoveVertex(RcTelemetry ctx, RcPolyMesh mesh, int rem)
  492. {
  493. int nvp = mesh.nvp;
  494. // Count number of polygons to remove.
  495. int numTouchedVerts = 0;
  496. int numRemainingEdges = 0;
  497. for (int i = 0; i < mesh.npolys; ++i)
  498. {
  499. int p = i * nvp * 2;
  500. int nv = CountPolyVerts(mesh.polys, p, nvp);
  501. int numRemoved = 0;
  502. int numVerts = 0;
  503. for (int j = 0; j < nv; ++j)
  504. {
  505. if (mesh.polys[p + j] == rem)
  506. {
  507. numTouchedVerts++;
  508. numRemoved++;
  509. }
  510. numVerts++;
  511. }
  512. if (numRemoved != 0)
  513. {
  514. numRemainingEdges += numVerts - (numRemoved + 1);
  515. }
  516. }
  517. // There would be too few edges remaining to create a polygon.
  518. // This can happen for example when a tip of a triangle is marked
  519. // as deletion, but there are no other polys that share the vertex.
  520. // In this case, the vertex should not be removed.
  521. if (numRemainingEdges <= 2)
  522. return false;
  523. // Find edges which share the removed vertex.
  524. int maxEdges = numTouchedVerts * 2;
  525. int nedges = 0;
  526. int[] edges = new int[maxEdges * 3];
  527. for (int i = 0; i < mesh.npolys; ++i)
  528. {
  529. int p = i * nvp * 2;
  530. int nv = CountPolyVerts(mesh.polys, p, nvp);
  531. // Collect edges which touches the removed vertex.
  532. for (int j = 0, k = nv - 1; j < nv; k = j++)
  533. {
  534. if (mesh.polys[p + j] == rem || mesh.polys[p + k] == rem)
  535. {
  536. // Arrange edge so that a=rem.
  537. int a = mesh.polys[p + j], b = mesh.polys[p + k];
  538. if (b == rem)
  539. {
  540. int temp = a;
  541. a = b;
  542. b = temp;
  543. }
  544. // Check if the edge exists
  545. bool exists = false;
  546. for (int m = 0; m < nedges; ++m)
  547. {
  548. int e = m * 3;
  549. if (edges[e + 1] == b)
  550. {
  551. // Exists, increment vertex share count.
  552. edges[e + 2]++;
  553. exists = true;
  554. }
  555. }
  556. // Add new edge.
  557. if (!exists)
  558. {
  559. int e = nedges * 3;
  560. edges[e + 0] = a;
  561. edges[e + 1] = b;
  562. edges[e + 2] = 1;
  563. nedges++;
  564. }
  565. }
  566. }
  567. }
  568. // There should be no more than 2 open edges.
  569. // This catches the case that two non-adjacent polygons
  570. // share the removed vertex. In that case, do not remove the vertex.
  571. int numOpenEdges = 0;
  572. for (int i = 0; i < nedges; ++i)
  573. {
  574. if (edges[i * 3 + 2] < 2)
  575. numOpenEdges++;
  576. }
  577. if (numOpenEdges > 2)
  578. return false;
  579. return true;
  580. }
  581. private static void RemoveVertex(RcTelemetry ctx, RcPolyMesh mesh, int rem, int maxTris)
  582. {
  583. int nvp = mesh.nvp;
  584. // Count number of polygons to remove.
  585. int numRemovedVerts = 0;
  586. for (int i = 0; i < mesh.npolys; ++i)
  587. {
  588. int p = i * nvp * 2;
  589. int nv = CountPolyVerts(mesh.polys, p, nvp);
  590. for (int j = 0; j < nv; ++j)
  591. {
  592. if (mesh.polys[p + j] == rem)
  593. numRemovedVerts++;
  594. }
  595. }
  596. int nedges = 0;
  597. int[] edges = new int[numRemovedVerts * nvp * 4];
  598. int nhole = 0;
  599. int[] hole = new int[numRemovedVerts * nvp];
  600. int nhreg = 0;
  601. int[] hreg = new int[numRemovedVerts * nvp];
  602. int nharea = 0;
  603. int[] harea = new int[numRemovedVerts * nvp];
  604. for (int i = 0; i < mesh.npolys; ++i)
  605. {
  606. int p = i * nvp * 2;
  607. int nv = CountPolyVerts(mesh.polys, p, nvp);
  608. bool hasRem = false;
  609. for (int j = 0; j < nv; ++j)
  610. if (mesh.polys[p + j] == rem)
  611. hasRem = true;
  612. if (hasRem)
  613. {
  614. // Collect edges which does not touch the removed vertex.
  615. for (int j = 0, k = nv - 1; j < nv; k = j++)
  616. {
  617. if (mesh.polys[p + j] != rem && mesh.polys[p + k] != rem)
  618. {
  619. int e = nedges * 4;
  620. edges[e + 0] = mesh.polys[p + k];
  621. edges[e + 1] = mesh.polys[p + j];
  622. edges[e + 2] = mesh.regs[i];
  623. edges[e + 3] = mesh.areas[i];
  624. nedges++;
  625. }
  626. }
  627. // Remove the polygon.
  628. int p2 = (mesh.npolys - 1) * nvp * 2;
  629. if (p != p2)
  630. {
  631. Array.Copy(mesh.polys, p2, mesh.polys, p, nvp);
  632. }
  633. Array.Fill(mesh.polys, RC_MESH_NULL_IDX, p + nvp, (p + nvp + nvp) - (p + nvp));
  634. mesh.regs[i] = mesh.regs[mesh.npolys - 1];
  635. mesh.areas[i] = mesh.areas[mesh.npolys - 1];
  636. mesh.npolys--;
  637. --i;
  638. }
  639. }
  640. // Remove vertex.
  641. for (int i = rem; i < mesh.nverts - 1; ++i)
  642. {
  643. mesh.verts[i * 3 + 0] = mesh.verts[(i + 1) * 3 + 0];
  644. mesh.verts[i * 3 + 1] = mesh.verts[(i + 1) * 3 + 1];
  645. mesh.verts[i * 3 + 2] = mesh.verts[(i + 1) * 3 + 2];
  646. }
  647. mesh.nverts--;
  648. // Adjust indices to match the removed vertex layout.
  649. for (int i = 0; i < mesh.npolys; ++i)
  650. {
  651. int p = i * nvp * 2;
  652. int nv = CountPolyVerts(mesh.polys, p, nvp);
  653. for (int j = 0; j < nv; ++j)
  654. if (mesh.polys[p + j] > rem)
  655. mesh.polys[p + j]--;
  656. }
  657. for (int i = 0; i < nedges; ++i)
  658. {
  659. if (edges[i * 4 + 0] > rem)
  660. edges[i * 4 + 0]--;
  661. if (edges[i * 4 + 1] > rem)
  662. edges[i * 4 + 1]--;
  663. }
  664. if (nedges == 0)
  665. return;
  666. // Start with one vertex, keep appending connected
  667. // segments to the start and end of the hole.
  668. nhole = PushBack(edges[0], hole, nhole);
  669. nhreg = PushBack(edges[2], hreg, nhreg);
  670. nharea = PushBack(edges[3], harea, nharea);
  671. while (nedges != 0)
  672. {
  673. bool match = false;
  674. for (int i = 0; i < nedges; ++i)
  675. {
  676. int ea = edges[i * 4 + 0];
  677. int eb = edges[i * 4 + 1];
  678. int r = edges[i * 4 + 2];
  679. int a = edges[i * 4 + 3];
  680. bool add = false;
  681. if (hole[0] == eb)
  682. {
  683. // The segment matches the beginning of the hole boundary.
  684. nhole = PushFront(ea, hole, nhole);
  685. nhreg = PushFront(r, hreg, nhreg);
  686. nharea = PushFront(a, harea, nharea);
  687. add = true;
  688. }
  689. else if (hole[nhole - 1] == ea)
  690. {
  691. // The segment matches the end of the hole boundary.
  692. nhole = PushBack(eb, hole, nhole);
  693. nhreg = PushBack(r, hreg, nhreg);
  694. nharea = PushBack(a, harea, nharea);
  695. add = true;
  696. }
  697. if (add)
  698. {
  699. // The edge segment was added, remove it.
  700. edges[i * 4 + 0] = edges[(nedges - 1) * 4 + 0];
  701. edges[i * 4 + 1] = edges[(nedges - 1) * 4 + 1];
  702. edges[i * 4 + 2] = edges[(nedges - 1) * 4 + 2];
  703. edges[i * 4 + 3] = edges[(nedges - 1) * 4 + 3];
  704. --nedges;
  705. match = true;
  706. --i;
  707. }
  708. }
  709. if (!match)
  710. break;
  711. }
  712. int[] tris = new int[nhole * 3];
  713. int[] tverts = new int[nhole * 4];
  714. int[] thole = new int[nhole];
  715. // Generate temp vertex array for triangulation.
  716. for (int i = 0; i < nhole; ++i)
  717. {
  718. int pi = hole[i];
  719. tverts[i * 4 + 0] = mesh.verts[pi * 3 + 0];
  720. tverts[i * 4 + 1] = mesh.verts[pi * 3 + 1];
  721. tverts[i * 4 + 2] = mesh.verts[pi * 3 + 2];
  722. tverts[i * 4 + 3] = 0;
  723. thole[i] = i;
  724. }
  725. // Triangulate the hole.
  726. int ntris = Triangulate(nhole, tverts, thole, tris);
  727. if (ntris < 0)
  728. {
  729. ntris = -ntris;
  730. ctx.Warn("removeVertex: Triangulate() returned bad results.");
  731. }
  732. // Merge the hole triangles back to polygons.
  733. int[] polys = new int[(ntris + 1) * nvp];
  734. int[] pregs = new int[ntris];
  735. int[] pareas = new int[ntris];
  736. int tmpPoly = ntris * nvp;
  737. // Build initial polygons.
  738. int npolys = 0;
  739. Array.Fill(polys, RC_MESH_NULL_IDX, 0, (ntris * nvp) - (0));
  740. for (int j = 0; j < ntris; ++j)
  741. {
  742. int t = j * 3;
  743. if (tris[t + 0] != tris[t + 1] && tris[t + 0] != tris[t + 2] && tris[t + 1] != tris[t + 2])
  744. {
  745. polys[npolys * nvp + 0] = hole[tris[t + 0]];
  746. polys[npolys * nvp + 1] = hole[tris[t + 1]];
  747. polys[npolys * nvp + 2] = hole[tris[t + 2]];
  748. // If this polygon covers multiple region types then
  749. // mark it as such
  750. if (hreg[tris[t + 0]] != hreg[tris[t + 1]] || hreg[tris[t + 1]] != hreg[tris[t + 2]])
  751. pregs[npolys] = RC_MULTIPLE_REGS;
  752. else
  753. pregs[npolys] = hreg[tris[t + 0]];
  754. pareas[npolys] = harea[tris[t + 0]];
  755. npolys++;
  756. }
  757. }
  758. if (npolys == 0)
  759. return;
  760. // Merge polygons.
  761. if (nvp > 3)
  762. {
  763. for (;;)
  764. {
  765. // Find best polygons to merge.
  766. int bestMergeVal = 0;
  767. int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
  768. for (int j = 0; j < npolys - 1; ++j)
  769. {
  770. int pj = j * nvp;
  771. for (int k = j + 1; k < npolys; ++k)
  772. {
  773. int pk = k * nvp;
  774. var v = GetPolyMergeValue(polys, pj, pk, mesh.verts, out var ea, out var eb, nvp);
  775. if (v > bestMergeVal)
  776. {
  777. bestMergeVal = v;
  778. bestPa = j;
  779. bestPb = k;
  780. bestEa = ea;
  781. bestEb = eb;
  782. }
  783. }
  784. }
  785. if (bestMergeVal > 0)
  786. {
  787. // Found best, merge.
  788. int pa = bestPa * nvp;
  789. int pb = bestPb * nvp;
  790. MergePolyVerts(polys, pa, pb, bestEa, bestEb, tmpPoly, nvp);
  791. if (pregs[bestPa] != pregs[bestPb])
  792. pregs[bestPa] = RC_MULTIPLE_REGS;
  793. int last = (npolys - 1) * nvp;
  794. if (pb != last)
  795. {
  796. Array.Copy(polys, last, polys, pb, nvp);
  797. }
  798. pregs[bestPb] = pregs[npolys - 1];
  799. pareas[bestPb] = pareas[npolys - 1];
  800. npolys--;
  801. }
  802. else
  803. {
  804. // Could not merge any polygons, stop.
  805. break;
  806. }
  807. }
  808. }
  809. // Store polygons.
  810. for (int i = 0; i < npolys; ++i)
  811. {
  812. if (mesh.npolys >= maxTris)
  813. break;
  814. int p = mesh.npolys * nvp * 2;
  815. Array.Fill(mesh.polys, RC_MESH_NULL_IDX, p, (p + nvp * 2) - (p));
  816. for (int j = 0; j < nvp; ++j)
  817. mesh.polys[p + j] = polys[i * nvp + j];
  818. mesh.regs[mesh.npolys] = pregs[i];
  819. mesh.areas[mesh.npolys] = pareas[i];
  820. mesh.npolys++;
  821. if (mesh.npolys > maxTris)
  822. {
  823. throw new Exception("removeVertex: Too many polygons " + mesh.npolys + " (max:" + maxTris + ".");
  824. }
  825. }
  826. }
  827. /// @par
  828. ///
  829. /// @note If the mesh data is to be used to construct a Detour navigation mesh, then the upper
  830. /// limit must be restricted to <= #DT_VERTS_PER_POLYGON.
  831. ///
  832. /// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig
  833. public static RcPolyMesh BuildPolyMesh(RcTelemetry ctx, RcContourSet cset, int nvp)
  834. {
  835. using var timer = ctx.ScopedTimer(RcTimerLabel.RC_TIMER_BUILD_POLYMESH);
  836. RcPolyMesh mesh = new RcPolyMesh();
  837. mesh.bmin = cset.bmin;
  838. mesh.bmax = cset.bmax;
  839. mesh.cs = cset.cs;
  840. mesh.ch = cset.ch;
  841. mesh.borderSize = cset.borderSize;
  842. mesh.maxEdgeError = cset.maxError;
  843. int maxVertices = 0;
  844. int maxTris = 0;
  845. int maxVertsPerCont = 0;
  846. for (int i = 0; i < cset.conts.Count; ++i)
  847. {
  848. // Skip null contours.
  849. if (cset.conts[i].nverts < 3)
  850. continue;
  851. maxVertices += cset.conts[i].nverts;
  852. maxTris += cset.conts[i].nverts - 2;
  853. maxVertsPerCont = Math.Max(maxVertsPerCont, cset.conts[i].nverts);
  854. }
  855. if (maxVertices >= 0xfffe)
  856. {
  857. throw new Exception("rcBuildPolyMesh: Too many vertices " + maxVertices);
  858. }
  859. int[] vflags = new int[maxVertices];
  860. mesh.verts = new int[maxVertices * 3];
  861. mesh.polys = new int[maxTris * nvp * 2];
  862. Array.Fill(mesh.polys, RC_MESH_NULL_IDX);
  863. mesh.regs = new int[maxTris];
  864. mesh.areas = new int[maxTris];
  865. mesh.nverts = 0;
  866. mesh.npolys = 0;
  867. mesh.nvp = nvp;
  868. mesh.maxpolys = maxTris;
  869. int[] nextVert = new int[maxVertices];
  870. int[] firstVert = new int[VERTEX_BUCKET_COUNT];
  871. for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
  872. firstVert[i] = -1;
  873. int[] indices = new int[maxVertsPerCont];
  874. int[] tris = new int[maxVertsPerCont * 3];
  875. int[] polys = new int[(maxVertsPerCont + 1) * nvp];
  876. int tmpPoly = maxVertsPerCont * nvp;
  877. for (int i = 0; i < cset.conts.Count; ++i)
  878. {
  879. RcContour cont = cset.conts[i];
  880. // Skip null contours.
  881. if (cont.nverts < 3)
  882. continue;
  883. // Triangulate contour
  884. for (int j = 0; j < cont.nverts; ++j)
  885. indices[j] = j;
  886. int ntris = Triangulate(cont.nverts, cont.verts, indices, tris);
  887. if (ntris <= 0)
  888. {
  889. // Bad triangulation, should not happen.
  890. ctx.Warn("buildPolyMesh: Bad triangulation Contour " + i + ".");
  891. ntris = -ntris;
  892. }
  893. // Add and merge vertices.
  894. for (int j = 0; j < cont.nverts; ++j)
  895. {
  896. int v = j * 4;
  897. indices[j] = AddVertex(cont.verts[v + 0], cont.verts[v + 1], cont.verts[v + 2],
  898. mesh.verts, firstVert, nextVert, ref mesh.nverts);
  899. if ((cont.verts[v + 3] & RC_BORDER_VERTEX) != 0)
  900. {
  901. // This vertex should be removed.
  902. vflags[indices[j]] = 1;
  903. }
  904. }
  905. // Build initial polygons.
  906. int npolys = 0;
  907. Array.Fill(polys, RC_MESH_NULL_IDX);
  908. for (int j = 0; j < ntris; ++j)
  909. {
  910. int t = j * 3;
  911. if (tris[t + 0] != tris[t + 1] && tris[t + 0] != tris[t + 2] && tris[t + 1] != tris[t + 2])
  912. {
  913. polys[npolys * nvp + 0] = indices[tris[t + 0]];
  914. polys[npolys * nvp + 1] = indices[tris[t + 1]];
  915. polys[npolys * nvp + 2] = indices[tris[t + 2]];
  916. npolys++;
  917. }
  918. }
  919. if (npolys == 0)
  920. continue;
  921. // Merge polygons.
  922. if (nvp > 3)
  923. {
  924. for (;;)
  925. {
  926. // Find best polygons to merge.
  927. int bestMergeVal = 0;
  928. int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0;
  929. for (int j = 0; j < npolys - 1; ++j)
  930. {
  931. int pj = j * nvp;
  932. for (int k = j + 1; k < npolys; ++k)
  933. {
  934. int pk = k * nvp;
  935. var v = GetPolyMergeValue(polys, pj, pk, mesh.verts, out var ea, out var eb, nvp);
  936. if (v > bestMergeVal)
  937. {
  938. bestMergeVal = v;
  939. bestPa = j;
  940. bestPb = k;
  941. bestEa = ea;
  942. bestEb = eb;
  943. }
  944. }
  945. }
  946. if (bestMergeVal > 0)
  947. {
  948. // Found best, merge.
  949. int pa = bestPa * nvp;
  950. int pb = bestPb * nvp;
  951. MergePolyVerts(polys, pa, pb, bestEa, bestEb, tmpPoly, nvp);
  952. int lastPoly = (npolys - 1) * nvp;
  953. if (pb != lastPoly)
  954. {
  955. Array.Copy(polys, lastPoly, polys, pb, nvp);
  956. }
  957. npolys--;
  958. }
  959. else
  960. {
  961. // Could not merge any polygons, stop.
  962. break;
  963. }
  964. }
  965. }
  966. // Store polygons.
  967. for (int j = 0; j < npolys; ++j)
  968. {
  969. int p = mesh.npolys * nvp * 2;
  970. int q = j * nvp;
  971. for (int k = 0; k < nvp; ++k)
  972. mesh.polys[p + k] = polys[q + k];
  973. mesh.regs[mesh.npolys] = cont.reg;
  974. mesh.areas[mesh.npolys] = cont.area;
  975. mesh.npolys++;
  976. if (mesh.npolys > maxTris)
  977. {
  978. throw new Exception(
  979. "rcBuildPolyMesh: Too many polygons " + mesh.npolys + " (max:" + maxTris + ").");
  980. }
  981. }
  982. }
  983. // Remove edge vertices.
  984. for (int i = 0; i < mesh.nverts; ++i)
  985. {
  986. if (vflags[i] != 0)
  987. {
  988. if (!CanRemoveVertex(ctx, mesh, i))
  989. continue;
  990. RemoveVertex(ctx, mesh, i, maxTris);
  991. // Remove vertex
  992. // Note: mesh.nverts is already decremented inside RemoveVertex()!
  993. // Fixup vertex flags
  994. for (int j = i; j < mesh.nverts; ++j)
  995. vflags[j] = vflags[j + 1];
  996. --i;
  997. }
  998. }
  999. // Calculate adjacency.
  1000. BuildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp);
  1001. // Find portal edges
  1002. if (mesh.borderSize > 0)
  1003. {
  1004. int w = cset.width;
  1005. int h = cset.height;
  1006. for (int i = 0; i < mesh.npolys; ++i)
  1007. {
  1008. int p = i * 2 * nvp;
  1009. for (int j = 0; j < nvp; ++j)
  1010. {
  1011. if (mesh.polys[p + j] == RC_MESH_NULL_IDX)
  1012. break;
  1013. // Skip connected edges.
  1014. if (mesh.polys[p + nvp + j] != RC_MESH_NULL_IDX)
  1015. continue;
  1016. int nj = j + 1;
  1017. if (nj >= nvp || mesh.polys[p + nj] == RC_MESH_NULL_IDX)
  1018. nj = 0;
  1019. int va = mesh.polys[p + j] * 3;
  1020. int vb = mesh.polys[p + nj] * 3;
  1021. if (mesh.verts[va + 0] == 0 && mesh.verts[vb + 0] == 0)
  1022. mesh.polys[p + nvp + j] = 0x8000 | 0;
  1023. else if (mesh.verts[va + 2] == h && mesh.verts[vb + 2] == h)
  1024. mesh.polys[p + nvp + j] = 0x8000 | 1;
  1025. else if (mesh.verts[va + 0] == w && mesh.verts[vb + 0] == w)
  1026. mesh.polys[p + nvp + j] = 0x8000 | 2;
  1027. else if (mesh.verts[va + 2] == 0 && mesh.verts[vb + 2] == 0)
  1028. mesh.polys[p + nvp + j] = 0x8000 | 3;
  1029. }
  1030. }
  1031. }
  1032. // Just allocate the mesh flags array. The user is resposible to fill it.
  1033. mesh.flags = new int[mesh.npolys];
  1034. if (mesh.nverts > MAX_MESH_VERTS_POLY)
  1035. {
  1036. throw new Exception("rcBuildPolyMesh: The resulting mesh has too many vertices " + mesh.nverts
  1037. + " (max " + MAX_MESH_VERTS_POLY + "). Data can be corrupted.");
  1038. }
  1039. if (mesh.npolys > MAX_MESH_VERTS_POLY)
  1040. {
  1041. throw new Exception("rcBuildPolyMesh: The resulting mesh has too many polygons " + mesh.npolys
  1042. + " (max " + MAX_MESH_VERTS_POLY + "). Data can be corrupted.");
  1043. }
  1044. return mesh;
  1045. }
  1046. /// @see rcAllocPolyMesh, rcPolyMesh
  1047. public static RcPolyMesh MergePolyMeshes(RcTelemetry ctx, RcPolyMesh[] meshes, int nmeshes)
  1048. {
  1049. if (nmeshes == 0 || meshes == null)
  1050. return null;
  1051. using var timer = ctx.ScopedTimer(RcTimerLabel.RC_TIMER_MERGE_POLYMESH);
  1052. RcPolyMesh mesh = new RcPolyMesh();
  1053. mesh.nvp = meshes[0].nvp;
  1054. mesh.cs = meshes[0].cs;
  1055. mesh.ch = meshes[0].ch;
  1056. mesh.bmin = meshes[0].bmin;
  1057. mesh.bmax = meshes[0].bmax;
  1058. int maxVerts = 0;
  1059. int maxPolys = 0;
  1060. int maxVertsPerMesh = 0;
  1061. for (int i = 0; i < nmeshes; ++i)
  1062. {
  1063. mesh.bmin.Min(meshes[i].bmin);
  1064. mesh.bmax.Max(meshes[i].bmax);
  1065. maxVertsPerMesh = Math.Max(maxVertsPerMesh, meshes[i].nverts);
  1066. maxVerts += meshes[i].nverts;
  1067. maxPolys += meshes[i].npolys;
  1068. }
  1069. mesh.nverts = 0;
  1070. mesh.verts = new int[maxVerts * 3];
  1071. mesh.npolys = 0;
  1072. mesh.polys = new int[maxPolys * 2 * mesh.nvp];
  1073. Array.Fill(mesh.polys, RC_MESH_NULL_IDX, 0, (mesh.polys.Length) - (0));
  1074. mesh.regs = new int[maxPolys];
  1075. mesh.areas = new int[maxPolys];
  1076. mesh.flags = new int[maxPolys];
  1077. int[] nextVert = new int[maxVerts];
  1078. int[] firstVert = new int[VERTEX_BUCKET_COUNT];
  1079. for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
  1080. firstVert[i] = -1;
  1081. int[] vremap = new int[maxVertsPerMesh];
  1082. for (int i = 0; i < nmeshes; ++i)
  1083. {
  1084. RcPolyMesh pmesh = meshes[i];
  1085. int ox = (int)Math.Floor((pmesh.bmin.x - mesh.bmin.x) / mesh.cs + 0.5f);
  1086. int oz = (int)Math.Floor((pmesh.bmin.z - mesh.bmin.z) / mesh.cs + 0.5f);
  1087. bool isMinX = (ox == 0);
  1088. bool isMinZ = (oz == 0);
  1089. bool isMaxX = (Math.Floor((mesh.bmax.x - pmesh.bmax.x) / mesh.cs + 0.5f)) == 0;
  1090. bool isMaxZ = (Math.Floor((mesh.bmax.z - pmesh.bmax.z) / mesh.cs + 0.5f)) == 0;
  1091. bool isOnBorder = (isMinX || isMinZ || isMaxX || isMaxZ);
  1092. for (int j = 0; j < pmesh.nverts; ++j)
  1093. {
  1094. int v = j * 3;
  1095. vremap[j] = AddVertex(pmesh.verts[v + 0] + ox, pmesh.verts[v + 1], pmesh.verts[v + 2] + oz,
  1096. mesh.verts, firstVert, nextVert, ref mesh.nverts);
  1097. }
  1098. for (int j = 0; j < pmesh.npolys; ++j)
  1099. {
  1100. int tgt = mesh.npolys * 2 * mesh.nvp;
  1101. int src = j * 2 * mesh.nvp;
  1102. mesh.regs[mesh.npolys] = pmesh.regs[j];
  1103. mesh.areas[mesh.npolys] = pmesh.areas[j];
  1104. mesh.flags[mesh.npolys] = pmesh.flags[j];
  1105. mesh.npolys++;
  1106. for (int k = 0; k < mesh.nvp; ++k)
  1107. {
  1108. if (pmesh.polys[src + k] == RC_MESH_NULL_IDX)
  1109. break;
  1110. mesh.polys[tgt + k] = vremap[pmesh.polys[src + k]];
  1111. }
  1112. if (isOnBorder)
  1113. {
  1114. for (int k = mesh.nvp; k < mesh.nvp * 2; ++k)
  1115. {
  1116. if ((pmesh.polys[src + k] & 0x8000) != 0 && pmesh.polys[src + k] != 0xffff)
  1117. {
  1118. int dir = pmesh.polys[src + k] & 0xf;
  1119. switch (dir)
  1120. {
  1121. case 0: // Portal x-
  1122. if (isMinX)
  1123. mesh.polys[tgt + k] = pmesh.polys[src + k];
  1124. break;
  1125. case 1: // Portal z+
  1126. if (isMaxZ)
  1127. mesh.polys[tgt + k] = pmesh.polys[src + k];
  1128. break;
  1129. case 2: // Portal x+
  1130. if (isMaxX)
  1131. mesh.polys[tgt + k] = pmesh.polys[src + k];
  1132. break;
  1133. case 3: // Portal z-
  1134. if (isMinZ)
  1135. mesh.polys[tgt + k] = pmesh.polys[src + k];
  1136. break;
  1137. }
  1138. }
  1139. }
  1140. }
  1141. }
  1142. }
  1143. // Calculate adjacency.
  1144. BuildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp);
  1145. if (mesh.nverts > MAX_MESH_VERTS_POLY)
  1146. {
  1147. throw new Exception("rcBuildPolyMesh: The resulting mesh has too many vertices " + mesh.nverts
  1148. + " (max " + MAX_MESH_VERTS_POLY + "). Data can be corrupted.");
  1149. }
  1150. if (mesh.npolys > MAX_MESH_VERTS_POLY)
  1151. {
  1152. throw new Exception("rcBuildPolyMesh: The resulting mesh has too many polygons " + mesh.npolys
  1153. + " (max " + MAX_MESH_VERTS_POLY + "). Data can be corrupted.");
  1154. }
  1155. return mesh;
  1156. }
  1157. public static RcPolyMesh CopyPolyMesh(RcTelemetry ctx, RcPolyMesh src)
  1158. {
  1159. RcPolyMesh dst = new RcPolyMesh();
  1160. dst.nverts = src.nverts;
  1161. dst.npolys = src.npolys;
  1162. dst.maxpolys = src.npolys;
  1163. dst.nvp = src.nvp;
  1164. dst.bmin = src.bmin;
  1165. dst.bmax = src.bmax;
  1166. dst.cs = src.cs;
  1167. dst.ch = src.ch;
  1168. dst.borderSize = src.borderSize;
  1169. dst.maxEdgeError = src.maxEdgeError;
  1170. dst.verts = new int[src.nverts * 3];
  1171. Array.Copy(src.verts, 0, dst.verts, 0, dst.verts.Length);
  1172. dst.polys = new int[src.npolys * 2 * src.nvp];
  1173. Array.Copy(src.polys, 0, dst.polys, 0, dst.polys.Length);
  1174. dst.regs = new int[src.npolys];
  1175. Array.Copy(src.regs, 0, dst.regs, 0, dst.regs.Length);
  1176. dst.areas = new int[src.npolys];
  1177. Array.Copy(src.areas, 0, dst.areas, 0, dst.areas.Length);
  1178. dst.flags = new int[src.npolys];
  1179. Array.Copy(src.flags, 0, dst.flags, 0, dst.flags.Length);
  1180. return dst;
  1181. }
  1182. }
  1183. }