VoxelContour.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. namespace Pathfinding.Voxels {
  4. public partial class Voxelize {
  5. public void BuildContours (float maxError, int maxEdgeLength, VoxelContourSet cset, int buildFlags) {
  6. AstarProfiler.StartProfile("Build Contours");
  7. AstarProfiler.StartProfile("- Init");
  8. int w = voxelArea.width;
  9. int d = voxelArea.depth;
  10. int wd = w*d;
  11. //cset.bounds = voxelArea.bounds;
  12. int maxContours = Mathf.Max(8 /*Max Regions*/, 8);
  13. //cset.conts = new VoxelContour[maxContours];
  14. List<VoxelContour> contours = new List<VoxelContour>(maxContours);
  15. AstarProfiler.EndProfile("- Init");
  16. AstarProfiler.StartProfile("- Mark Boundaries");
  17. //cset.nconts = 0;
  18. //NOTE: This array may contain any data, but since we explicitly set all data in it before we use it, it's OK.
  19. ushort[] flags = voxelArea.tmpUShortArr;
  20. if (flags.Length < voxelArea.compactSpanCount) {
  21. flags = voxelArea.tmpUShortArr = new ushort[voxelArea.compactSpanCount];
  22. }
  23. // Mark boundaries. (@?)
  24. for (int z = 0; z < wd; z += voxelArea.width) {
  25. for (int x = 0; x < voxelArea.width; x++) {
  26. CompactVoxelCell c = voxelArea.compactCells[x+z];
  27. for (int i = (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) {
  28. ushort res = 0;
  29. CompactVoxelSpan s = voxelArea.compactSpans[i];
  30. if (s.reg == 0 || (s.reg & BorderReg) == BorderReg) {
  31. flags[i] = 0;
  32. continue;
  33. }
  34. for (int dir = 0; dir < 4; dir++) {
  35. int r = 0;
  36. if (s.GetConnection(dir) != NotConnected) {
  37. int nx = x + voxelArea.DirectionX[dir];
  38. int nz = z + voxelArea.DirectionZ[dir];
  39. int ni = (int)voxelArea.compactCells[nx+nz].index + s.GetConnection(dir);
  40. r = voxelArea.compactSpans[ni].reg;
  41. }
  42. //@TODO - Why isn't this inside the previous IF
  43. if (r == s.reg) {
  44. res |= (ushort)(1 << dir);
  45. }
  46. }
  47. //Inverse, mark non connected edges.
  48. flags[i] = (ushort)(res ^ 0xf);
  49. }
  50. }
  51. }
  52. AstarProfiler.EndProfile("- Mark Boundaries");
  53. AstarProfiler.StartProfile("- Simplify Contours");
  54. List<int> verts = Pathfinding.Util.ListPool<int>.Claim(256);//new List<int> (256);
  55. List<int> simplified = Pathfinding.Util.ListPool<int>.Claim(64);//new List<int> (64);
  56. for (int z = 0; z < wd; z += voxelArea.width) {
  57. for (int x = 0; x < voxelArea.width; x++) {
  58. CompactVoxelCell c = voxelArea.compactCells[x+z];
  59. for (int i = (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) {
  60. //CompactVoxelSpan s = voxelArea.compactSpans[i];
  61. if (flags[i] == 0 || flags[i] == 0xf) {
  62. flags[i] = 0;
  63. continue;
  64. }
  65. int reg = voxelArea.compactSpans[i].reg;
  66. if (reg == 0 || (reg & BorderReg) == BorderReg) {
  67. continue;
  68. }
  69. int area = voxelArea.areaTypes[i];
  70. verts.Clear();
  71. simplified.Clear();
  72. WalkContour(x, z, i, flags, verts);
  73. SimplifyContour(verts, simplified, maxError, maxEdgeLength, buildFlags);
  74. RemoveDegenerateSegments(simplified);
  75. VoxelContour contour = new VoxelContour();
  76. contour.verts = Pathfinding.Util.ArrayPool<int>.Claim(simplified.Count);//simplified.ToArray ();
  77. for (int j = 0; j < simplified.Count; j++) contour.verts[j] = simplified[j];
  78. #if ASTAR_RECAST_INCLUDE_RAW_VERTEX_CONTOUR
  79. //Not used at the moment, just debug stuff
  80. contour.rverts = ClaimIntArr(verts.Count);
  81. for (int j = 0; j < verts.Count; j++) contour.rverts[j] = verts[j];
  82. #endif
  83. contour.nverts = simplified.Count/4;
  84. contour.reg = reg;
  85. contour.area = area;
  86. contours.Add(contour);
  87. #if ASTARDEBUG
  88. for (int q = 0, j = (simplified.Count/4)-1; q < (simplified.Count/4); j = q, q++) {
  89. int i4 = q*4;
  90. int j4 = j*4;
  91. Vector3 p1 = Vector3.Scale(
  92. new Vector3(
  93. simplified[i4+0],
  94. simplified[i4+1],
  95. (simplified[i4+2]/(float)voxelArea.width)
  96. ),
  97. cellScale)
  98. +voxelOffset;
  99. Vector3 p2 = Vector3.Scale(
  100. new Vector3(
  101. simplified[j4+0],
  102. simplified[j4+1],
  103. (simplified[j4+2]/(float)voxelArea.width)
  104. )
  105. , cellScale)
  106. +voxelOffset;
  107. if (CalcAreaOfPolygon2D(contour.verts, contour.nverts) > 0) {
  108. Debug.DrawLine(p1, p2, AstarMath.IntToColor(reg, 0.5F));
  109. } else {
  110. Debug.DrawLine(p1, p2, Color.red);
  111. }
  112. }
  113. #endif
  114. }
  115. }
  116. }
  117. Pathfinding.Util.ListPool<int>.Release(ref verts);
  118. Pathfinding.Util.ListPool<int>.Release(ref simplified);
  119. AstarProfiler.EndProfile("- Simplify Contours");
  120. AstarProfiler.StartProfile("- Fix Contours");
  121. // Check and merge droppings.
  122. // Sometimes the previous algorithms can fail and create several contours
  123. // per area. This pass will try to merge the holes into the main region.
  124. for (int i = 0; i < contours.Count; i++) {
  125. VoxelContour cont = contours[i];
  126. // Check if the contour is would backwards.
  127. if (CalcAreaOfPolygon2D(cont.verts, cont.nverts) < 0) {
  128. // Find another contour which has the same region ID.
  129. int mergeIdx = -1;
  130. for (int j = 0; j < contours.Count; j++) {
  131. if (i == j) continue;
  132. if (contours[j].nverts > 0 && contours[j].reg == cont.reg) {
  133. // Make sure the polygon is correctly oriented.
  134. if (CalcAreaOfPolygon2D(contours[j].verts, contours[j].nverts) > 0) {
  135. mergeIdx = j;
  136. break;
  137. }
  138. }
  139. }
  140. if (mergeIdx == -1) {
  141. Debug.LogError("rcBuildContours: Could not find merge target for bad contour "+i+".");
  142. } else {
  143. // Debugging
  144. // Debug.LogWarning ("Fixing contour");
  145. VoxelContour mcont = contours[mergeIdx];
  146. // Merge by closest points.
  147. int ia = 0, ib = 0;
  148. GetClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ref ia, ref ib);
  149. if (ia == -1 || ib == -1) {
  150. Debug.LogWarning("rcBuildContours: Failed to find merge points for "+i+" and "+mergeIdx+".");
  151. continue;
  152. }
  153. #if ASTARDEBUG
  154. int p4 = ia*4;
  155. int p42 = ib*4;
  156. Vector3 p12 = Vector3.Scale(
  157. new Vector3(
  158. mcont.verts[p4+0],
  159. mcont.verts[p4+1],
  160. (mcont.verts[p4+2]/(float)voxelArea.width)
  161. ),
  162. cellScale)
  163. +voxelOffset;
  164. Vector3 p22 = Vector3.Scale(
  165. new Vector3(
  166. cont.verts[p42+0],
  167. cont.verts[p42+1],
  168. (cont.verts[p42+2]/(float)voxelArea.width)
  169. )
  170. , cellScale)
  171. +voxelOffset;
  172. Debug.DrawLine(p12, p22, Color.green);
  173. #endif
  174. if (!MergeContours(ref mcont, ref cont, ia, ib)) {
  175. Debug.LogWarning("rcBuildContours: Failed to merge contours "+i+" and "+mergeIdx+".");
  176. continue;
  177. }
  178. contours[mergeIdx] = mcont;
  179. contours[i] = cont;
  180. #if ASTARDEBUG
  181. Debug.Log(mcont.nverts);
  182. for (int q = 0, j = (mcont.nverts)-1; q < (mcont.nverts); j = q, q++) {
  183. int i4 = q*4;
  184. int j4 = j*4;
  185. Vector3 p1 = Vector3.Scale(
  186. new Vector3(
  187. mcont.verts[i4+0],
  188. mcont.verts[i4+1],
  189. (mcont.verts[i4+2]/(float)voxelArea.width)
  190. ),
  191. cellScale)
  192. +voxelOffset;
  193. Vector3 p2 = Vector3.Scale(
  194. new Vector3(
  195. mcont.verts[j4+0],
  196. mcont.verts[j4+1],
  197. (mcont.verts[j4+2]/(float)voxelArea.width)
  198. )
  199. , cellScale)
  200. +voxelOffset;
  201. Debug.DrawLine(p1, p2, Color.red);
  202. //}
  203. }
  204. #endif
  205. }
  206. }
  207. }
  208. cset.conts = contours;
  209. AstarProfiler.EndProfile("- Fix Contours");
  210. AstarProfiler.EndProfile("Build Contours");
  211. }
  212. void GetClosestIndices (int[] vertsa, int nvertsa,
  213. int[] vertsb, int nvertsb,
  214. ref int ia, ref int ib) {
  215. int closestDist = 0xfffffff;
  216. ia = -1;
  217. ib = -1;
  218. for (int i = 0; i < nvertsa; i++) {
  219. //in is a keyword in C#, so I can't use that as a variable name
  220. int in2 = (i+1) % nvertsa;
  221. int ip = (i+nvertsa-1) % nvertsa;
  222. int va = i*4;
  223. int van = in2*4;
  224. int vap = ip*4;
  225. for (int j = 0; j < nvertsb; ++j) {
  226. int vb = j*4;
  227. // vb must be "infront" of va.
  228. if (Ileft(vap, va, vb, vertsa, vertsa, vertsb) && Ileft(va, van, vb, vertsa, vertsa, vertsb)) {
  229. int dx = vertsb[vb+0] - vertsa[va+0];
  230. int dz = (vertsb[vb+2]/voxelArea.width) - (vertsa[va+2]/voxelArea.width);
  231. int d = dx*dx + dz*dz;
  232. if (d < closestDist) {
  233. ia = i;
  234. ib = j;
  235. closestDist = d;
  236. }
  237. }
  238. }
  239. }
  240. }
  241. /// <summary>Releases contents of a contour set to caches</summary>
  242. static void ReleaseContours (VoxelContourSet cset) {
  243. for (int i = 0; i < cset.conts.Count; i++) {
  244. VoxelContour cont = cset.conts[i];
  245. Pathfinding.Util.ArrayPool<int>.Release(ref cont.verts);
  246. Pathfinding.Util.ArrayPool<int>.Release(ref cont.rverts);
  247. }
  248. cset.conts = null;
  249. }
  250. public static bool MergeContours (ref VoxelContour ca, ref VoxelContour cb, int ia, int ib) {
  251. int maxVerts = ca.nverts + cb.nverts + 2;
  252. int[] verts = Pathfinding.Util.ArrayPool<int>.Claim(maxVerts*4);
  253. //if (!verts)
  254. // return false;
  255. int nv = 0;
  256. // Copy contour A.
  257. for (int i = 0; i <= ca.nverts; i++) {
  258. int dst = nv*4;
  259. int src = ((ia+i) % ca.nverts)*4;
  260. verts[dst+0] = ca.verts[src+0];
  261. verts[dst+1] = ca.verts[src+1];
  262. verts[dst+2] = ca.verts[src+2];
  263. verts[dst+3] = ca.verts[src+3];
  264. nv++;
  265. }
  266. // Copy contour B
  267. for (int i = 0; i <= cb.nverts; i++) {
  268. int dst = nv*4;
  269. int src = ((ib+i) % cb.nverts)*4;
  270. verts[dst+0] = cb.verts[src+0];
  271. verts[dst+1] = cb.verts[src+1];
  272. verts[dst+2] = cb.verts[src+2];
  273. verts[dst+3] = cb.verts[src+3];
  274. nv++;
  275. }
  276. Pathfinding.Util.ArrayPool<int>.Release(ref ca.verts);
  277. Pathfinding.Util.ArrayPool<int>.Release(ref cb.verts);
  278. ca.verts = verts;
  279. ca.nverts = nv;
  280. cb.verts = Pathfinding.Util.ArrayPool<int>.Claim(0);
  281. cb.nverts = 0;
  282. return true;
  283. }
  284. public void SimplifyContour (List<int> verts, List<int> simplified, float maxError, int maxEdgeLenght, int buildFlags) {
  285. // Add initial points.
  286. bool hasConnections = false;
  287. for (int i = 0; i < verts.Count; i += 4) {
  288. if ((verts[i+3] & ContourRegMask) != 0) {
  289. hasConnections = true;
  290. break;
  291. }
  292. }
  293. if (hasConnections) {
  294. // The contour has some portals to other regions.
  295. // Add a new point to every location where the region changes.
  296. for (int i = 0, ni = verts.Count/4; i < ni; i++) {
  297. int ii = (i+1) % ni;
  298. bool differentRegs = (verts[i*4+3] & ContourRegMask) != (verts[ii*4+3] & ContourRegMask);
  299. bool areaBorders = (verts[i*4+3] & RC_AREA_BORDER) != (verts[ii*4+3] & RC_AREA_BORDER);
  300. if (differentRegs || areaBorders) {
  301. simplified.Add(verts[i*4+0]);
  302. simplified.Add(verts[i*4+1]);
  303. simplified.Add(verts[i*4+2]);
  304. simplified.Add(i);
  305. }
  306. }
  307. }
  308. if (simplified.Count == 0) {
  309. // If there is no connections at all,
  310. // create some initial points for the simplification process.
  311. // Find lower-left and upper-right vertices of the contour.
  312. int llx = verts[0];
  313. int lly = verts[1];
  314. int llz = verts[2];
  315. int lli = 0;
  316. int urx = verts[0];
  317. int ury = verts[1];
  318. int urz = verts[2];
  319. int uri = 0;
  320. for (int i = 0; i < verts.Count; i += 4) {
  321. int x = verts[i+0];
  322. int y = verts[i+1];
  323. int z = verts[i+2];
  324. if (x < llx || (x == llx && z < llz)) {
  325. llx = x;
  326. lly = y;
  327. llz = z;
  328. lli = i/4;
  329. }
  330. if (x > urx || (x == urx && z > urz)) {
  331. urx = x;
  332. ury = y;
  333. urz = z;
  334. uri = i/4;
  335. }
  336. }
  337. simplified.Add(llx);
  338. simplified.Add(lly);
  339. simplified.Add(llz);
  340. simplified.Add(lli);
  341. simplified.Add(urx);
  342. simplified.Add(ury);
  343. simplified.Add(urz);
  344. simplified.Add(uri);
  345. }
  346. // Add points until all raw points are within
  347. // error tolerance to the simplified shape.
  348. // This uses the Douglas-Peucker algorithm.
  349. int pn = verts.Count/4;
  350. //Use the max squared error instead
  351. maxError *= maxError;
  352. for (int i = 0; i < simplified.Count/4;) {
  353. int ii = (i+1) % (simplified.Count/4);
  354. int ax = simplified[i*4+0];
  355. int az = simplified[i*4+2];
  356. int ai = simplified[i*4+3];
  357. int bx = simplified[ii*4+0];
  358. int bz = simplified[ii*4+2];
  359. int bi = simplified[ii*4+3];
  360. // Find maximum deviation from the segment.
  361. float maxd = 0;
  362. int maxi = -1;
  363. int ci, cinc, endi;
  364. // Traverse the segment in lexilogical order so that the
  365. // max deviation is calculated similarly when traversing
  366. // opposite segments.
  367. if (bx > ax || (bx == ax && bz > az)) {
  368. cinc = 1;
  369. ci = (ai+cinc) % pn;
  370. endi = bi;
  371. } else {
  372. cinc = pn-1;
  373. ci = (bi+cinc) % pn;
  374. endi = ai;
  375. Util.Memory.Swap(ref ax, ref bx);
  376. Util.Memory.Swap(ref az, ref bz);
  377. }
  378. // Tessellate only outer edges or edges between areas.
  379. if ((verts[ci*4+3] & ContourRegMask) == 0 ||
  380. (verts[ci*4+3] & RC_AREA_BORDER) == RC_AREA_BORDER) {
  381. while (ci != endi) {
  382. float d2 = VectorMath.SqrDistancePointSegmentApproximate(verts[ci*4+0], verts[ci*4+2]/voxelArea.width, ax, az/voxelArea.width, bx, bz/voxelArea.width);
  383. if (d2 > maxd) {
  384. maxd = d2;
  385. maxi = ci;
  386. }
  387. ci = (ci+cinc) % pn;
  388. }
  389. }
  390. // If the max deviation is larger than accepted error,
  391. // add new point, else continue to next segment.
  392. if (maxi != -1 && maxd > maxError) {
  393. // Add space for the new point.
  394. //simplified.resize(simplified.size()+4);
  395. simplified.Add(0);
  396. simplified.Add(0);
  397. simplified.Add(0);
  398. simplified.Add(0);
  399. int n = simplified.Count/4;
  400. for (int j = n-1; j > i; --j) {
  401. simplified[j*4+0] = simplified[(j-1)*4+0];
  402. simplified[j*4+1] = simplified[(j-1)*4+1];
  403. simplified[j*4+2] = simplified[(j-1)*4+2];
  404. simplified[j*4+3] = simplified[(j-1)*4+3];
  405. }
  406. // Add the point.
  407. simplified[(i+1)*4+0] = verts[maxi*4+0];
  408. simplified[(i+1)*4+1] = verts[maxi*4+1];
  409. simplified[(i+1)*4+2] = verts[maxi*4+2];
  410. simplified[(i+1)*4+3] = maxi;
  411. } else {
  412. i++;
  413. }
  414. }
  415. //Split too long edges
  416. float maxEdgeLen = maxEdgeLength / cellSize;
  417. if (maxEdgeLen > 0 && (buildFlags & (RC_CONTOUR_TESS_WALL_EDGES|RC_CONTOUR_TESS_AREA_EDGES|RC_CONTOUR_TESS_TILE_EDGES)) != 0) {
  418. for (int i = 0; i < simplified.Count/4;) {
  419. if (simplified.Count/4 > 200) {
  420. break;
  421. }
  422. int ii = (i+1) % (simplified.Count/4);
  423. int ax = simplified[i*4+0];
  424. int az = simplified[i*4+2];
  425. int ai = simplified[i*4+3];
  426. int bx = simplified[ii*4+0];
  427. int bz = simplified[ii*4+2];
  428. int bi = simplified[ii*4+3];
  429. // Find maximum deviation from the segment.
  430. int maxi = -1;
  431. int ci = (ai+1) % pn;
  432. // Tessellate only outer edges or edges between areas.
  433. bool tess = false;
  434. // Wall edges.
  435. if ((buildFlags & RC_CONTOUR_TESS_WALL_EDGES) != 0 && (verts[ci*4+3] & ContourRegMask) == 0)
  436. tess = true;
  437. // Edges between areas.
  438. if ((buildFlags & RC_CONTOUR_TESS_AREA_EDGES) != 0 && (verts[ci*4+3] & RC_AREA_BORDER) == RC_AREA_BORDER)
  439. tess = true;
  440. // Border of tile
  441. if ((buildFlags & RC_CONTOUR_TESS_TILE_EDGES) != 0 && (verts[ci*4+3] & BorderReg) == BorderReg)
  442. tess = true;
  443. if (tess) {
  444. int dx = bx - ax;
  445. int dz = (bz/voxelArea.width) - (az/voxelArea.width);
  446. if (dx*dx + dz*dz > maxEdgeLen*maxEdgeLen) {
  447. // Round based on the segments in lexilogical order so that the
  448. // max tesselation is consistent regardles in which direction
  449. // segments are traversed.
  450. int n = bi < ai ? (bi+pn - ai) : (bi - ai);
  451. if (n > 1) {
  452. if (bx > ax || (bx == ax && bz > az)) {
  453. maxi = (ai + n/2) % pn;
  454. } else {
  455. maxi = (ai + (n+1)/2) % pn;
  456. }
  457. }
  458. }
  459. }
  460. // If the max deviation is larger than accepted error,
  461. // add new point, else continue to next segment.
  462. if (maxi != -1) {
  463. // Add space for the new point.
  464. //simplified.resize(simplified.size()+4);
  465. simplified.AddRange(new int[4]);
  466. int n = simplified.Count/4;
  467. for (int j = n-1; j > i; --j) {
  468. simplified[j*4+0] = simplified[(j-1)*4+0];
  469. simplified[j*4+1] = simplified[(j-1)*4+1];
  470. simplified[j*4+2] = simplified[(j-1)*4+2];
  471. simplified[j*4+3] = simplified[(j-1)*4+3];
  472. }
  473. // Add the point.
  474. simplified[(i+1)*4+0] = verts[maxi*4+0];
  475. simplified[(i+1)*4+1] = verts[maxi*4+1];
  476. simplified[(i+1)*4+2] = verts[maxi*4+2];
  477. simplified[(i+1)*4+3] = maxi;
  478. } else {
  479. ++i;
  480. }
  481. }
  482. }
  483. for (int i = 0; i < simplified.Count/4; i++) {
  484. // The edge vertex flag is take from the current raw point,
  485. // and the neighbour region is take from the next raw point.
  486. int ai = (simplified[i*4+3]+1) % pn;
  487. int bi = simplified[i*4+3];
  488. simplified[i*4+3] = (verts[ai*4+3] & ContourRegMask) | (verts[bi*4+3] & RC_BORDER_VERTEX);
  489. }
  490. }
  491. public void WalkContour (int x, int z, int i, ushort[] flags, List<int> verts) {
  492. // Choose the first non-connected edge
  493. int dir = 0;
  494. while ((flags[i] & (ushort)(1 << dir)) == 0) {
  495. dir++;
  496. }
  497. int startDir = dir;
  498. int startI = i;
  499. int area = voxelArea.areaTypes[i];
  500. int iter = 0;
  501. #if ASTARDEBUG
  502. Vector3 previousPos;
  503. Vector3 currentPos;
  504. previousPos = ConvertPos(
  505. x,
  506. 0,
  507. z
  508. );
  509. Vector3 previousPos2 = ConvertPos(
  510. x,
  511. 0,
  512. z
  513. );
  514. #endif
  515. while (iter++ < 40000) {
  516. //Are we facing a region edge
  517. if ((flags[i] & (ushort)(1 << dir)) != 0) {
  518. #if ASTARDEBUG
  519. Vector3 pos = ConvertPos(x, 0, z)+new Vector3((voxelArea.DirectionX[dir] != 0) ? Mathf.Sign(voxelArea.DirectionX[dir]) : 0, 0, (voxelArea.DirectionZ[dir]) != 0 ? Mathf.Sign(voxelArea.DirectionZ[dir]) : 0)*0.6F;
  520. //int dir2 = (dir+1) & 0x3;
  521. //pos += new Vector3 ((voxelArea.DirectionX[dir2] != 0) ? Mathf.Sign(voxelArea.DirectionX[dir2]) : 0,0,(voxelArea.DirectionZ[dir2]) != 0 ? Mathf.Sign(voxelArea.DirectionZ[dir2]) : 0)*1.2F;
  522. //Debug.DrawLine (ConvertPos (x,0,z),pos,Color.cyan);
  523. Debug.DrawLine(previousPos2, pos, Color.blue);
  524. previousPos2 = pos;
  525. #endif
  526. //Choose the edge corner
  527. bool isBorderVertex = false;
  528. bool isAreaBorder = false;
  529. int px = x;
  530. int py = GetCornerHeight(x, z, i, dir, ref isBorderVertex);
  531. int pz = z;
  532. switch (dir) {
  533. case 0: pz += voxelArea.width;; break;
  534. case 1: px++; pz += voxelArea.width; break;
  535. case 2: px++; break;
  536. }
  537. /*case 1: px++; break;
  538. * case 2: px++; pz += voxelArea.width; break;
  539. * case 3: pz += voxelArea.width; break;
  540. */
  541. int r = 0;
  542. CompactVoxelSpan s = voxelArea.compactSpans[i];
  543. if (s.GetConnection(dir) != NotConnected) {
  544. int nx = x + voxelArea.DirectionX[dir];
  545. int nz = z + voxelArea.DirectionZ[dir];
  546. int ni = (int)voxelArea.compactCells[nx+nz].index + s.GetConnection(dir);
  547. r = (int)voxelArea.compactSpans[ni].reg;
  548. if (area != voxelArea.areaTypes[ni]) {
  549. isAreaBorder = true;
  550. }
  551. }
  552. if (isBorderVertex) {
  553. r |= RC_BORDER_VERTEX;
  554. }
  555. if (isAreaBorder) {
  556. r |= RC_AREA_BORDER;
  557. }
  558. verts.Add(px);
  559. verts.Add(py);
  560. verts.Add(pz);
  561. verts.Add(r);
  562. //Debug.DrawRay (previousPos,new Vector3 ((dir == 1 || dir == 2) ? 1 : 0, 0, (dir == 0 || dir == 1) ? 1 : 0),Color.cyan);
  563. flags[i] = (ushort)(flags[i] & ~(1 << dir)); // Remove visited edges
  564. dir = (dir+1) & 0x3; // Rotate CW
  565. } else {
  566. int ni = -1;
  567. int nx = x + voxelArea.DirectionX[dir];
  568. int nz = z + voxelArea.DirectionZ[dir];
  569. CompactVoxelSpan s = voxelArea.compactSpans[i];
  570. if (s.GetConnection(dir) != NotConnected) {
  571. CompactVoxelCell nc = voxelArea.compactCells[nx+nz];
  572. ni = (int)nc.index + s.GetConnection(dir);
  573. }
  574. if (ni == -1) {
  575. Debug.LogWarning("Degenerate triangles might have been generated.\n" +
  576. "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.");
  577. return;
  578. }
  579. x = nx;
  580. z = nz;
  581. i = ni;
  582. // & 0x3 is the same as % 4 (modulo 4)
  583. dir = (dir+3) & 0x3; // Rotate CCW
  584. #if ASTARDEBUG
  585. currentPos = ConvertPos(
  586. x,
  587. 0,
  588. z
  589. );
  590. Debug.DrawLine(previousPos+Vector3.up*0, currentPos, Color.blue);
  591. previousPos = currentPos;
  592. #endif
  593. }
  594. if (startI == i && startDir == dir) {
  595. break;
  596. }
  597. }
  598. #if ASTARDEBUG
  599. Color col = new Color(Random.value, Random.value, Random.value);
  600. for (int q = 0, j = (verts.Count/4)-1; q < (verts.Count/4); j = q, q++) {
  601. int i4 = q*4;
  602. int j4 = j*4;
  603. Vector3 p1 = ConvertPosWithoutOffset(
  604. verts[i4+0],
  605. verts[i4+1],
  606. verts[i4+2]
  607. );
  608. Vector3 p2 = ConvertPosWithoutOffset(
  609. verts[j4+0],
  610. verts[j4+1],
  611. verts[j4+2]
  612. );
  613. Debug.DrawLine(p1, p2, col);
  614. }
  615. #endif
  616. }
  617. public int GetCornerHeight (int x, int z, int i, int dir, ref bool isBorderVertex) {
  618. CompactVoxelSpan s = voxelArea.compactSpans[i];
  619. int ch = (int)s.y;
  620. //dir + clockwise direction
  621. int dirp = (dir+1) & 0x3;
  622. //int dirp = (dir+3) & 0x3;
  623. uint[] regs = new uint[4];
  624. regs[0] = (uint)voxelArea.compactSpans[i].reg | ((uint)voxelArea.areaTypes[i] << 16);
  625. if (s.GetConnection(dir) != NotConnected) {
  626. int nx = x + voxelArea.DirectionX[dir];
  627. int nz = z + voxelArea.DirectionZ[dir];
  628. int ni = (int)voxelArea.compactCells[nx+nz].index + s.GetConnection(dir);
  629. CompactVoxelSpan ns = voxelArea.compactSpans[ni];
  630. ch = System.Math.Max(ch, (int)ns.y);
  631. regs[1] = (uint)ns.reg | ((uint)voxelArea.areaTypes[ni] << 16);
  632. if (ns.GetConnection(dirp) != NotConnected) {
  633. int nx2 = nx + voxelArea.DirectionX[dirp];
  634. int nz2 = nz + voxelArea.DirectionZ[dirp];
  635. int ni2 = (int)voxelArea.compactCells[nx2+nz2].index + ns.GetConnection(dirp);
  636. CompactVoxelSpan ns2 = voxelArea.compactSpans[ni2];
  637. ch = System.Math.Max(ch, (int)ns2.y);
  638. regs[2] = (uint)ns2.reg | ((uint)voxelArea.areaTypes[ni2] << 16);
  639. }
  640. }
  641. if (s.GetConnection(dirp) != NotConnected) {
  642. int nx = x + voxelArea.DirectionX[dirp];
  643. int nz = z + voxelArea.DirectionZ[dirp];
  644. int ni = (int)voxelArea.compactCells[nx+nz].index + s.GetConnection(dirp);
  645. CompactVoxelSpan ns = voxelArea.compactSpans[ni];
  646. ch = System.Math.Max(ch, (int)ns.y);
  647. regs[3] = (uint)ns.reg | ((uint)voxelArea.areaTypes[ni] << 16);
  648. if (ns.GetConnection(dir) != NotConnected) {
  649. int nx2 = nx + voxelArea.DirectionX[dir];
  650. int nz2 = nz + voxelArea.DirectionZ[dir];
  651. int ni2 = (int)voxelArea.compactCells[nx2+nz2].index + ns.GetConnection(dir);
  652. CompactVoxelSpan ns2 = voxelArea.compactSpans[ni2];
  653. ch = System.Math.Max(ch, (int)ns2.y);
  654. regs[2] = (uint)ns2.reg | ((uint)voxelArea.areaTypes[ni2] << 16);
  655. }
  656. }
  657. // Check if the vertex is special edge vertex, these vertices will be removed later.
  658. for (int j = 0; j < 4; ++j) {
  659. int a = j;
  660. int b = (j+1) & 0x3;
  661. int c = (j+2) & 0x3;
  662. int d = (j+3) & 0x3;
  663. // The vertex is a border vertex there are two same exterior cells in a row,
  664. // followed by two interior cells and none of the regions are out of bounds.
  665. bool twoSameExts = (regs[a] & regs[b] & BorderReg) != 0 && regs[a] == regs[b];
  666. bool twoInts = ((regs[c] | regs[d]) & BorderReg) == 0;
  667. bool intsSameArea = (regs[c]>>16) == (regs[d]>>16);
  668. bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0;
  669. if (twoSameExts && twoInts && intsSameArea && noZeros) {
  670. isBorderVertex = true;
  671. break;
  672. }
  673. }
  674. return ch;
  675. }
  676. public void RemoveDegenerateSegments (List<int> simplified) {
  677. // Remove adjacent vertices which are equal on xz-plane,
  678. // or else the triangulator will get confused
  679. for (int i = 0; i < simplified.Count/4; i++) {
  680. int ni = i+1;
  681. if (ni >= (simplified.Count/4))
  682. ni = 0;
  683. if (simplified[i*4+0] == simplified[ni*4+0] &&
  684. simplified[i*4+2] == simplified[ni*4+2]) {
  685. // Degenerate segment, remove.
  686. simplified.RemoveRange(i, 4);
  687. }
  688. }
  689. }
  690. public int CalcAreaOfPolygon2D (int[] verts, int nverts) {
  691. int area = 0;
  692. for (int i = 0, j = nverts-1; i < nverts; j = i++) {
  693. int vi = i*4;
  694. int vj = j*4;
  695. area += verts[vi+0] * (verts[vj+2]/voxelArea.width) - verts[vj+0] * (verts[vi+2]/voxelArea.width);
  696. }
  697. return (area+1) / 2;
  698. }
  699. public static bool Ileft (int a, int b, int c, int[] va, int[] vb, int[] vc) {
  700. return (vb[b+0] - va[a+0]) * (vc[c+2] - va[a+2]) - (vc[c+0] - va[a+0]) * (vb[b+2] - va[a+2]) <= 0;
  701. }
  702. }
  703. }