private bool FilterSmallRegions(int minRegionArea, int mergeRegionArea, ref int[] srcReg) { int w = Width; int h = Height; int nreg = MaxRegions + 1; Region[] regions = new Region[nreg]; for (int i = 0; i < nreg; i++) { regions[i] = new Region(i); } for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { CompactCell c = Cells[x + y * w]; for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++) { int r = srcReg[i]; if (r <= 0 || r >= nreg) { continue; } Region reg = regions[r]; reg.SpanCount++; for (int j = (int)c.Index; j < ni; j++) { if (i == j) { continue; } int floorId = srcReg[j]; if (floorId <= 0 || floorId >= nreg) { continue; } AddUniqueFloorRegion(ref reg, floorId); } if (reg.Connections.Size > 0) { continue; } reg.AreaType = (short)Areas[i]; int ndir = -1; for (int dir = 0; dir < 4; dir++) { if (IsSolidEdge(srcReg, x, y, i, dir)) { ndir = dir; break; } } if (ndir != -1) { WalkContour(x, y, i, ndir, ref srcReg, ref reg); } } } } // remove too small regions. IntArray stack = new IntArray(32); IntArray trace = new IntArray(32); for (int i = 0; i < nreg; i++) { Region reg = regions[i]; if (reg.Id <= 0 || (reg.Id & BorderReg) != 0) { continue; } if (reg.SpanCount == 0) { continue; } if (reg.Visited) { continue; } bool connectsToBorder = false; int spanCount = 0; stack.Resize(0); trace.Resize(0); reg.Visited = true; stack.Push(i); while (stack.Size > 0) { int ri = stack.Pop(); Region creg = regions[ri]; spanCount += creg.SpanCount; trace.Push(ri); for (int j = 0; j < creg.Connections.Size; j++) { if ((creg.Connections[j] & BorderReg) != 0) { connectsToBorder = true; continue; } Region neireg = regions[creg.Connections[j]]; if (neireg.Visited) { continue; } if (neireg.Id <= 0 || (neireg.Id & BorderReg) != 0) { continue; } stack.Push(neireg.Id); neireg.Visited = true; } } // if the region's size is too small, remove it. if (spanCount < minRegionArea && !connectsToBorder) { for (int j = 0; j < trace.Size; j++) { regions[trace[j]].SpanCount = 0; regions[trace[j]].Id = 0; } } } int mergeCount = 0; do { mergeCount = 0; for (int i = 0; i < nreg; i++) { Region reg = regions[i]; if (reg.Id <= 0 || (reg.Id & BorderReg) != 0) { continue; } if (reg.SpanCount == 0) { continue; } if (reg.SpanCount > mergeRegionArea && IsRegionConnectedToBorder(reg)) { continue; } // small region with more than 1 connection // or region which is not connected to a border at all. // find smallest neighbor int smallest = int.MaxValue; int mergeId = reg.Id; for (int j = 0; j < reg.Connections.Size; j++) { if ((reg.Connections[j] & BorderReg) != 0) { continue; } Region mreg = regions[reg.Connections[j]]; if (mreg.Id <= 0 || (mreg.Id & BorderReg) != 0) { continue; } if (mreg.SpanCount < smallest && CanMergeWithRegion(reg, mreg) && CanMergeWithRegion(mreg, reg)) { smallest = mreg.SpanCount; mergeId = mreg.Id; } } if (mergeId != reg.Id) { int oldId = reg.Id; Region target = regions[mergeId]; if (MergeRegions(ref target, ref reg)) { for (int j = 0; j < nreg; j++) { if (regions[j].Id == 0 || (regions[j].Id & BorderReg) != 0) { continue; } if (regions[j].Id == oldId) { regions[j].Id = mergeId; } Region reg2 = regions[j]; ReplaceNeighbor(ref reg2, oldId, mergeId); } mergeCount++; } } } } while (mergeCount > 0); // compress regions Ids for (int i = 0; i < nreg; i++) { regions[i].Remap = false; if (regions[i].Id == 0 || (regions[i].Id & BorderReg) != 0) { continue; } regions[i].Remap = true; } int regIdGen = 0; for (int i = 0; i < nreg; i++) { if (!regions[i].Remap) { continue; } int oldId = regions[i].Id; int newId = ++regIdGen; for (int j = i; j < nreg; j++) { if (regions[j].Id == oldId) { regions[j].Id = newId; regions[j].Remap = false; } } } MaxRegions = regIdGen; // remap regions for (int i = 0; i < SpanCount; i++) { if ((srcReg[i] & BorderReg) == 0) { srcReg[i] = regions[srcReg[i]].Id; } } return(true); }
private void GetHeightData(CompactHeightfield chf, int[] p, int npoly, int[] verts, int bs, ref HeightPatch hp, ref IntArray stack) { for (int i = 0; i < hp.Width * hp.Height; i++) { hp.Data[i] = 0; } stack.Resize(0); int[] offset = { 0, 0, -1, -1, 0, -1, 1, -1, 1, 0, 1, 1, 0, 1, -1, 1, -1, 0 }; for (int j = 0; j < npoly; j++) { int cx = 0, cz = 0, ci = -1; int dmin = UnsetHeight; for (int k = 0; k < 9; k++) { int ax = verts[p[j] * 3 + 0] + offset[k * 2 + 0]; int ay = verts[p[j] * 3 + 1]; int az = verts[p[j] * 3 + 2] + offset[k * 2 + 1]; if (ax < hp.XMin || ax >= hp.XMin + hp.Width || az < hp.YMin || az >= hp.YMin + hp.Height) { continue; } CompactCell c = chf.Cells[(ax + bs) + (az + bs) * chf.Width]; for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++) { CompactSpan s = chf.Spans[i]; int d = Math.Abs(ay - s.Y); if (d < dmin) { cx = ax; cz = az; ci = i; dmin = d; } } } if (ci != -1) { stack.Push(cx); stack.Push(cz); stack.Push(ci); } } int pcx = 0, pcz = 0; for (int j = 0; j < npoly; j++) { pcx += verts[p[j] * 3 + 0]; pcz += verts[p[j] * 3 + 2]; } pcx /= npoly; pcz /= npoly; for (int i = 0; i < stack.Size; i += 3) { int cx = stack[i + 0]; int cy = stack[i + 1]; int idx = cx - hp.XMin + (cy - hp.YMin) * hp.Width; hp.Data[idx] = 1; } while (stack.Size > 0) { int ci = stack.Pop(); int cy = stack.Pop(); int cx = stack.Pop(); if (Math.Abs(cx - pcx) <= 1 && Math.Abs(cy - pcz) <= 1) { stack.Resize(0); stack.Push(cx); stack.Push(cy); stack.Push(ci); break; } CompactSpan cs = chf.Spans[ci]; for (int dir = 0; dir < 4; dir++) { if (cs.GetCon(dir) == CompactHeightfield.NotConnected) { continue; } int ax = cx + Helper.GetDirOffsetX(dir); int ay = cy + Helper.GetDirOffsetY(dir); if (ax < hp.XMin || ax >= (hp.XMin + hp.Width) || ay < hp.YMin || ay >= (hp.YMin + hp.Height)) { continue; } if (hp.Data[ax - hp.XMin + (ay - hp.YMin) * hp.Width] != 0) { continue; } int ai = (int)chf.Cells[(ax + bs) + (ay + bs) * chf.Width].Index + cs.GetCon(dir); int idx = ax - hp.XMin + (ay - hp.YMin) * hp.Width; hp.Data[idx] = 1; stack.Push(ax); stack.Push(ay); stack.Push(ai); } } for (int i = 0; i < hp.Data.Length; i++) { hp.Data[i] = UnsetHeight; } for (int i = 0; i < stack.Size; i += 3) { int cx = stack[i + 0]; int cy = stack[i + 1]; int ci = stack[i + 2]; int idx = cx - hp.XMin + (cy - hp.YMin) * hp.Width; CompactSpan cs = chf.Spans[ci]; hp.Data[idx] = cs.Y; } int RetractSize = 256; int head = 0; while (head * 3 < stack.Size) { int cx = stack[head * 3 + 0]; int cy = stack[head * 3 + 1]; int ci = stack[head * 3 + 2]; head++; if (head >= RetractSize) { head = 0; if (stack.Size > RetractSize * 3) { Array.Copy(stack.Data, RetractSize * 3, stack.Data, 0, stack.Size - RetractSize * 3); } stack.Resize(stack.Size - RetractSize * 3); } CompactSpan cs = chf.Spans[ci]; for (int dir = 0; dir < 4; dir++) { if (cs.GetCon(dir) == CompactHeightfield.NotConnected) { continue; } int ax = cx + Helper.GetDirOffsetX(dir); int ay = cy + Helper.GetDirOffsetY(dir); if (ax < hp.XMin || ax >= (hp.XMin + hp.Width) || ay < hp.YMin || ay >= (hp.YMin + hp.Height)) { continue; } if (hp.Data[ax - hp.XMin + (ay - hp.YMin) * hp.Width] != UnsetHeight) { continue; } int ai = (int)chf.Cells[(ax + bs) + (ay + bs) * chf.Width].Index + cs.GetCon(dir); CompactSpan aspan = chf.Spans[ai]; int idx = ax - hp.XMin + (ay - hp.YMin) * hp.Width; hp.Data[idx] = aspan.Y; stack.Push(ax); stack.Push(ay); stack.Push(ai); } } }
private bool FloodRegion(int x, int y, int i, int level, int r, ref int[] srcReg, ref int[] srcDist, ref IntArray stack) { int w = Width; uint area = Areas[i]; stack.Resize(0); stack.Push(x); stack.Push(y); stack.Push(i); srcReg[i] = r; srcDist[i] = 0; int lev = level >= 2 ? level - 2 : 0; int count = 0; while (stack.Size > 0) { int ci = stack.Pop(); int cy = stack.Pop(); int cx = stack.Pop(); CompactSpan cs = Spans[ci]; int ar = 0; for (int dir = 0; dir < 4; dir++) { if (cs.GetCon(dir) != NotConnected) { int ax = cx + Helper.GetDirOffsetX(dir); int ay = cy + Helper.GetDirOffsetY(dir); int ai = (int)Cells[ax + ay * w].Index + cs.GetCon(dir); if (Areas[ai] != area) { continue; } int nr = srcReg[ai]; if ((nr & BorderReg) != 0) { continue; } if (nr != 0 && nr != r) { ar = nr; } CompactSpan aspan = Spans[ai]; int dir2 = (dir + 1) & 0x3; if (aspan.GetCon(dir2) != NotConnected) { int ax2 = ax + Helper.GetDirOffsetX(dir2); int ay2 = ay + Helper.GetDirOffsetY(dir2); int ai2 = (int)Cells[ax2 + ay2 * w].Index + aspan.GetCon(dir2); if (Areas[ai2] != area) { continue; } int nr2 = srcReg[ai2]; if (nr2 != 0 && nr2 != r) { ar = nr2; } } } } if (ar != 0) { srcReg[ci] = 0; continue; } count++; for (int dir = 0; dir < 4; dir++) { if (cs.GetCon(dir) != NotConnected) { int ax = cx + Helper.GetDirOffsetX(dir); int ay = cy + Helper.GetDirOffsetY(dir); int ai = (int)Cells[ax + ay * w].Index + cs.GetCon(dir); if (Areas[ai] != area) { continue; } if (Dist[ai] >= lev && srcReg[ai] == 0) { srcReg[ai] = r; srcDist[ai] = 0; stack.Push(ax); stack.Push(ay); stack.Push(ai); } } } } return(count > 0); }