VoxelRegion.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. //#define ASTAR_DEBUGREPLAY
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. namespace Pathfinding.Voxels {
  5. public partial class Voxelize {
  6. public bool FloodRegion (int x, int z, int i, uint level, ushort r, ushort[] srcReg, ushort[] srcDist, Int3[] stack, int[] flags = null, bool[] closed = null) {
  7. int area = voxelArea.areaTypes[i];
  8. // Flood f mark region.
  9. int stackSize = 1;
  10. stack[0] = new Int3 {
  11. x = x,
  12. y = i,
  13. z = z,
  14. };
  15. srcReg[i] = r;
  16. srcDist[i] = 0;
  17. int lev = (int)(level >= 2 ? level-2 : 0);
  18. int count = 0;
  19. // Store these in local variables (for performance, avoids an extra indirection)
  20. var DirectionX = voxelArea.DirectionX;
  21. var DirectionZ = voxelArea.DirectionZ;
  22. var compactCells = voxelArea.compactCells;
  23. var compactSpans = voxelArea.compactSpans;
  24. var areaTypes = voxelArea.areaTypes;
  25. var dist = voxelArea.dist;
  26. while (stackSize > 0) {
  27. stackSize--;
  28. var c = stack[stackSize];
  29. //Similar to the Pop operation of an array, but Pop is not implemented in List<>
  30. int ci = c.y;
  31. int cx = c.x;
  32. int cz = c.z;
  33. CompactVoxelSpan cs = compactSpans[ci];
  34. //Debug.DrawRay (ConvertPosition(cx,cz,ci),Vector3.up, Color.cyan);
  35. // Check if any of the neighbours already have a valid region set.
  36. ushort ar = 0;
  37. // Loop through four neighbours
  38. // then check one neighbour of the neighbour
  39. // to get the diagonal neighbour
  40. for (int dir = 0; dir < 4; dir++) {
  41. // 8 connected
  42. if (cs.GetConnection(dir) != NotConnected) {
  43. int ax = cx + DirectionX[dir];
  44. int az = cz + DirectionZ[dir];
  45. int ai = (int)compactCells[ax+az].index + cs.GetConnection(dir);
  46. if (areaTypes[ai] != area)
  47. continue;
  48. ushort nr = srcReg[ai];
  49. if ((nr & BorderReg) == BorderReg) // Do not take borders into account.
  50. continue;
  51. if (nr != 0 && nr != r) {
  52. ar = nr;
  53. // Found a valid region, skip checking the rest
  54. break;
  55. }
  56. // Rotate dir 90 degrees
  57. int dir2 = (dir+1) & 0x3;
  58. var neighbour2 = compactSpans[ai].GetConnection(dir2);
  59. // Check the diagonal connection
  60. if (neighbour2 != NotConnected) {
  61. int ax2 = ax + DirectionX[dir2];
  62. int az2 = az + DirectionZ[dir2];
  63. int ai2 = (int)compactCells[ax2+az2].index + neighbour2;
  64. if (areaTypes[ai2] != area)
  65. continue;
  66. ushort nr2 = srcReg[ai2];
  67. if ((nr2 & BorderReg) == BorderReg) // Do not take borders into account.
  68. continue;
  69. if (nr2 != 0 && nr2 != r) {
  70. ar = nr2;
  71. // Found a valid region, skip checking the rest
  72. break;
  73. }
  74. }
  75. }
  76. }
  77. if (ar != 0) {
  78. srcReg[ci] = 0;
  79. srcDist[ci] = (ushort)0xFFFF;
  80. continue;
  81. }
  82. count++;
  83. if (closed != null) {
  84. closed[ci] = true;
  85. }
  86. // Expand neighbours.
  87. for (int dir = 0; dir < 4; ++dir) {
  88. if (cs.GetConnection(dir) != NotConnected) {
  89. int ax = cx + DirectionX[dir];
  90. int az = cz + DirectionZ[dir];
  91. int ai = (int)compactCells[ax+az].index + cs.GetConnection(dir);
  92. if (areaTypes[ai] != area)
  93. continue;
  94. if (srcReg[ai] == 0) {
  95. if (dist[ai] >= lev && flags[ai] == 0) {
  96. srcReg[ai] = r;
  97. srcDist[ai] = 0;
  98. stack[stackSize] = new Int3 {
  99. x = ax,
  100. y = ai,
  101. z = az,
  102. };
  103. stackSize++;
  104. } else if (flags != null) {
  105. flags[ai] = r;
  106. srcDist[ai] = 2;
  107. }
  108. }
  109. }
  110. }
  111. }
  112. return count > 0;
  113. }
  114. public void MarkRectWithRegion (int minx, int maxx, int minz, int maxz, ushort region, ushort[] srcReg) {
  115. int md = maxz * voxelArea.width;
  116. for (int z = minz*voxelArea.width; z < md; z += voxelArea.width) {
  117. for (int x = minx; x < maxx; x++) {
  118. CompactVoxelCell c = voxelArea.compactCells[z+x];
  119. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; i++) {
  120. if (voxelArea.areaTypes[i] != UnwalkableArea) {
  121. srcReg[i] = region;
  122. }
  123. }
  124. }
  125. }
  126. }
  127. public ushort CalculateDistanceField (ushort[] src) {
  128. int wd = voxelArea.width*voxelArea.depth;
  129. //Mark boundary cells
  130. for (int z = 0; z < wd; z += voxelArea.width) {
  131. for (int x = 0; x < voxelArea.width; x++) {
  132. CompactVoxelCell c = voxelArea.compactCells[x+z];
  133. for (int i = (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) {
  134. CompactVoxelSpan s = voxelArea.compactSpans[i];
  135. int nc = 0;
  136. for (int d = 0; d < 4; d++) {
  137. if (s.GetConnection(d) != NotConnected) {
  138. //This function (CalculateDistanceField) is used for both ErodeWalkableArea and by itself.
  139. //The C++ recast source uses different code for those two cases, but I have found it works with one function
  140. //the voxelArea.areaTypes[ni] will actually only be one of two cases when used from ErodeWalkableArea
  141. //so it will have the same effect as
  142. // if (area != UnwalkableArea) {
  143. //This line is the one where the differ most
  144. nc++;
  145. } else {
  146. break;
  147. }
  148. }
  149. if (nc != 4) {
  150. src[i] = 0;
  151. }
  152. }
  153. }
  154. }
  155. //Pass 1
  156. for (int z = 0; z < wd; z += voxelArea.width) {
  157. for (int x = 0; x < voxelArea.width; x++) {
  158. CompactVoxelCell c = voxelArea.compactCells[x+z];
  159. for (int i = (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) {
  160. CompactVoxelSpan s = voxelArea.compactSpans[i];
  161. if (s.GetConnection(0) != NotConnected) {
  162. // (-1,0)
  163. int nx = x+voxelArea.DirectionX[0];
  164. int nz = z+voxelArea.DirectionZ[0];
  165. int ni = (int)(voxelArea.compactCells[nx+nz].index+s.GetConnection(0));
  166. if (src[ni]+2 < src[i]) {
  167. src[i] = (ushort)(src[ni]+2);
  168. }
  169. CompactVoxelSpan ns = voxelArea.compactSpans[ni];
  170. if (ns.GetConnection(3) != NotConnected) {
  171. // (-1,0) + (0,-1) = (-1,-1)
  172. int nnx = nx+voxelArea.DirectionX[3];
  173. int nnz = nz+voxelArea.DirectionZ[3];
  174. int nni = (int)(voxelArea.compactCells[nnx+nnz].index+ns.GetConnection(3));
  175. if (src[nni]+3 < src[i]) {
  176. src[i] = (ushort)(src[nni]+3);
  177. }
  178. }
  179. }
  180. if (s.GetConnection(3) != NotConnected) {
  181. // (0,-1)
  182. int nx = x+voxelArea.DirectionX[3];
  183. int nz = z+voxelArea.DirectionZ[3];
  184. int ni = (int)(voxelArea.compactCells[nx+nz].index+s.GetConnection(3));
  185. if (src[ni]+2 < src[i]) {
  186. src[i] = (ushort)(src[ni]+2);
  187. }
  188. CompactVoxelSpan ns = voxelArea.compactSpans[ni];
  189. if (ns.GetConnection(2) != NotConnected) {
  190. // (0,-1) + (1,0) = (1,-1)
  191. int nnx = nx+voxelArea.DirectionX[2];
  192. int nnz = nz+voxelArea.DirectionZ[2];
  193. int nni = (int)(voxelArea.compactCells[nnx+nnz].index+ns.GetConnection(2));
  194. if (src[nni]+3 < src[i]) {
  195. src[i] = (ushort)(src[nni]+3);
  196. }
  197. }
  198. }
  199. }
  200. }
  201. }
  202. //Pass 2
  203. for (int z = wd-voxelArea.width; z >= 0; z -= voxelArea.width) {
  204. for (int x = voxelArea.width-1; x >= 0; x--) {
  205. CompactVoxelCell c = voxelArea.compactCells[x+z];
  206. for (int i = (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) {
  207. CompactVoxelSpan s = voxelArea.compactSpans[i];
  208. if (s.GetConnection(2) != NotConnected) {
  209. // (-1,0)
  210. int nx = x+voxelArea.DirectionX[2];
  211. int nz = z+voxelArea.DirectionZ[2];
  212. int ni = (int)(voxelArea.compactCells[nx+nz].index+s.GetConnection(2));
  213. if (src[ni]+2 < src[i]) {
  214. src[i] = (ushort)(src[ni]+2);
  215. }
  216. CompactVoxelSpan ns = voxelArea.compactSpans[ni];
  217. if (ns.GetConnection(1) != NotConnected) {
  218. // (-1,0) + (0,-1) = (-1,-1)
  219. int nnx = nx+voxelArea.DirectionX[1];
  220. int nnz = nz+voxelArea.DirectionZ[1];
  221. int nni = (int)(voxelArea.compactCells[nnx+nnz].index+ns.GetConnection(1));
  222. if (src[nni]+3 < src[i]) {
  223. src[i] = (ushort)(src[nni]+3);
  224. }
  225. }
  226. }
  227. if (s.GetConnection(1) != NotConnected) {
  228. // (0,-1)
  229. int nx = x+voxelArea.DirectionX[1];
  230. int nz = z+voxelArea.DirectionZ[1];
  231. int ni = (int)(voxelArea.compactCells[nx+nz].index+s.GetConnection(1));
  232. if (src[ni]+2 < src[i]) {
  233. src[i] = (ushort)(src[ni]+2);
  234. }
  235. CompactVoxelSpan ns = voxelArea.compactSpans[ni];
  236. if (ns.GetConnection(0) != NotConnected) {
  237. // (0,-1) + (1,0) = (1,-1)
  238. int nnx = nx+voxelArea.DirectionX[0];
  239. int nnz = nz+voxelArea.DirectionZ[0];
  240. int nni = (int)(voxelArea.compactCells[nnx+nnz].index+ns.GetConnection(0));
  241. if (src[nni]+3 < src[i]) {
  242. src[i] = (ushort)(src[nni]+3);
  243. }
  244. }
  245. }
  246. }
  247. }
  248. }
  249. ushort maxDist = 0;
  250. for (int i = 0; i < voxelArea.compactSpanCount; i++) {
  251. maxDist = System.Math.Max(src[i], maxDist);
  252. }
  253. return maxDist;
  254. }
  255. public ushort[] BoxBlur (ushort[] src, ushort[] dst) {
  256. ushort thr = 20;
  257. int wd = voxelArea.width*voxelArea.depth;
  258. for (int z = wd-voxelArea.width; z >= 0; z -= voxelArea.width) {
  259. for (int x = voxelArea.width-1; x >= 0; x--) {
  260. CompactVoxelCell c = voxelArea.compactCells[x+z];
  261. for (int i = (int)c.index, ci = (int)(c.index+c.count); i < ci; i++) {
  262. CompactVoxelSpan s = voxelArea.compactSpans[i];
  263. ushort cd = src[i];
  264. if (cd < thr) {
  265. dst[i] = cd;
  266. continue;
  267. }
  268. int total = (int)cd;
  269. for (int d = 0; d < 4; d++) {
  270. if (s.GetConnection(d) != NotConnected) {
  271. int nx = x+voxelArea.DirectionX[d];
  272. int nz = z+voxelArea.DirectionZ[d];
  273. int ni = (int)(voxelArea.compactCells[nx+nz].index+s.GetConnection(d));
  274. total += (int)src[ni];
  275. CompactVoxelSpan ns = voxelArea.compactSpans[ni];
  276. int d2 = (d+1) & 0x3;
  277. if (ns.GetConnection(d2) != NotConnected) {
  278. int nnx = nx+voxelArea.DirectionX[d2];
  279. int nnz = nz+voxelArea.DirectionZ[d2];
  280. int nni = (int)(voxelArea.compactCells[nnx+nnz].index+ns.GetConnection(d2));
  281. total += (int)src[nni];
  282. } else {
  283. total += cd;
  284. }
  285. } else {
  286. total += cd*2;
  287. }
  288. }
  289. dst[i] = (ushort)((total+5)/9F);
  290. }
  291. }
  292. }
  293. return dst;
  294. }
  295. public void BuildRegions () {
  296. /*System.Diagnostics.Stopwatch w0 = new System.Diagnostics.Stopwatch();
  297. System.Diagnostics.Stopwatch w1 = new System.Diagnostics.Stopwatch();
  298. System.Diagnostics.Stopwatch w2 = new System.Diagnostics.Stopwatch();
  299. System.Diagnostics.Stopwatch w3 = new System.Diagnostics.Stopwatch();
  300. System.Diagnostics.Stopwatch w4 = new System.Diagnostics.Stopwatch();
  301. System.Diagnostics.Stopwatch w5 = new System.Diagnostics.Stopwatch();
  302. w3.Start();*/
  303. int w = voxelArea.width;
  304. int d = voxelArea.depth;
  305. int wd = w*d;
  306. int spanCount = voxelArea.compactSpanCount;
  307. int expandIterations = 8;
  308. ushort[] srcReg = Util.ArrayPool<ushort>.Claim(spanCount);
  309. ushort[] srcDist = Util.ArrayPool<ushort>.Claim(spanCount);
  310. bool[] closed = Util.ArrayPool<bool>.Claim(spanCount);
  311. int[] spanFlags = Util.ArrayPool<int>.Claim(spanCount);
  312. Int3[] stack = Util.ArrayPool<Int3>.Claim(spanCount);
  313. // The array pool arrays may contain arbitrary data. We need to zero it out.
  314. Util.Memory.MemSet(srcReg, (ushort)0, sizeof(ushort));
  315. Util.Memory.MemSet(srcDist, (ushort)0xFFFF, sizeof(ushort));
  316. Util.Memory.MemSet(closed, false, sizeof(bool));
  317. Util.Memory.MemSet(spanFlags, 0, sizeof(int));
  318. var DirectionX = voxelArea.DirectionX;
  319. var DirectionZ = voxelArea.DirectionZ;
  320. var spanDistances = voxelArea.dist;
  321. var areaTypes = voxelArea.areaTypes;
  322. var compactCells = voxelArea.compactCells;
  323. ushort regionId = 2;
  324. MarkRectWithRegion(0, borderSize, 0, d, (ushort)(regionId | BorderReg), srcReg); regionId++;
  325. MarkRectWithRegion(w-borderSize, w, 0, d, (ushort)(regionId | BorderReg), srcReg); regionId++;
  326. MarkRectWithRegion(0, w, 0, borderSize, (ushort)(regionId | BorderReg), srcReg); regionId++;
  327. MarkRectWithRegion(0, w, d-borderSize, d, (ushort)(regionId | BorderReg), srcReg); regionId++;
  328. Int3[][] buckedSortedSpans = new Int3[(voxelArea.maxDistance)/2 + 1][];
  329. int[] sortedSpanCounts = new int[buckedSortedSpans.Length];
  330. for (int i = 0; i < buckedSortedSpans.Length; i++) buckedSortedSpans[i] = new Int3[16];
  331. //w3.Stop();
  332. //w5.Start();
  333. // Bucket sort the spans based on distance
  334. for (int z = 0, pz = 0; z < wd; z += w, pz++) {
  335. for (int x = 0; x < voxelArea.width; x++) {
  336. CompactVoxelCell c = compactCells[z+x];
  337. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; i++) {
  338. if ((srcReg[i] & BorderReg) == BorderReg) // Do not take borders into account.
  339. continue;
  340. if (areaTypes[i] == UnwalkableArea)
  341. continue;
  342. int distIndex = voxelArea.dist[i] / 2;
  343. if (sortedSpanCounts[distIndex] >= buckedSortedSpans[distIndex].Length) {
  344. var newBuffer = new Int3[sortedSpanCounts[distIndex]*2];
  345. buckedSortedSpans[distIndex].CopyTo(newBuffer, 0);
  346. buckedSortedSpans[distIndex] = newBuffer;
  347. }
  348. buckedSortedSpans[distIndex][sortedSpanCounts[distIndex]++] = new Int3(x, i, z);
  349. }
  350. }
  351. }
  352. //w5.Stop();
  353. Queue<Int3> srcQue = new Queue<Int3>();
  354. Queue<Int3> dstQue = new Queue<Int3>();
  355. // Go through spans in reverse order (i.e largest distances first)
  356. for (int distIndex = buckedSortedSpans.Length - 1; distIndex >= 0; distIndex--) {
  357. var level = (uint)distIndex * 2;
  358. var spans = buckedSortedSpans[distIndex];
  359. var spansAtLevel = sortedSpanCounts[distIndex];
  360. for (int i = 0; i < spansAtLevel; i++) {
  361. int spanIndex = spans[i].y;
  362. // This span is adjacent to a region, so we should start the BFS search from it
  363. if (spanFlags[spanIndex] != 0 && srcReg[spanIndex] == 0) {
  364. srcReg[spanIndex] = (ushort)spanFlags[spanIndex];
  365. srcQue.Enqueue(spans[i]);
  366. closed[spanIndex] = true;
  367. }
  368. }
  369. // Expand a few iterations out from every known node
  370. for (int expansionIteration = 0; expansionIteration < expandIterations && srcQue.Count > 0; expansionIteration++) {
  371. while (srcQue.Count > 0) {
  372. Int3 spanInfo = srcQue.Dequeue();
  373. var area = areaTypes[spanInfo.y];
  374. var span = voxelArea.compactSpans[spanInfo.y];
  375. var region = srcReg[spanInfo.y];
  376. closed[spanInfo.y] = true;
  377. ushort nextDist = (ushort)(srcDist[spanInfo.y] + 2);
  378. // Go through the neighbours of the span
  379. for (int dir = 0; dir < 4; dir++) {
  380. var neighbour = span.GetConnection(dir);
  381. if (neighbour == NotConnected) continue;
  382. int nx = spanInfo.x + DirectionX[dir];
  383. int nz = spanInfo.z + DirectionZ[dir];
  384. int ni = (int)compactCells[nx+nz].index + neighbour;
  385. if ((srcReg[ni] & BorderReg) == BorderReg) // Do not take borders into account.
  386. continue;
  387. // Do not combine different area types
  388. if (area == areaTypes[ni]) {
  389. if (nextDist < srcDist[ni]) {
  390. if (spanDistances[ni] < level) {
  391. srcDist[ni] = nextDist;
  392. spanFlags[ni] = region;
  393. } else if (!closed[ni]) {
  394. srcDist[ni] = nextDist;
  395. if (srcReg[ni] == 0) dstQue.Enqueue(new Int3(nx, ni, nz));
  396. srcReg[ni] = region;
  397. }
  398. }
  399. }
  400. }
  401. }
  402. Util.Memory.Swap(ref srcQue, ref dstQue);
  403. }
  404. // Find the first span that has not been seen yet and start a new region that expands from there
  405. for (int i = 0; i < spansAtLevel; i++) {
  406. var info = spans[i];
  407. if (srcReg[info.y] == 0) {
  408. if (!FloodRegion(info.x, info.z, info.y, level, regionId, srcReg, srcDist, stack, spanFlags, closed)) {
  409. // The starting voxel was already adjacent to an existing region so we skip flooding it.
  410. // It will be visited in the next area expansion.
  411. } else {
  412. regionId++;
  413. }
  414. }
  415. }
  416. }
  417. voxelArea.maxRegions = regionId;
  418. // Filter out small regions.
  419. FilterSmallRegions(srcReg, minRegionSize, voxelArea.maxRegions);
  420. // Write the result out.
  421. var compactSpans = voxelArea.compactSpans;
  422. for (int i = 0; i < spanCount; i++) {
  423. compactSpans[i].reg = srcReg[i];
  424. }
  425. // Pool arrays
  426. Util.ArrayPool<ushort>.Release(ref srcReg);
  427. Util.ArrayPool<ushort>.Release(ref srcDist);
  428. Util.ArrayPool<bool>.Release(ref closed);
  429. Util.ArrayPool<int>.Release(ref spanFlags);
  430. Util.ArrayPool<Int3>.Release(ref stack);
  431. //Debug.Log(w0.Elapsed.TotalMilliseconds.ToString("0.0") + " " + w1.Elapsed.TotalMilliseconds.ToString("0.0") + " " + w2.Elapsed.TotalMilliseconds.ToString("0.0") + " " + w3.Elapsed.TotalMilliseconds.ToString("0.0") + " " + w4.Elapsed.TotalMilliseconds.ToString("0.0") + " " + w5.Elapsed.TotalMilliseconds.ToString("0.0"));
  432. }
  433. /// <summary>
  434. /// Find method in the UnionFind data structure.
  435. /// See: https://en.wikipedia.org/wiki/Disjoint-set_data_structure
  436. /// </summary>
  437. static int union_find_find (int[] arr, int x) {
  438. if (arr[x] < 0) return x;
  439. return arr[x] = union_find_find(arr, arr[x]);
  440. }
  441. /// <summary>
  442. /// Join method in the UnionFind data structure.
  443. /// See: https://en.wikipedia.org/wiki/Disjoint-set_data_structure
  444. /// </summary>
  445. static void union_find_union (int[] arr, int a, int b) {
  446. a = union_find_find(arr, a);
  447. b = union_find_find(arr, b);
  448. if (a == b) return;
  449. if (arr[a] > arr[b]) {
  450. int tmp = a;
  451. a = b;
  452. b = tmp;
  453. }
  454. arr[a] += arr[b];
  455. arr[b] = a;
  456. }
  457. /// <summary>Filters out or merges small regions.</summary>
  458. public void FilterSmallRegions (ushort[] reg, int minRegionSize, int maxRegions) {
  459. RelevantGraphSurface c = RelevantGraphSurface.Root;
  460. // Need to use ReferenceEquals because it might be called from another thread
  461. bool anySurfaces = !RelevantGraphSurface.ReferenceEquals(c, null) && (relevantGraphSurfaceMode != RecastGraph.RelevantGraphSurfaceMode.DoNotRequire);
  462. // Nothing to do here
  463. if (!anySurfaces && minRegionSize <= 0) {
  464. return;
  465. }
  466. int[] counter = new int[maxRegions];
  467. ushort[] bits = voxelArea.tmpUShortArr;
  468. if (bits == null || bits.Length < maxRegions) {
  469. bits = voxelArea.tmpUShortArr = new ushort[maxRegions];
  470. }
  471. Util.Memory.MemSet(counter, -1, sizeof(int));
  472. Util.Memory.MemSet(bits, (ushort)0, maxRegions, sizeof(ushort));
  473. int nReg = counter.Length;
  474. int wd = voxelArea.width*voxelArea.depth;
  475. const int RelevantSurfaceSet = 1 << 1;
  476. const int BorderBit = 1 << 0;
  477. // Mark RelevantGraphSurfaces
  478. // If they can also be adjacent to tile borders, this will also include the BorderBit
  479. int RelevantSurfaceCheck = RelevantSurfaceSet | ((relevantGraphSurfaceMode == RecastGraph.RelevantGraphSurfaceMode.OnlyForCompletelyInsideTile) ? BorderBit : 0x0);
  480. if (anySurfaces) {
  481. // Need to use ReferenceEquals because it might be called from another thread
  482. while (!RelevantGraphSurface.ReferenceEquals(c, null)) {
  483. int x, z;
  484. this.VectorToIndex(c.Position, out x, out z);
  485. // Check for out of bounds
  486. if (x >= 0 && z >= 0 && x < voxelArea.width && z < voxelArea.depth) {
  487. int y = (int)((c.Position.y - voxelOffset.y)/cellHeight);
  488. int rad = (int)(c.maxRange / cellHeight);
  489. CompactVoxelCell cell = voxelArea.compactCells[x+z*voxelArea.width];
  490. for (int i = (int)cell.index; i < cell.index+cell.count; i++) {
  491. CompactVoxelSpan s = voxelArea.compactSpans[i];
  492. if (System.Math.Abs(s.y - y) <= rad && reg[i] != 0) {
  493. bits[union_find_find(counter, (int)reg[i] & ~BorderReg)] |= RelevantSurfaceSet;
  494. }
  495. }
  496. }
  497. c = c.Next;
  498. }
  499. }
  500. for (int z = 0, pz = 0; z < wd; z += voxelArea.width, pz++) {
  501. for (int x = 0; x < voxelArea.width; x++) {
  502. CompactVoxelCell cell = voxelArea.compactCells[x+z];
  503. for (int i = (int)cell.index; i < cell.index+cell.count; i++) {
  504. CompactVoxelSpan s = voxelArea.compactSpans[i];
  505. int r = (int)reg[i];
  506. if ((r & ~BorderReg) == 0) continue;
  507. if (r >= nReg) { //Probably border
  508. bits[union_find_find(counter, r & ~BorderReg)] |= BorderBit;
  509. continue;
  510. }
  511. int k = union_find_find(counter, r);
  512. // Count this span
  513. counter[k]--;
  514. for (int dir = 0; dir < 4; dir++) {
  515. if (s.GetConnection(dir) == NotConnected) { continue; }
  516. int nx = x + voxelArea.DirectionX[dir];
  517. int nz = z + voxelArea.DirectionZ[dir];
  518. int ni = (int)voxelArea.compactCells[nx+nz].index + s.GetConnection(dir);
  519. int r2 = (int)reg[ni];
  520. if (r != r2 && (r2 & ~BorderReg) != 0) {
  521. if ((r2 & BorderReg) != 0) {
  522. bits[k] |= BorderBit;
  523. } else {
  524. union_find_union(counter, k, r2);
  525. }
  526. //counter[r] = minRegionSize;
  527. }
  528. }
  529. //counter[r]++;
  530. }
  531. }
  532. }
  533. // Propagate bits
  534. for (int i = 0; i < counter.Length; i++) bits[union_find_find(counter, i)] |= bits[i];
  535. for (int i = 0; i < counter.Length; i++) {
  536. int ctr = union_find_find(counter, i);
  537. // Adjacent to border
  538. if ((bits[ctr] & BorderBit) != 0) counter[ctr] = -minRegionSize-2;
  539. // Not in any relevant surface
  540. // or it is adjacent to a border (see RelevantSurfaceCheck)
  541. if (anySurfaces && (bits[ctr] & RelevantSurfaceCheck) == 0) counter[ctr] = -1;
  542. }
  543. for (int i = 0; i < voxelArea.compactSpanCount; i++) {
  544. int r = (int)reg[i];
  545. if (r >= nReg) {
  546. continue;
  547. }
  548. if (counter[union_find_find(counter, r)] >= -minRegionSize-1) {
  549. reg[i] = 0;
  550. }
  551. }
  552. }
  553. }
  554. }