RecastRegion.cpp 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337
  1. //
  2. // Copyright (c) 2009-2010 Mikko Mononen [email protected]
  3. //
  4. // This software is provided 'as-is', without any express or implied
  5. // warranty. In no event will the authors be held liable for any damages
  6. // arising from the use of this software.
  7. // Permission is granted to anyone to use this software for any purpose,
  8. // including commercial applications, and to alter it and redistribute it
  9. // freely, subject to the following restrictions:
  10. // 1. The origin of this software must not be misrepresented; you must not
  11. // claim that you wrote the original software. If you use this software
  12. // in a product, an acknowledgment in the product documentation would be
  13. // appreciated but is not required.
  14. // 2. Altered source versions must be plainly marked as such, and must not be
  15. // misrepresented as being the original software.
  16. // 3. This notice may not be removed or altered from any source distribution.
  17. //
  18. #include <float.h>
  19. #define _USE_MATH_DEFINES
  20. #include <math.h>
  21. #include <string.h>
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include "Recast.h"
  25. #include "RecastAlloc.h"
  26. #include "RecastAssert.h"
  27. #include <new>
  28. static void calculateDistanceField(rcCompactHeightfield& chf, unsigned short* src, unsigned short& maxDist)
  29. {
  30. const int w = chf.width;
  31. const int h = chf.height;
  32. // Init distance and points.
  33. for (int i = 0; i < chf.spanCount; ++i)
  34. src[i] = 0xffff;
  35. // Mark boundary cells.
  36. for (int y = 0; y < h; ++y)
  37. {
  38. for (int x = 0; x < w; ++x)
  39. {
  40. const rcCompactCell& c = chf.cells[x+y*w];
  41. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
  42. {
  43. const rcCompactSpan& s = chf.spans[i];
  44. const unsigned char area = chf.areas[i];
  45. int nc = 0;
  46. for (int dir = 0; dir < 4; ++dir)
  47. {
  48. if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
  49. {
  50. const int ax = x + rcGetDirOffsetX(dir);
  51. const int ay = y + rcGetDirOffsetY(dir);
  52. const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
  53. if (area == chf.areas[ai])
  54. nc++;
  55. }
  56. }
  57. if (nc != 4)
  58. src[i] = 0;
  59. }
  60. }
  61. }
  62. // Pass 1
  63. for (int y = 0; y < h; ++y)
  64. {
  65. for (int x = 0; x < w; ++x)
  66. {
  67. const rcCompactCell& c = chf.cells[x+y*w];
  68. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
  69. {
  70. const rcCompactSpan& s = chf.spans[i];
  71. if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
  72. {
  73. // (-1,0)
  74. const int ax = x + rcGetDirOffsetX(0);
  75. const int ay = y + rcGetDirOffsetY(0);
  76. const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
  77. const rcCompactSpan& as = chf.spans[ai];
  78. if (src[ai]+2 < src[i])
  79. src[i] = src[ai]+2;
  80. // (-1,-1)
  81. if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
  82. {
  83. const int aax = ax + rcGetDirOffsetX(3);
  84. const int aay = ay + rcGetDirOffsetY(3);
  85. const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);
  86. if (src[aai]+3 < src[i])
  87. src[i] = src[aai]+3;
  88. }
  89. }
  90. if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
  91. {
  92. // (0,-1)
  93. const int ax = x + rcGetDirOffsetX(3);
  94. const int ay = y + rcGetDirOffsetY(3);
  95. const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
  96. const rcCompactSpan& as = chf.spans[ai];
  97. if (src[ai]+2 < src[i])
  98. src[i] = src[ai]+2;
  99. // (1,-1)
  100. if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
  101. {
  102. const int aax = ax + rcGetDirOffsetX(2);
  103. const int aay = ay + rcGetDirOffsetY(2);
  104. const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);
  105. if (src[aai]+3 < src[i])
  106. src[i] = src[aai]+3;
  107. }
  108. }
  109. }
  110. }
  111. }
  112. // Pass 2
  113. for (int y = h-1; y >= 0; --y)
  114. {
  115. for (int x = w-1; x >= 0; --x)
  116. {
  117. const rcCompactCell& c = chf.cells[x+y*w];
  118. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
  119. {
  120. const rcCompactSpan& s = chf.spans[i];
  121. if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
  122. {
  123. // (1,0)
  124. const int ax = x + rcGetDirOffsetX(2);
  125. const int ay = y + rcGetDirOffsetY(2);
  126. const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
  127. const rcCompactSpan& as = chf.spans[ai];
  128. if (src[ai]+2 < src[i])
  129. src[i] = src[ai]+2;
  130. // (1,1)
  131. if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
  132. {
  133. const int aax = ax + rcGetDirOffsetX(1);
  134. const int aay = ay + rcGetDirOffsetY(1);
  135. const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);
  136. if (src[aai]+3 < src[i])
  137. src[i] = src[aai]+3;
  138. }
  139. }
  140. if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
  141. {
  142. // (0,1)
  143. const int ax = x + rcGetDirOffsetX(1);
  144. const int ay = y + rcGetDirOffsetY(1);
  145. const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
  146. const rcCompactSpan& as = chf.spans[ai];
  147. if (src[ai]+2 < src[i])
  148. src[i] = src[ai]+2;
  149. // (-1,1)
  150. if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
  151. {
  152. const int aax = ax + rcGetDirOffsetX(0);
  153. const int aay = ay + rcGetDirOffsetY(0);
  154. const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);
  155. if (src[aai]+3 < src[i])
  156. src[i] = src[aai]+3;
  157. }
  158. }
  159. }
  160. }
  161. }
  162. maxDist = 0;
  163. for (int i = 0; i < chf.spanCount; ++i)
  164. maxDist = rcMax(src[i], maxDist);
  165. }
  166. static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr,
  167. unsigned short* src, unsigned short* dst)
  168. {
  169. const int w = chf.width;
  170. const int h = chf.height;
  171. thr *= 2;
  172. for (int y = 0; y < h; ++y)
  173. {
  174. for (int x = 0; x < w; ++x)
  175. {
  176. const rcCompactCell& c = chf.cells[x+y*w];
  177. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
  178. {
  179. const rcCompactSpan& s = chf.spans[i];
  180. const unsigned short cd = src[i];
  181. if (cd <= thr)
  182. {
  183. dst[i] = cd;
  184. continue;
  185. }
  186. int d = (int)cd;
  187. for (int dir = 0; dir < 4; ++dir)
  188. {
  189. if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
  190. {
  191. const int ax = x + rcGetDirOffsetX(dir);
  192. const int ay = y + rcGetDirOffsetY(dir);
  193. const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
  194. d += (int)src[ai];
  195. const rcCompactSpan& as = chf.spans[ai];
  196. const int dir2 = (dir+1) & 0x3;
  197. if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
  198. {
  199. const int ax2 = ax + rcGetDirOffsetX(dir2);
  200. const int ay2 = ay + rcGetDirOffsetY(dir2);
  201. const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
  202. d += (int)src[ai2];
  203. }
  204. else
  205. {
  206. d += cd;
  207. }
  208. }
  209. else
  210. {
  211. d += cd*2;
  212. }
  213. }
  214. dst[i] = (unsigned short)((d+5)/9);
  215. }
  216. }
  217. }
  218. return dst;
  219. }
  220. static bool floodRegion(int x, int y, int i,
  221. unsigned short level, unsigned short r,
  222. rcCompactHeightfield& chf,
  223. unsigned short* srcReg, unsigned short* srcDist,
  224. rcIntArray& stack)
  225. {
  226. const int w = chf.width;
  227. const unsigned char area = chf.areas[i];
  228. // Flood fill mark region.
  229. stack.resize(0);
  230. stack.push((int)x);
  231. stack.push((int)y);
  232. stack.push((int)i);
  233. srcReg[i] = r;
  234. srcDist[i] = 0;
  235. unsigned short lev = level >= 2 ? level-2 : 0;
  236. int count = 0;
  237. while (stack.size() > 0)
  238. {
  239. int ci = stack.pop();
  240. int cy = stack.pop();
  241. int cx = stack.pop();
  242. const rcCompactSpan& cs = chf.spans[ci];
  243. // Check if any of the neighbours already have a valid region set.
  244. unsigned short ar = 0;
  245. for (int dir = 0; dir < 4; ++dir)
  246. {
  247. // 8 connected
  248. if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
  249. {
  250. const int ax = cx + rcGetDirOffsetX(dir);
  251. const int ay = cy + rcGetDirOffsetY(dir);
  252. const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
  253. if (chf.areas[ai] != area)
  254. continue;
  255. unsigned short nr = srcReg[ai];
  256. if (nr & RC_BORDER_REG) // Do not take borders into account.
  257. continue;
  258. if (nr != 0 && nr != r)
  259. ar = nr;
  260. const rcCompactSpan& as = chf.spans[ai];
  261. const int dir2 = (dir+1) & 0x3;
  262. if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
  263. {
  264. const int ax2 = ax + rcGetDirOffsetX(dir2);
  265. const int ay2 = ay + rcGetDirOffsetY(dir2);
  266. const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
  267. if (chf.areas[ai2] != area)
  268. continue;
  269. unsigned short nr2 = srcReg[ai2];
  270. if (nr2 != 0 && nr2 != r)
  271. ar = nr2;
  272. }
  273. }
  274. }
  275. if (ar != 0)
  276. {
  277. srcReg[ci] = 0;
  278. continue;
  279. }
  280. count++;
  281. // Expand neighbours.
  282. for (int dir = 0; dir < 4; ++dir)
  283. {
  284. if (rcGetCon(cs, dir) != RC_NOT_CONNECTED)
  285. {
  286. const int ax = cx + rcGetDirOffsetX(dir);
  287. const int ay = cy + rcGetDirOffsetY(dir);
  288. const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir);
  289. if (chf.areas[ai] != area)
  290. continue;
  291. if (chf.dist[ai] >= lev && srcReg[ai] == 0)
  292. {
  293. srcReg[ai] = r;
  294. srcDist[ai] = 0;
  295. stack.push(ax);
  296. stack.push(ay);
  297. stack.push(ai);
  298. }
  299. }
  300. }
  301. }
  302. return count > 0;
  303. }
  304. static unsigned short* expandRegions(int maxIter, unsigned short level,
  305. rcCompactHeightfield& chf,
  306. unsigned short* srcReg, unsigned short* srcDist,
  307. unsigned short* dstReg, unsigned short* dstDist,
  308. rcIntArray& stack)
  309. {
  310. const int w = chf.width;
  311. const int h = chf.height;
  312. // Find cells revealed by the raised level.
  313. stack.resize(0);
  314. for (int y = 0; y < h; ++y)
  315. {
  316. for (int x = 0; x < w; ++x)
  317. {
  318. const rcCompactCell& c = chf.cells[x+y*w];
  319. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
  320. {
  321. if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA)
  322. {
  323. stack.push(x);
  324. stack.push(y);
  325. stack.push(i);
  326. }
  327. }
  328. }
  329. }
  330. int iter = 0;
  331. while (stack.size() > 0)
  332. {
  333. int failed = 0;
  334. memcpy(dstReg, srcReg, sizeof(unsigned short)*chf.spanCount);
  335. memcpy(dstDist, srcDist, sizeof(unsigned short)*chf.spanCount);
  336. for (int j = 0; j < stack.size(); j += 3)
  337. {
  338. int x = stack[j+0];
  339. int y = stack[j+1];
  340. int i = stack[j+2];
  341. if (i < 0)
  342. {
  343. failed++;
  344. continue;
  345. }
  346. unsigned short r = srcReg[i];
  347. unsigned short d2 = 0xffff;
  348. const unsigned char area = chf.areas[i];
  349. const rcCompactSpan& s = chf.spans[i];
  350. for (int dir = 0; dir < 4; ++dir)
  351. {
  352. if (rcGetCon(s, dir) == RC_NOT_CONNECTED) continue;
  353. const int ax = x + rcGetDirOffsetX(dir);
  354. const int ay = y + rcGetDirOffsetY(dir);
  355. const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
  356. if (chf.areas[ai] != area) continue;
  357. if (srcReg[ai] > 0 && (srcReg[ai] & RC_BORDER_REG) == 0)
  358. {
  359. if ((int)srcDist[ai]+2 < (int)d2)
  360. {
  361. r = srcReg[ai];
  362. d2 = srcDist[ai]+2;
  363. }
  364. }
  365. }
  366. if (r)
  367. {
  368. stack[j+2] = -1; // mark as used
  369. dstReg[i] = r;
  370. dstDist[i] = d2;
  371. }
  372. else
  373. {
  374. failed++;
  375. }
  376. }
  377. // rcSwap source and dest.
  378. rcSwap(srcReg, dstReg);
  379. rcSwap(srcDist, dstDist);
  380. if (failed*3 == stack.size())
  381. break;
  382. if (level > 0)
  383. {
  384. ++iter;
  385. if (iter >= maxIter)
  386. break;
  387. }
  388. }
  389. return srcReg;
  390. }
  391. struct rcRegion
  392. {
  393. inline rcRegion(unsigned short i) :
  394. spanCount(0),
  395. id(i),
  396. areaType(0),
  397. remap(false),
  398. visited(false)
  399. {}
  400. int spanCount; // Number of spans belonging to this region
  401. unsigned short id; // ID of the region
  402. unsigned char areaType; // Are type.
  403. bool remap;
  404. bool visited;
  405. rcIntArray connections;
  406. rcIntArray floors;
  407. };
  408. static void removeAdjacentNeighbours(rcRegion& reg)
  409. {
  410. // Remove adjacent duplicates.
  411. for (int i = 0; i < reg.connections.size() && reg.connections.size() > 1; )
  412. {
  413. int ni = (i+1) % reg.connections.size();
  414. if (reg.connections[i] == reg.connections[ni])
  415. {
  416. // Remove duplicate
  417. for (int j = i; j < reg.connections.size()-1; ++j)
  418. reg.connections[j] = reg.connections[j+1];
  419. reg.connections.pop();
  420. }
  421. else
  422. ++i;
  423. }
  424. }
  425. static void replaceNeighbour(rcRegion& reg, unsigned short oldId, unsigned short newId)
  426. {
  427. bool neiChanged = false;
  428. for (int i = 0; i < reg.connections.size(); ++i)
  429. {
  430. if (reg.connections[i] == oldId)
  431. {
  432. reg.connections[i] = newId;
  433. neiChanged = true;
  434. }
  435. }
  436. for (int i = 0; i < reg.floors.size(); ++i)
  437. {
  438. if (reg.floors[i] == oldId)
  439. reg.floors[i] = newId;
  440. }
  441. if (neiChanged)
  442. removeAdjacentNeighbours(reg);
  443. }
  444. static bool canMergeWithRegion(const rcRegion& rega, const rcRegion& regb)
  445. {
  446. if (rega.areaType != regb.areaType)
  447. return false;
  448. int n = 0;
  449. for (int i = 0; i < rega.connections.size(); ++i)
  450. {
  451. if (rega.connections[i] == regb.id)
  452. n++;
  453. }
  454. if (n > 1)
  455. return false;
  456. for (int i = 0; i < rega.floors.size(); ++i)
  457. {
  458. if (rega.floors[i] == regb.id)
  459. return false;
  460. }
  461. return true;
  462. }
  463. static void addUniqueFloorRegion(rcRegion& reg, int n)
  464. {
  465. for (int i = 0; i < reg.floors.size(); ++i)
  466. if (reg.floors[i] == n)
  467. return;
  468. reg.floors.push(n);
  469. }
  470. static bool mergeRegions(rcRegion& rega, rcRegion& regb)
  471. {
  472. unsigned short aid = rega.id;
  473. unsigned short bid = regb.id;
  474. // Duplicate current neighbourhood.
  475. rcIntArray acon;
  476. acon.resize(rega.connections.size());
  477. for (int i = 0; i < rega.connections.size(); ++i)
  478. acon[i] = rega.connections[i];
  479. rcIntArray& bcon = regb.connections;
  480. // Find insertion point on A.
  481. int insa = -1;
  482. for (int i = 0; i < acon.size(); ++i)
  483. {
  484. if (acon[i] == bid)
  485. {
  486. insa = i;
  487. break;
  488. }
  489. }
  490. if (insa == -1)
  491. return false;
  492. // Find insertion point on B.
  493. int insb = -1;
  494. for (int i = 0; i < bcon.size(); ++i)
  495. {
  496. if (bcon[i] == aid)
  497. {
  498. insb = i;
  499. break;
  500. }
  501. }
  502. if (insb == -1)
  503. return false;
  504. // Merge neighbours.
  505. rega.connections.resize(0);
  506. for (int i = 0, ni = acon.size(); i < ni-1; ++i)
  507. rega.connections.push(acon[(insa+1+i) % ni]);
  508. for (int i = 0, ni = bcon.size(); i < ni-1; ++i)
  509. rega.connections.push(bcon[(insb+1+i) % ni]);
  510. removeAdjacentNeighbours(rega);
  511. for (int j = 0; j < regb.floors.size(); ++j)
  512. addUniqueFloorRegion(rega, regb.floors[j]);
  513. rega.spanCount += regb.spanCount;
  514. regb.spanCount = 0;
  515. regb.connections.resize(0);
  516. return true;
  517. }
  518. static bool isRegionConnectedToBorder(const rcRegion& reg)
  519. {
  520. // Region is connected to border if
  521. // one of the neighbours is null id.
  522. for (int i = 0; i < reg.connections.size(); ++i)
  523. {
  524. if (reg.connections[i] == 0)
  525. return true;
  526. }
  527. return false;
  528. }
  529. static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* srcReg,
  530. int x, int y, int i, int dir)
  531. {
  532. const rcCompactSpan& s = chf.spans[i];
  533. unsigned short r = 0;
  534. if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
  535. {
  536. const int ax = x + rcGetDirOffsetX(dir);
  537. const int ay = y + rcGetDirOffsetY(dir);
  538. const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
  539. r = srcReg[ai];
  540. }
  541. if (r == srcReg[i])
  542. return false;
  543. return true;
  544. }
  545. static void walkContour(int x, int y, int i, int dir,
  546. rcCompactHeightfield& chf,
  547. unsigned short* srcReg,
  548. rcIntArray& cont)
  549. {
  550. int startDir = dir;
  551. int starti = i;
  552. const rcCompactSpan& ss = chf.spans[i];
  553. unsigned short curReg = 0;
  554. if (rcGetCon(ss, dir) != RC_NOT_CONNECTED)
  555. {
  556. const int ax = x + rcGetDirOffsetX(dir);
  557. const int ay = y + rcGetDirOffsetY(dir);
  558. const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(ss, dir);
  559. curReg = srcReg[ai];
  560. }
  561. cont.push(curReg);
  562. int iter = 0;
  563. while (++iter < 40000)
  564. {
  565. const rcCompactSpan& s = chf.spans[i];
  566. if (isSolidEdge(chf, srcReg, x, y, i, dir))
  567. {
  568. // Choose the edge corner
  569. unsigned short r = 0;
  570. if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
  571. {
  572. const int ax = x + rcGetDirOffsetX(dir);
  573. const int ay = y + rcGetDirOffsetY(dir);
  574. const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
  575. r = srcReg[ai];
  576. }
  577. if (r != curReg)
  578. {
  579. curReg = r;
  580. cont.push(curReg);
  581. }
  582. dir = (dir+1) & 0x3; // Rotate CW
  583. }
  584. else
  585. {
  586. int ni = -1;
  587. const int nx = x + rcGetDirOffsetX(dir);
  588. const int ny = y + rcGetDirOffsetY(dir);
  589. if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
  590. {
  591. const rcCompactCell& nc = chf.cells[nx+ny*chf.width];
  592. ni = (int)nc.index + rcGetCon(s, dir);
  593. }
  594. if (ni == -1)
  595. {
  596. // Should not happen.
  597. return;
  598. }
  599. x = nx;
  600. y = ny;
  601. i = ni;
  602. dir = (dir+3) & 0x3; // Rotate CCW
  603. }
  604. if (starti == i && startDir == dir)
  605. {
  606. break;
  607. }
  608. }
  609. // Remove adjacent duplicates.
  610. if (cont.size() > 1)
  611. {
  612. for (int j = 0; j < cont.size(); )
  613. {
  614. int nj = (j+1) % cont.size();
  615. if (cont[j] == cont[nj])
  616. {
  617. for (int k = j; k < cont.size()-1; ++k)
  618. cont[k] = cont[k+1];
  619. cont.pop();
  620. }
  621. else
  622. ++j;
  623. }
  624. }
  625. }
  626. static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize,
  627. unsigned short& maxRegionId,
  628. rcCompactHeightfield& chf,
  629. unsigned short* srcReg)
  630. {
  631. const int w = chf.width;
  632. const int h = chf.height;
  633. const int nreg = maxRegionId+1;
  634. rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP);
  635. if (!regions)
  636. {
  637. ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg);
  638. return false;
  639. }
  640. // Construct regions
  641. for (int i = 0; i < nreg; ++i)
  642. new(&regions[i]) rcRegion((unsigned short)i);
  643. // Find edge of a region and find connections around the contour.
  644. for (int y = 0; y < h; ++y)
  645. {
  646. for (int x = 0; x < w; ++x)
  647. {
  648. const rcCompactCell& c = chf.cells[x+y*w];
  649. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
  650. {
  651. unsigned short r = srcReg[i];
  652. if (r == 0 || r >= nreg)
  653. continue;
  654. rcRegion& reg = regions[r];
  655. reg.spanCount++;
  656. // Update floors.
  657. for (int j = (int)c.index; j < ni; ++j)
  658. {
  659. if (i == j) continue;
  660. unsigned short floorId = srcReg[j];
  661. if (floorId == 0 || floorId >= nreg)
  662. continue;
  663. addUniqueFloorRegion(reg, floorId);
  664. }
  665. // Have found contour
  666. if (reg.connections.size() > 0)
  667. continue;
  668. reg.areaType = chf.areas[i];
  669. // Check if this cell is next to a border.
  670. int ndir = -1;
  671. for (int dir = 0; dir < 4; ++dir)
  672. {
  673. if (isSolidEdge(chf, srcReg, x, y, i, dir))
  674. {
  675. ndir = dir;
  676. break;
  677. }
  678. }
  679. if (ndir != -1)
  680. {
  681. // The cell is at border.
  682. // Walk around the contour to find all the neighbours.
  683. walkContour(x, y, i, ndir, chf, srcReg, reg.connections);
  684. }
  685. }
  686. }
  687. }
  688. // Remove too small regions.
  689. rcIntArray stack(32);
  690. rcIntArray trace(32);
  691. for (int i = 0; i < nreg; ++i)
  692. {
  693. rcRegion& reg = regions[i];
  694. if (reg.id == 0 || (reg.id & RC_BORDER_REG))
  695. continue;
  696. if (reg.spanCount == 0)
  697. continue;
  698. if (reg.visited)
  699. continue;
  700. // Count the total size of all the connected regions.
  701. // Also keep track of the regions connects to a tile border.
  702. bool connectsToBorder = false;
  703. int spanCount = 0;
  704. stack.resize(0);
  705. trace.resize(0);
  706. reg.visited = true;
  707. stack.push(i);
  708. while (stack.size())
  709. {
  710. // Pop
  711. int ri = stack.pop();
  712. rcRegion& creg = regions[ri];
  713. spanCount += creg.spanCount;
  714. trace.push(ri);
  715. for (int j = 0; j < creg.connections.size(); ++j)
  716. {
  717. if (creg.connections[j] & RC_BORDER_REG)
  718. {
  719. connectsToBorder = true;
  720. continue;
  721. }
  722. rcRegion& neireg = regions[creg.connections[j]];
  723. if (neireg.visited)
  724. continue;
  725. if (neireg.id == 0 || (neireg.id & RC_BORDER_REG))
  726. continue;
  727. // Visit
  728. stack.push(neireg.id);
  729. neireg.visited = true;
  730. }
  731. }
  732. // If the accumulated regions size is too small, remove it.
  733. // Do not remove areas which connect to tile borders
  734. // as their size cannot be estimated correctly and removing them
  735. // can potentially remove necessary areas.
  736. if (spanCount < minRegionArea && !connectsToBorder)
  737. {
  738. // Kill all visited regions.
  739. for (int j = 0; j < trace.size(); ++j)
  740. {
  741. regions[trace[j]].spanCount = 0;
  742. regions[trace[j]].id = 0;
  743. }
  744. }
  745. }
  746. // Merge too small regions to neighbour regions.
  747. int mergeCount = 0 ;
  748. do
  749. {
  750. mergeCount = 0;
  751. for (int i = 0; i < nreg; ++i)
  752. {
  753. rcRegion& reg = regions[i];
  754. if (reg.id == 0 || (reg.id & RC_BORDER_REG))
  755. continue;
  756. if (reg.spanCount == 0)
  757. continue;
  758. // Check to see if the region should be merged.
  759. if (reg.spanCount > mergeRegionSize && isRegionConnectedToBorder(reg))
  760. continue;
  761. // Small region with more than 1 connection.
  762. // Or region which is not connected to a border at all.
  763. // Find smallest neighbour region that connects to this one.
  764. int smallest = 0xfffffff;
  765. unsigned short mergeId = reg.id;
  766. for (int j = 0; j < reg.connections.size(); ++j)
  767. {
  768. if (reg.connections[j] & RC_BORDER_REG) continue;
  769. rcRegion& mreg = regions[reg.connections[j]];
  770. if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue;
  771. if (mreg.spanCount < smallest &&
  772. canMergeWithRegion(reg, mreg) &&
  773. canMergeWithRegion(mreg, reg))
  774. {
  775. smallest = mreg.spanCount;
  776. mergeId = mreg.id;
  777. }
  778. }
  779. // Found new id.
  780. if (mergeId != reg.id)
  781. {
  782. unsigned short oldId = reg.id;
  783. rcRegion& target = regions[mergeId];
  784. // Merge neighbours.
  785. if (mergeRegions(target, reg))
  786. {
  787. // Fixup regions pointing to current region.
  788. for (int j = 0; j < nreg; ++j)
  789. {
  790. if (regions[j].id == 0 || (regions[j].id & RC_BORDER_REG)) continue;
  791. // If another region was already merged into current region
  792. // change the nid of the previous region too.
  793. if (regions[j].id == oldId)
  794. regions[j].id = mergeId;
  795. // Replace the current region with the new one if the
  796. // current regions is neighbour.
  797. replaceNeighbour(regions[j], oldId, mergeId);
  798. }
  799. mergeCount++;
  800. }
  801. }
  802. }
  803. }
  804. while (mergeCount > 0);
  805. // Compress region Ids.
  806. for (int i = 0; i < nreg; ++i)
  807. {
  808. regions[i].remap = false;
  809. if (regions[i].id == 0) continue; // Skip nil regions.
  810. if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions.
  811. regions[i].remap = true;
  812. }
  813. unsigned short regIdGen = 0;
  814. for (int i = 0; i < nreg; ++i)
  815. {
  816. if (!regions[i].remap)
  817. continue;
  818. unsigned short oldId = regions[i].id;
  819. unsigned short newId = ++regIdGen;
  820. for (int j = i; j < nreg; ++j)
  821. {
  822. if (regions[j].id == oldId)
  823. {
  824. regions[j].id = newId;
  825. regions[j].remap = false;
  826. }
  827. }
  828. }
  829. maxRegionId = regIdGen;
  830. // Remap regions.
  831. for (int i = 0; i < chf.spanCount; ++i)
  832. {
  833. if ((srcReg[i] & RC_BORDER_REG) == 0)
  834. srcReg[i] = regions[srcReg[i]].id;
  835. }
  836. for (int i = 0; i < nreg; ++i)
  837. regions[i].~rcRegion();
  838. rcFree(regions);
  839. return true;
  840. }
  841. /// @par
  842. ///
  843. /// This is usually the second to the last step in creating a fully built
  844. /// compact heightfield. This step is required before regions are built
  845. /// using #rcBuildRegions or #rcBuildRegionsMonotone.
  846. ///
  847. /// After this step, the distance data is available via the rcCompactHeightfield::maxDistance
  848. /// and rcCompactHeightfield::dist fields.
  849. ///
  850. /// @see rcCompactHeightfield, rcBuildRegions, rcBuildRegionsMonotone
  851. bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf)
  852. {
  853. rcAssert(ctx);
  854. ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD);
  855. if (chf.dist)
  856. {
  857. rcFree(chf.dist);
  858. chf.dist = 0;
  859. }
  860. unsigned short* src = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
  861. if (!src)
  862. {
  863. ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'src' (%d).", chf.spanCount);
  864. return false;
  865. }
  866. unsigned short* dst = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
  867. if (!dst)
  868. {
  869. ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dst' (%d).", chf.spanCount);
  870. rcFree(src);
  871. return false;
  872. }
  873. unsigned short maxDist = 0;
  874. ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
  875. calculateDistanceField(chf, src, maxDist);
  876. chf.maxDistance = maxDist;
  877. ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
  878. ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
  879. // Blur
  880. if (boxBlur(chf, 1, src, dst) != src)
  881. rcSwap(src, dst);
  882. // Store distance.
  883. chf.dist = src;
  884. ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
  885. ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD);
  886. rcFree(dst);
  887. return true;
  888. }
  889. static void paintRectRegion(int minx, int maxx, int miny, int maxy, unsigned short regId,
  890. rcCompactHeightfield& chf, unsigned short* srcReg)
  891. {
  892. const int w = chf.width;
  893. for (int y = miny; y < maxy; ++y)
  894. {
  895. for (int x = minx; x < maxx; ++x)
  896. {
  897. const rcCompactCell& c = chf.cells[x+y*w];
  898. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
  899. {
  900. if (chf.areas[i] != RC_NULL_AREA)
  901. srcReg[i] = regId;
  902. }
  903. }
  904. }
  905. }
  906. static const unsigned short RC_NULL_NEI = 0xffff;
  907. struct rcSweepSpan
  908. {
  909. unsigned short rid; // row id
  910. unsigned short id; // region id
  911. unsigned short ns; // number samples
  912. unsigned short nei; // neighbour id
  913. };
  914. /// @par
  915. ///
  916. /// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour.
  917. /// Contours will form simple polygons.
  918. ///
  919. /// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be
  920. /// re-assigned to the zero (null) region.
  921. ///
  922. /// Partitioning can result in smaller than necessary regions. @p mergeRegionArea helps
  923. /// reduce unecessarily small regions.
  924. ///
  925. /// See the #rcConfig documentation for more information on the configuration parameters.
  926. ///
  927. /// The region data will be available via the rcCompactHeightfield::maxRegions
  928. /// and rcCompactSpan::reg fields.
  929. ///
  930. /// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions.
  931. ///
  932. /// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig
  933. bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
  934. const int borderSize, const int minRegionArea, const int mergeRegionArea)
  935. {
  936. rcAssert(ctx);
  937. ctx->startTimer(RC_TIMER_BUILD_REGIONS);
  938. const int w = chf.width;
  939. const int h = chf.height;
  940. unsigned short id = 1;
  941. rcScopedDelete<unsigned short> srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
  942. if (!srcReg)
  943. {
  944. ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount);
  945. return false;
  946. }
  947. memset(srcReg,0,sizeof(unsigned short)*chf.spanCount);
  948. const int nsweeps = rcMax(chf.width,chf.height);
  949. rcScopedDelete<rcSweepSpan> sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP);
  950. if (!sweeps)
  951. {
  952. ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps);
  953. return false;
  954. }
  955. // Mark border regions.
  956. if (borderSize > 0)
  957. {
  958. // Make sure border will not overflow.
  959. const int bw = rcMin(w, borderSize);
  960. const int bh = rcMin(h, borderSize);
  961. // Paint regions
  962. paintRectRegion(0, bw, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
  963. paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
  964. paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++;
  965. paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++;
  966. chf.borderSize = borderSize;
  967. }
  968. rcIntArray prev(256);
  969. // Sweep one line at a time.
  970. for (int y = borderSize; y < h-borderSize; ++y)
  971. {
  972. // Collect spans from this row.
  973. prev.resize(id+1);
  974. memset(&prev[0],0,sizeof(int)*id);
  975. unsigned short rid = 1;
  976. for (int x = borderSize; x < w-borderSize; ++x)
  977. {
  978. const rcCompactCell& c = chf.cells[x+y*w];
  979. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
  980. {
  981. const rcCompactSpan& s = chf.spans[i];
  982. if (chf.areas[i] == RC_NULL_AREA) continue;
  983. // -x
  984. unsigned short previd = 0;
  985. if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
  986. {
  987. const int ax = x + rcGetDirOffsetX(0);
  988. const int ay = y + rcGetDirOffsetY(0);
  989. const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
  990. if ((srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
  991. previd = srcReg[ai];
  992. }
  993. if (!previd)
  994. {
  995. previd = rid++;
  996. sweeps[previd].rid = previd;
  997. sweeps[previd].ns = 0;
  998. sweeps[previd].nei = 0;
  999. }
  1000. // -y
  1001. if (rcGetCon(s,3) != RC_NOT_CONNECTED)
  1002. {
  1003. const int ax = x + rcGetDirOffsetX(3);
  1004. const int ay = y + rcGetDirOffsetY(3);
  1005. const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
  1006. if (srcReg[ai] && (srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
  1007. {
  1008. unsigned short nr = srcReg[ai];
  1009. if (!sweeps[previd].nei || sweeps[previd].nei == nr)
  1010. {
  1011. sweeps[previd].nei = nr;
  1012. sweeps[previd].ns++;
  1013. prev[nr]++;
  1014. }
  1015. else
  1016. {
  1017. sweeps[previd].nei = RC_NULL_NEI;
  1018. }
  1019. }
  1020. }
  1021. srcReg[i] = previd;
  1022. }
  1023. }
  1024. // Create unique ID.
  1025. for (int i = 1; i < rid; ++i)
  1026. {
  1027. if (sweeps[i].nei != RC_NULL_NEI && sweeps[i].nei != 0 &&
  1028. prev[sweeps[i].nei] == (int)sweeps[i].ns)
  1029. {
  1030. sweeps[i].id = sweeps[i].nei;
  1031. }
  1032. else
  1033. {
  1034. sweeps[i].id = id++;
  1035. }
  1036. }
  1037. // Remap IDs
  1038. for (int x = borderSize; x < w-borderSize; ++x)
  1039. {
  1040. const rcCompactCell& c = chf.cells[x+y*w];
  1041. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
  1042. {
  1043. if (srcReg[i] > 0 && srcReg[i] < rid)
  1044. srcReg[i] = sweeps[srcReg[i]].id;
  1045. }
  1046. }
  1047. }
  1048. ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
  1049. // Filter out small regions.
  1050. chf.maxRegions = id;
  1051. if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
  1052. return false;
  1053. ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
  1054. // Store the result out.
  1055. for (int i = 0; i < chf.spanCount; ++i)
  1056. chf.spans[i].reg = srcReg[i];
  1057. ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
  1058. return true;
  1059. }
  1060. /// @par
  1061. ///
  1062. /// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour.
  1063. /// Contours will form simple polygons.
  1064. ///
  1065. /// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be
  1066. /// re-assigned to the zero (null) region.
  1067. ///
  1068. /// Watershed partitioning can result in smaller than necessary regions, especially in diagonal corridors.
  1069. /// @p mergeRegionArea helps reduce unecessarily small regions.
  1070. ///
  1071. /// See the #rcConfig documentation for more information on the configuration parameters.
  1072. ///
  1073. /// The region data will be available via the rcCompactHeightfield::maxRegions
  1074. /// and rcCompactSpan::reg fields.
  1075. ///
  1076. /// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions.
  1077. ///
  1078. /// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig
  1079. bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
  1080. const int borderSize, const int minRegionArea, const int mergeRegionArea)
  1081. {
  1082. rcAssert(ctx);
  1083. ctx->startTimer(RC_TIMER_BUILD_REGIONS);
  1084. const int w = chf.width;
  1085. const int h = chf.height;
  1086. rcScopedDelete<unsigned short> buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP);
  1087. if (!buf)
  1088. {
  1089. ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4);
  1090. return false;
  1091. }
  1092. ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
  1093. rcIntArray stack(1024);
  1094. rcIntArray visited(1024);
  1095. unsigned short* srcReg = buf;
  1096. unsigned short* srcDist = buf+chf.spanCount;
  1097. unsigned short* dstReg = buf+chf.spanCount*2;
  1098. unsigned short* dstDist = buf+chf.spanCount*3;
  1099. memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount);
  1100. memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount);
  1101. unsigned short regionId = 1;
  1102. unsigned short level = (chf.maxDistance+1) & ~1;
  1103. // TODO: Figure better formula, expandIters defines how much the
  1104. // watershed "overflows" and simplifies the regions. Tying it to
  1105. // agent radius was usually good indication how greedy it could be.
  1106. // const int expandIters = 4 + walkableRadius * 2;
  1107. const int expandIters = 8;
  1108. if (borderSize > 0)
  1109. {
  1110. // Make sure border will not overflow.
  1111. const int bw = rcMin(w, borderSize);
  1112. const int bh = rcMin(h, borderSize);
  1113. // Paint regions
  1114. paintRectRegion(0, bw, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
  1115. paintRectRegion(w-bw, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
  1116. paintRectRegion(0, w, 0, bh, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
  1117. paintRectRegion(0, w, h-bh, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
  1118. chf.borderSize = borderSize;
  1119. }
  1120. while (level > 0)
  1121. {
  1122. level = level >= 2 ? level-2 : 0;
  1123. ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
  1124. // Expand current regions until no empty connected cells found.
  1125. if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
  1126. {
  1127. rcSwap(srcReg, dstReg);
  1128. rcSwap(srcDist, dstDist);
  1129. }
  1130. ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
  1131. ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
  1132. // Mark new regions with IDs.
  1133. for (int y = 0; y < h; ++y)
  1134. {
  1135. for (int x = 0; x < w; ++x)
  1136. {
  1137. const rcCompactCell& c = chf.cells[x+y*w];
  1138. for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
  1139. {
  1140. if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA)
  1141. continue;
  1142. if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack))
  1143. regionId++;
  1144. }
  1145. }
  1146. }
  1147. ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
  1148. }
  1149. // Expand current regions until no empty connected cells found.
  1150. if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg)
  1151. {
  1152. rcSwap(srcReg, dstReg);
  1153. rcSwap(srcDist, dstDist);
  1154. }
  1155. ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
  1156. ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
  1157. // Filter out small regions.
  1158. chf.maxRegions = regionId;
  1159. if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
  1160. return false;
  1161. ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
  1162. // Write the result out.
  1163. for (int i = 0; i < chf.spanCount; ++i)
  1164. chf.spans[i].reg = srcReg[i];
  1165. ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
  1166. return true;
  1167. }