static bool CollectLayerRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, const int borderSize, unsigned short* srcReg, rcLayerRegionMonotone*& regs, int& nregs) { const int w = chf.width; const int h = chf.height; const int nsweeps = chf.width; rcScopedDelete<rcLayerSweepSpan> sweeps = (rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP); if (!sweeps) { ctx->log(RC_LOG_ERROR, "CollectLayerRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps); return false; } // Partition walkable area into monotone regions. rcIntArray prev(256); unsigned short regId = 0; for (int y = borderSize; y < h-borderSize; ++y) { prev.resize(regId+1); memset(&prev[0],0,sizeof(int)*regId); unsigned short sweepId = 0; for (int x = borderSize; x < w-borderSize; ++x) { const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { const rcCompactSpan& s = chf.spans[i]; if (chf.areas[i] == RC_NULL_AREA) continue; unsigned short sid = 0xffff; // -x if (rcGetCon(s, 0) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(0); const int ay = y + rcGetDirOffsetY(0); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xffff) sid = srcReg[ai]; } if (sid == 0xffff) { sid = sweepId++; sweeps[sid].nei = 0xffff; sweeps[sid].ns = 0; } // -y if (rcGetCon(s,3) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(3); const int ay = y + rcGetDirOffsetY(3); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); const unsigned short nr = srcReg[ai]; if (nr != 0xffff) { // Set neighbour when first valid neighbour is encoutered. if (sweeps[sid].ns == 0) sweeps[sid].nei = nr; if (sweeps[sid].nei == nr) { // Update existing neighbour sweeps[sid].ns++; prev[nr]++; } else { // This is hit if there is nore than one neighbour. // Invalidate the neighbour. sweeps[sid].nei = 0xffff; } } } srcReg[i] = sid; } } // Create unique ID. for (int i = 0; i < sweepId; ++i) { // If the neighbour is set and there is only one continuous connection to it, // the sweep will be merged with the previous one, else new region is created. if (sweeps[i].nei != 0xffff && prev[sweeps[i].nei] == sweeps[i].ns) { sweeps[i].id = sweeps[i].nei; } else { sweeps[i].id = regId++; } } // Remap local sweep ids to region ids. for (int x = borderSize; x < w-borderSize; ++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 (srcReg[i] != 0xffff) srcReg[i] = sweeps[srcReg[i]].id; } } } // Allocate and init layer regions. nregs = (int)regId; regs = (rcLayerRegionMonotone*)rcAlloc(sizeof(rcLayerRegionMonotone)*nregs, RC_ALLOC_TEMP); if (!regs) { ctx->log(RC_LOG_ERROR, "CollectLayerRegionsMonotone: Out of memory 'regs' (%d).", nregs); return false; } memset(regs, 0, sizeof(rcLayerRegionMonotone)*nregs); for (int i = 0; i < nregs; ++i) { regs[i].layerId = 0xffff; regs[i].ymin = 0xffff; regs[i].ymax = 0; } rcIntArray lregs(64); // Find region neighbours and overlapping regions. for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const rcCompactCell& c = chf.cells[x+y*w]; lregs.resize(0); for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { const rcCompactSpan& s = chf.spans[i]; const unsigned short ri = srcReg[i]; if (ri == 0xffff) continue; regs[ri].ymin = rcMin(regs[ri].ymin, s.y); regs[ri].ymax = rcMax(regs[ri].ymax, s.y); // Collect all region layers. lregs.push(ri); // Update neighbours for (int dir = 0; dir < 4; ++dir) { if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(dir); const int ay = y + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); const unsigned short rai = srcReg[ai]; if (rai != 0xffff && rai != ri) addUnique(regs[ri].neis, rai); } } } // Update overlapping regions. const int nlregs = lregs.size(); for (int i = 0; i < nlregs-1; ++i) { for (int j = i+1; j < nlregs; ++j) { if (lregs[i] != lregs[j]) { rcLayerRegionMonotone& ri = regs[lregs[i]]; rcLayerRegionMonotone& rj = regs[lregs[j]]; addUnique(ri.layers, lregs[j]); addUnique(rj.layers, lregs[i]); } } } } } return true; }
static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea, unsigned short& maxRegionId, rcCompactHeightfield& chf, unsigned short* srcReg, rcIntArray& overlaps) { const int w = chf.width; const int h = chf.height; const int nreg = maxRegionId+1; rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP); if (!regions) { ctx->log(RC_LOG_ERROR, "mergeAndFilterLayerRegions: Out of memory 'regions' (%d).", nreg); return false; } // Construct regions for (int i = 0; i < nreg; ++i) new(®ions[i]) rcRegion((unsigned short)i); // Find region neighbours and overlapping regions. rcIntArray lregs(32); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { const rcCompactCell& c = chf.cells[x+y*w]; lregs.resize(0); for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { const rcCompactSpan& s = chf.spans[i]; const unsigned short ri = srcReg[i]; if (ri == 0 || ri >= nreg) continue; rcRegion& reg = regions[ri]; reg.spanCount++; reg.ymin = rcMin(reg.ymin, s.y); reg.ymax = rcMax(reg.ymax, s.y); // Collect all region layers. lregs.push(ri); // Update neighbours for (int dir = 0; dir < 4; ++dir) { if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(dir); const int ay = y + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); const unsigned short rai = srcReg[ai]; if (rai > 0 && rai < nreg && rai != ri) addUniqueConnection(reg, rai); if (rai & RC_BORDER_REG) reg.connectsToBorder = true; } } } // Update overlapping regions. for (int i = 0; i < lregs.size()-1; ++i) { for (int j = i+1; j < lregs.size(); ++j) { if (lregs[i] != lregs[j]) { rcRegion& ri = regions[lregs[i]]; rcRegion& rj = regions[lregs[j]]; addUniqueFloorRegion(ri, lregs[j]); addUniqueFloorRegion(rj, lregs[i]); } } } } } // Create 2D layers from regions. unsigned short layerId = 1; for (int i = 0; i < nreg; ++i) regions[i].id = 0; // Merge montone regions to create non-overlapping areas. rcIntArray stack(32); for (int i = 1; i < nreg; ++i) { rcRegion& root = regions[i]; // Skip already visited. if (root.id != 0) continue; // Start search. root.id = layerId; stack.resize(0); stack.push(i); while (stack.size() > 0) { // Pop front rcRegion& reg = regions[stack[0]]; for (int j = 0; j < stack.size()-1; ++j) stack[j] = stack[j+1]; stack.resize(stack.size()-1); const int ncons = (int)reg.connections.size(); for (int j = 0; j < ncons; ++j) { const int nei = reg.connections[j]; rcRegion& regn = regions[nei]; // Skip already visited. if (regn.id != 0) continue; // Skip if the neighbour is overlapping root region. bool overlap = false; for (int k = 0; k < root.floors.size(); k++) { if (root.floors[k] == nei) { overlap = true; break; } } if (overlap) continue; // Deepen stack.push(nei); // Mark layer id regn.id = layerId; // Merge current layers to root. for (int k = 0; k < regn.floors.size(); ++k) addUniqueFloorRegion(root, regn.floors[k]); root.ymin = rcMin(root.ymin, regn.ymin); root.ymax = rcMax(root.ymax, regn.ymax); root.spanCount += regn.spanCount; regn.spanCount = 0; root.connectsToBorder = root.connectsToBorder || regn.connectsToBorder; } } layerId++; } // Remove small regions for (int i = 0; i < nreg; ++i) { if (regions[i].spanCount > 0 && regions[i].spanCount < minRegionArea && !regions[i].connectsToBorder) { unsigned short reg = regions[i].id; for (int j = 0; j < nreg; ++j) if (regions[j].id == reg) regions[j].id = 0; } } // Compress region Ids. for (int i = 0; i < nreg; ++i) { regions[i].remap = false; if (regions[i].id == 0) continue; // Skip nil regions. if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions. regions[i].remap = true; } unsigned short regIdGen = 0; for (int i = 0; i < nreg; ++i) { if (!regions[i].remap) continue; unsigned short oldId = regions[i].id; unsigned short newId = ++regIdGen; for (int j = i; j < nreg; ++j) { if (regions[j].id == oldId) { regions[j].id = newId; regions[j].remap = false; } } } maxRegionId = regIdGen; // Remap regions. for (int i = 0; i < chf.spanCount; ++i) { if ((srcReg[i] & RC_BORDER_REG) == 0) srcReg[i] = regions[srcReg[i]].id; } for (int i = 0; i < nreg; ++i) regions[i].~rcRegion(); rcFree(regions); return true; }