/// @par /// /// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour. /// Contours will form simple polygons. /// /// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be /// re-assigned to the zero (null) region. /// /// Watershed partitioning can result in smaller than necessary regions, especially in diagonal corridors. /// @p mergeRegionArea helps reduce unecessarily small regions. /// /// See the #rcConfig documentation for more information on the configuration parameters. /// /// The region data will be available via the rcCompactHeightfield::maxRegions /// and rcCompactSpan::reg fields. /// /// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions. /// /// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, const int borderSize, const int minRegionArea, const int mergeRegionArea) { rcAssert(ctx); ctx->startTimer(RC_TIMER_BUILD_REGIONS); const int w = chf.width; const int h = chf.height; rcScopedDelete<unsigned short> buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP); if (!buf) { ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4); return false; } ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); const int LOG_NB_STACKS = 3; const int NB_STACKS = 1 << LOG_NB_STACKS; rcIntArray lvlStacks[NB_STACKS]; for (int i=0; i<NB_STACKS; ++i) lvlStacks[i].resize(1024); rcIntArray stack(1024); rcIntArray visited(1024); unsigned short* srcReg = buf; unsigned short* srcDist = buf+chf.spanCount; unsigned short* dstReg = buf+chf.spanCount*2; unsigned short* dstDist = buf+chf.spanCount*3; memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount); memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount); unsigned short regionId = 1; unsigned short level = (chf.maxDistance+1) & ~1; // TODO: Figure better formula, expandIters defines how much the // watershed "overflows" and simplifies the regions. Tying it to // agent radius was usually good indication how greedy it could be. // const int expandIters = 4 + walkableRadius * 2; const int expandIters = 8; if (borderSize > 0) { // Make sure border will not overflow. const int bw = rcMin(w, borderSize); const int bh = rcMin(h, borderSize); // Paint regions paintRectRegion(0, bw, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(w-bw, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(0, w, 0, bh, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(0, w, h-bh, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; chf.borderSize = borderSize; } int sId = -1; while (level > 0) { level = level >= 2 ? level-2 : 0; sId = (sId+1) & (NB_STACKS-1); // ctx->startTimer(RC_TIMER_DIVIDE_TO_LEVELS); if (sId == 0) sortCellsByLevel(level, chf, srcReg, NB_STACKS, lvlStacks, 1); else appendStacks(lvlStacks[sId-1], lvlStacks[sId], srcReg); // copy left overs from last level // ctx->stopTimer(RC_TIMER_DIVIDE_TO_LEVELS); ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND); // Expand current regions until no empty connected cells found. if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); } ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND); ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD); // Mark new regions with IDs. for (int j=0; j<lvlStacks[sId].size(); j+=3) { int x = lvlStacks[sId][j]; int y = lvlStacks[sId][j+1]; int i = lvlStacks[sId][j+2]; if (i >= 0 && srcReg[i] == 0) { if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) regionId++; } } ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD); } // Expand current regions until no empty connected cells found. if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack, true) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); } ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); // Merge regions and filter out smalle regions. rcIntArray overlaps; chf.maxRegions = regionId; if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps)) return false; // If overlapping regions were found during merging, split those regions. if (overlaps.size() > 0) { ctx->log(RC_LOG_ERROR, "rcBuildRegions: %d overlapping regions.", overlaps.size()); } ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); // Write the result out. for (int i = 0; i < chf.spanCount; ++i) chf.spans[i].reg = srcReg[i]; ctx->stopTimer(RC_TIMER_BUILD_REGIONS); return true; }
/// @par /// /// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour. /// Contours will form simple polygons. /// /// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be /// re-assigned to the zero (null) region. /// /// Watershed partitioning can result in smaller than necessary regions, especially in diagonal corridors. /// @p mergeRegionArea helps reduce unecessarily small regions. /// /// See the #rcConfig documentation for more information on the configuration parameters. /// /// The region data will be available via the rcCompactHeightfield::maxRegions /// and rcCompactSpan::reg fields. /// /// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions. /// /// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, const int borderSize, const int minRegionArea, const int mergeRegionArea) { rcAssert(ctx); ctx->startTimer(RC_TIMER_BUILD_REGIONS); const int w = chf.width; const int h = chf.height; rcScopedDelete<unsigned short> buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP); if (!buf) { ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4); return false; } ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); rcIntArray stack(1024); rcIntArray visited(1024); unsigned short* srcReg = buf; unsigned short* srcDist = buf+chf.spanCount; unsigned short* dstReg = buf+chf.spanCount*2; unsigned short* dstDist = buf+chf.spanCount*3; memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount); memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount); unsigned short regionId = 1; unsigned short level = (chf.maxDistance+1) & ~1; // TODO: Figure better formula, expandIters defines how much the // watershed "overflows" and simplifies the regions. Tying it to // agent radius was usually good indication how greedy it could be. // const int expandIters = 4 + walkableRadius * 2; const int expandIters = 8; if (borderSize > 0) { // Make sure border will not overflow. const int bw = rcMin(w, borderSize); const int bh = rcMin(h, borderSize); // Paint regions paintRectRegion(0, bw, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(w-bw, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(0, w, 0, bh, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(0, w, h-bh, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; chf.borderSize = borderSize; } while (level > 0) { level = level >= 2 ? level-2 : 0; ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND); // Expand current regions until no empty connected cells found. if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); } ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND); ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD); // Mark new regions with IDs. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA) continue; if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) regionId++; } } } ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD); } // Expand current regions until no empty connected cells found. if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); } ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); // Filter out small regions. chf.maxRegions = regionId; if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg)) return false; ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); // Write the result out. for (int i = 0; i < chf.spanCount; ++i) chf.spans[i].reg = srcReg[i]; ctx->stopTimer(RC_TIMER_BUILD_REGIONS); return true; }
bool rcBuildRegions(rcCompactHeightfield& chf, int walkableRadius, int borderSize, int minRegionSize, int mergeRegionSize) { rcTimeVal startTime = rcGetPerformanceTimer(); const int w = chf.width; const int h = chf.height; unsigned short* tmp1 = new unsigned short[chf.spanCount*2]; if (!tmp1) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'tmp1' (%d).", chf.spanCount*2); return false; } unsigned short* tmp2 = new unsigned short[chf.spanCount*2]; if (!tmp2) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'tmp2' (%d).", chf.spanCount*2); delete [] tmp1; return false; } rcTimeVal regStartTime = rcGetPerformanceTimer(); rcIntArray stack(1024); rcIntArray visited(1024); unsigned short* src = tmp1; unsigned short* dst = tmp2; memset(src, 0, sizeof(unsigned short) * chf.spanCount*2); unsigned short regionId = 1; unsigned short level = (chf.maxDistance+1) & ~1; unsigned short minLevel = (unsigned short)(walkableRadius*2); const int expandIters = 4 + walkableRadius * 2; // Mark border regions. paintRectRegion(0, borderSize, 0, h, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++; paintRectRegion(w-borderSize, w, 0, h, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++; paintRectRegion(0, w, 0, borderSize, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++; paintRectRegion(0, w, h-borderSize, h, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++; rcTimeVal expTime = 0; rcTimeVal floodTime = 0; while (level > minLevel) { level = level >= 2 ? level-2 : 0; rcTimeVal expStartTime = rcGetPerformanceTimer(); // Expand current regions until no empty connected cells found. if (expandRegions(expandIters, level, chf, src, dst, stack) != src) rcSwap(src, dst); expTime += rcGetPerformanceTimer() - expStartTime; rcTimeVal floodStartTime = rcGetPerformanceTimer(); // Mark new regions with IDs. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { if (chf.spans[i].dist < level || src[i*2] != 0) continue; if (floodRegion(x, y, i, minLevel, level, regionId, chf, src, stack)) regionId++; } } } floodTime += rcGetPerformanceTimer() - floodStartTime; } // Expand current regions until no empty connected cells found. if (expandRegions(expandIters*8, minLevel, chf, src, dst, stack) != src) rcSwap(src, dst); rcTimeVal regEndTime = rcGetPerformanceTimer(); rcTimeVal filterStartTime = rcGetPerformanceTimer(); // Filter out small regions. chf.maxRegions = regionId; if (!filterSmallRegions(minRegionSize, mergeRegionSize, chf.maxRegions, chf, src)) return false; rcTimeVal filterEndTime = rcGetPerformanceTimer(); // Write the result out. for (int i = 0; i < chf.spanCount; ++i) chf.spans[i].reg = src[i*2]; delete [] tmp1; delete [] tmp2; rcTimeVal endTime = rcGetPerformanceTimer(); /* if (rcGetLog()) { rcGetLog()->log(RC_LOG_PROGRESS, "Build regions: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f); rcGetLog()->log(RC_LOG_PROGRESS, " - reg: %.3f ms", rcGetDeltaTimeUsec(regStartTime, regEndTime)/1000.0f); rcGetLog()->log(RC_LOG_PROGRESS, " - exp: %.3f ms", rcGetDeltaTimeUsec(0, expTime)/1000.0f); rcGetLog()->log(RC_LOG_PROGRESS, " - flood: %.3f ms", rcGetDeltaTimeUsec(0, floodTime)/1000.0f); rcGetLog()->log(RC_LOG_PROGRESS, " - filter: %.3f ms", rcGetDeltaTimeUsec(filterStartTime, filterEndTime)/1000.0f); } */ if (rcGetBuildTimes()) { rcGetBuildTimes()->buildRegions += rcGetDeltaTimeUsec(startTime, endTime); rcGetBuildTimes()->buildRegionsReg += rcGetDeltaTimeUsec(regStartTime, regEndTime); rcGetBuildTimes()->buildRegionsExp += rcGetDeltaTimeUsec(0, expTime); rcGetBuildTimes()->buildRegionsFlood += rcGetDeltaTimeUsec(0, floodTime); rcGetBuildTimes()->buildRegionsFilter += rcGetDeltaTimeUsec(filterStartTime, filterEndTime); } return true; }
bool rcBuildRegions(rcCompactHeightfield& chf, int borderSize, int minRegionSize, int mergeRegionSize) { rcTimeVal startTime = rcGetPerformanceTimer(); const int w = chf.width; const int h = chf.height; if (!chf.regs) { chf.regs = new unsigned short[chf.spanCount]; if (!chf.regs) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'chf.reg' (%d).", chf.spanCount); return false; } } rcScopedDelete<unsigned short> tmp = new unsigned short[chf.spanCount*4]; if (!tmp) { if (rcGetLog()) rcGetLog()->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4); return false; } rcTimeVal regStartTime = rcGetPerformanceTimer(); rcIntArray stack(1024); rcIntArray visited(1024); unsigned short* srcReg = tmp; unsigned short* srcDist = tmp+chf.spanCount; unsigned short* dstReg = tmp+chf.spanCount*2; unsigned short* dstDist = tmp+chf.spanCount*3; memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount); memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount); unsigned short regionId = 1; unsigned short level = (chf.maxDistance+1) & ~1; // TODO: Figure better formula, expandIters defines how much the // watershed "overflows" and simplifies the regions. Tying it to // agent radius was usually good indication how greedy it could be. // const int expandIters = 4 + walkableRadius * 2; const int expandIters = 8; // Mark border regions. paintRectRegion(0, borderSize, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(w-borderSize, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(0, w, 0, borderSize, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(0, w, h-borderSize, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; rcTimeVal expTime = 0; rcTimeVal floodTime = 0; while (level > 0) { level = level >= 2 ? level-2 : 0; rcTimeVal expStartTime = rcGetPerformanceTimer(); // Expand current regions until no empty connected cells found. if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); } expTime += rcGetPerformanceTimer() - expStartTime; rcTimeVal floodStartTime = rcGetPerformanceTimer(); // Mark new regions with IDs. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA) continue; if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) regionId++; } } } floodTime += rcGetPerformanceTimer() - floodStartTime; } // Expand current regions until no empty connected cells found. if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); } rcTimeVal regEndTime = rcGetPerformanceTimer(); rcTimeVal filterStartTime = rcGetPerformanceTimer(); // Filter out small regions. chf.maxRegions = regionId; if (!filterSmallRegions(minRegionSize, mergeRegionSize, chf.maxRegions, chf, srcReg)) return false; rcTimeVal filterEndTime = rcGetPerformanceTimer(); // Write the result out. memcpy(chf.regs, srcReg, sizeof(unsigned short)*chf.spanCount); rcTimeVal endTime = rcGetPerformanceTimer(); /* if (rcGetLog()) { rcGetLog()->log(RC_LOG_PROGRESS, "Build regions: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f); rcGetLog()->log(RC_LOG_PROGRESS, " - reg: %.3f ms", rcGetDeltaTimeUsec(regStartTime, regEndTime)/1000.0f); rcGetLog()->log(RC_LOG_PROGRESS, " - exp: %.3f ms", rcGetDeltaTimeUsec(0, expTime)/1000.0f); rcGetLog()->log(RC_LOG_PROGRESS, " - flood: %.3f ms", rcGetDeltaTimeUsec(0, floodTime)/1000.0f); rcGetLog()->log(RC_LOG_PROGRESS, " - filter: %.3f ms", rcGetDeltaTimeUsec(filterStartTime, filterEndTime)/1000.0f); } */ if (rcGetBuildTimes()) { rcGetBuildTimes()->buildRegions += rcGetDeltaTimeUsec(startTime, endTime); rcGetBuildTimes()->buildRegionsReg += rcGetDeltaTimeUsec(regStartTime, regEndTime); rcGetBuildTimes()->buildRegionsExp += rcGetDeltaTimeUsec(0, expTime); rcGetBuildTimes()->buildRegionsFlood += rcGetDeltaTimeUsec(0, floodTime); rcGetBuildTimes()->buildRegionsFilter += rcGetDeltaTimeUsec(filterStartTime, filterEndTime); } return true; }
dtStatus dtBuildTileCacheRegions(dtTileCacheAlloc* alloc, const int minRegionArea, const int mergeRegionArea, dtTileCacheLayer& layer, dtTileCacheDistanceField dfield) { dtAssert(alloc); const int w = (int)layer.header->width; const int h = (int)layer.header->height; const int size = w*h; dtFixedArray<unsigned short> buf(alloc, size*4); if (!buf) { return DT_FAILURE | DT_OUT_OF_MEMORY; } dtIntArray stack(1024); dtIntArray visited(1024); unsigned short* srcReg = buf; unsigned short* srcDist = buf+size; unsigned short* dstReg = buf+size*2; unsigned short* dstDist = buf+size*3; memset(srcReg, 0, sizeof(unsigned short)*size); memset(srcDist, 0, sizeof(unsigned short)*size); unsigned short regionId = 1; unsigned short level = (dfield.maxDist+1) & ~1; // TODO: Figure better formula, expandIters defines how much the // watershed "overflows" and simplifies the regions. Tying it to // agent radius was usually good indication how greedy it could be. // const int expandIters = 4 + walkableRadius * 2; const int expandIters = 8; while (level > 0) { level = level >= 2 ? level-2 : 0; // Expand current regions until no empty connected cells found. if (expandRegions(expandIters, level, layer, dfield, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) { dtSwap(srcReg, dstReg); dtSwap(srcDist, dstDist); } // Mark new regions with IDs. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const int i=x+y*w; if (dfield.data[i] < level || srcReg[i] != 0 || layer.areas[i] == DT_TILECACHE_NULL_AREA) continue; if (floodRegion(x, y, i, level, regionId, layer, dfield, srcReg, srcDist, stack)) regionId++; } } } // Expand current regions until no empty connected cells found. if (expandRegions(expandIters*8, 0, layer, dfield, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) { dtSwap(srcReg, dstReg); dtSwap(srcDist, dstDist); } dtStatus status = filterSmallRegions(alloc, layer, minRegionArea, mergeRegionArea, regionId, srcReg); if (dtStatusFailed(status)) { return status; } // Write the result out. memcpy(layer.regs, srcReg, sizeof(unsigned short)*size); layer.regCount = regionId; return DT_SUCCESS; }
void HoleRepairer::computeSignedDistanceField() { // distance field from the mesh std::cout << "Computing mesh distance field..." << std::endl; computeUDFmesh(&m_distanceGrid); // distance field from the hole std::cout << "Computing holes distance field..." << std::endl; computeUDFhole(&m_holeDistance); // compute the mask and final distance field std::cout << "Computing mask..." << std::endl; for (int i = 0; i < VOX_SAMPLES; i++) { for (int j = 0; j < VOX_SAMPLES; j++) { for (int k = 0; k < VOX_SAMPLES; k++) { float mdist = m_distanceGrid.get(i, j, k); float hdist = m_holeDistance.get(i, j, k); if (hdist < mdist + 0.5*glm::length(m_voxelSize)) { m_triangleMask.set(i, j, k, true); m_distanceGrid.set(i, j, k, mdist); } else { m_triangleMask.set(i, j, k, false); m_distanceGrid.set(i, j, k, mdist); } } } } std::cout << "Generating signed distance field..." << std::endl; // compute regions in the mask to propagate the sign Grid3D<int> regionGrid(VOX_SAMPLES, 0); int numRegions = 0; bool foundRegion = true; while (foundRegion) { foundRegion = false; for (int i = 0; i < VOX_SAMPLES; i++) { for (int j = 0; j < VOX_SAMPLES; j++) { for (int k = 0; k < VOX_SAMPLES; k++) { if (!m_triangleMask.get(i, j, k) && regionGrid.get(i, j, k) == 0) { numRegions++; foundRegion = true; floodRegion(®ionGrid, &m_triangleMask, m_surfaceSticks, i, j, k, numRegions); } } } } } // votes based on sticks std::vector<int> positiveVotes(numRegions+1, 0); std::vector<int> negativeVotes(numRegions+1, 0); for (std::set<std::pair<int, int> >::const_iterator it = m_surfaceSticks.begin(); it != m_surfaceSticks.end(); it++) { int v1id = it->first; int v2id = it->second; int i1 = v1id/(VOX_SAMPLES*VOX_SAMPLES); int j1 = (v1id%(VOX_SAMPLES*VOX_SAMPLES))/VOX_SAMPLES; int k1 = (v1id%(VOX_SAMPLES*VOX_SAMPLES))%VOX_SAMPLES; int i2 = v2id/(VOX_SAMPLES*VOX_SAMPLES); int j2 = (v2id%(VOX_SAMPLES*VOX_SAMPLES))/VOX_SAMPLES; int k2 = (v2id%(VOX_SAMPLES*VOX_SAMPLES))%VOX_SAMPLES; int r1 = regionGrid.get(i1, j1, k1); int r2 = regionGrid.get(i2, j2, k2); // if both endpoints are marked as positive, this is not a safe sign sample if (m_positivePoints.find(v1id) != m_positivePoints.end() && m_positivePoints.find(v2id) == m_positivePoints.end()) { positiveVotes[r1]++; negativeVotes[r2]++; } else if (m_positivePoints.find(v2id) != m_positivePoints.end() && m_positivePoints.find(v1id) == m_positivePoints.end()) { positiveVotes[r2]++; negativeVotes[r1]++; } } // define sign to regions according to votes std::vector<bool> setSign(numRegions+1, false); std::vector<bool> posRegion(numRegions+1, false); for (int i = 1; i <= numRegions; i++) { if (positiveVotes[i] != negativeVotes[i]) { setSign[i] = true; posRegion[i] = positiveVotes[i] > negativeVotes[i]; } } // assign sign for (int i = 0; i < VOX_SAMPLES; i++) { for (int j = 0; j < VOX_SAMPLES; j++) { for (int k = 0; k < VOX_SAMPLES; k++) { int rid = regionGrid.get(i, j, k); if (rid > 0) { float dist = m_distanceGrid.get(i, j, k); if (!setSign[rid]) { bool positive; findSign(®ionGrid, setSign, posRegion, m_surfaceSticks, i, j, k, positive); posRegion[rid] = positive; setSign[rid] = true; } if (!posRegion[rid]) { m_distanceGrid.set(i, j, k, -dist); } } } } } }
/// @UE4: rcBuildRegions is now split into two functions: gathering regions and merging them /// it allows reusing existing code for building layer set (flood fill based partitioning) /// /// spanBuf4 is temporary buffer, allocated and freed by caller (size = chf.spanCount * 4) /// bool rcGatherRegionsNoFilter(rcContext* ctx, rcCompactHeightfield& chf, const int borderSize, unsigned short* spanBuf4) { const int w = chf.width; const int h = chf.height; rcIntArray stack(1024); rcIntArray visited(1024); unsigned short* srcReg = spanBuf4; unsigned short* srcDist = spanBuf4+chf.spanCount; unsigned short* dstReg = spanBuf4+chf.spanCount*2; unsigned short* dstDist = spanBuf4+chf.spanCount*3; memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount); memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount); unsigned short regionId = 1; unsigned short level = (chf.maxDistance+1) & ~1; // TODO: Figure better formula, expandIters defines how much the // watershed "overflows" and simplifies the regions. Tying it to // agent radius was usually good indication how greedy it could be. // const int expandIters = 4 + walkableRadius * 2; const int expandIters = 8; if (borderSize > 0) { // Make sure border will not overflow. const int bw = rcMin(w, borderSize); const int bh = rcMin(h, borderSize); // Paint regions paintRectRegion(0, bw, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(w-bw, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(0, w, 0, bh, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(0, w, h-bh, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; chf.borderSize = borderSize; } while (level > 0) { level = level >= 2 ? level-2 : 0; ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND); // Expand current regions until no empty connected cells found. if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); } ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND); ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD); // Mark new regions with IDs. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA) continue; if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) regionId++; } } } ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD); } // Expand current regions until no empty connected cells found. if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) { rcSwap(srcReg, dstReg); rcSwap(srcDist, dstDist); } chf.maxRegions = regionId; return true; }