private void RemoveDegenerateSegments(ref IntArray simplified) { for (int i = 0; i < simplified.Size / 4; i++) { int ni = i + 1; if (ni >= simplified.Size / 4) { ni = 0; } if (simplified[i * 4 + 0] == simplified[ni * 4 + 0] && simplified[i * 4 + 2] == simplified[ni * 4 + 2]) { for (int j = i; j < simplified.Size / 4 - 1; j++) { simplified[j * 4 + 0] = simplified[(j + 1) * 4 + 0]; simplified[j * 4 + 1] = simplified[(j + 1) * 4 + 1]; simplified[j * 4 + 2] = simplified[(j + 1) * 4 + 2]; simplified[j * 4 + 3] = simplified[(j + 1) * 4 + 3]; } simplified.Resize(simplified.Size - 4); } } }
private void DelaunayHull(int npts, float[] pts, int nhull, int[] hull, ref IntArray tris, ref IntArray edges) { int nfaces = 0; int nedges = 0; int maxEdges = npts * 10; edges.Resize(maxEdges * 4); for (int i = 0, j = nhull - 1; i < nhull; j = i++) { AddEdge(ref edges, ref nedges, maxEdges, hull[j], hull[i], Hull, Undef); } int currentEdge = 0; while (currentEdge < nedges) { if (edges[currentEdge * 4 + 2] == Undef) { CompleteFacet(pts, npts, ref edges, ref nedges, maxEdges, ref nfaces, currentEdge); } if (edges[currentEdge * 4 + 3] == Undef) { CompleteFacet(pts, npts, ref edges, ref nedges, maxEdges, ref nfaces, currentEdge); } currentEdge++; } tris.Resize(nfaces * 4); for (int i = 0; i < nfaces * 4; i++) { tris[i] = -1; } for (int i = 0; i < nedges; i++) { int e = i * 4; if (edges[e + 3] >= 0) { int t = edges[e + 3] * 4; if (tris[t + 0] == -1) { tris[t + 0] = edges[e + 0]; tris[t + 1] = edges[e + 1]; } else if (tris[t + 0] == edges[e + 1]) { tris[t + 2] = edges[e + 0]; } else if (tris[t + 1] == edges[e + 0]) { tris[t + 2] = edges[e + 1]; } } if (edges[e + 2] >= 0) { int t = edges[e + 2] * 4; if (tris[t + 0] == -1) { tris[t + 0] = edges[e + 1]; tris[t + 1] = edges[e + 0]; } else if (tris[t + 0] == edges[e + 0]) { tris[t + 2] = edges[e + 1]; } else if (tris[t + 1] == edges[e + 1]) { tris[t + 2] = edges[e + 0]; } } } for (int i = 0; i < tris.Size / 4; i++) { int t = i * 4; if (tris[t + 0] == -1 || tris[t + 1] == -1 || tris[t + 2] == -1) { // error tris[t + 0] = tris[tris.Size - 4]; tris[t + 1] = tris[tris.Size - 3]; tris[t + 2] = tris[tris.Size - 2]; tris[t + 3] = tris[tris.Size - 1]; tris.Resize(tris.Size - 4); --i; } } }
private bool BuildPolyDetail(float[] inArray, int nin, float sampleDist, float sampleMaxError, CompactHeightfield chf, HeightPatch hp, ref float[] verts, out int nverts, ref IntArray tris, ref IntArray edges, ref IntArray samples) { int MaxVerts = 127; int MaxTris = 255; int MaxVertsPerEdge = 32; float[] edge = new float[(MaxVertsPerEdge + 1) * 3]; int[] hull = new int[MaxVerts]; int nhull = 0; nverts = 0; for (int i = 0; i < nin; i++) { Array.Copy(inArray, i * 3, verts, i * 3, 3); } nverts = nin; float cs = chf.Cs; float ics = 1.0f / cs; if (sampleDist > 0) { for (int i = 0, j = nin - 1; i < nin; j = i++) { int vj = j * 3; int vi = i * 3; bool swapped = false; if (Math.Abs(inArray[vj + 0] - inArray[vi + 0]) < 1e-6f) { if (inArray[vj + 2] > inArray[vi + 2]) { int temp = vj; vj = vi; vi = temp; swapped = true; } } else { if (inArray[vj + 0] > inArray[vi + 0]) { int temp = vj; vj = vi; vi = temp; swapped = true; } } float dx = inArray[vi + 0] - inArray[vj + 0]; float dy = inArray[vi + 1] - inArray[vj + 1]; float dz = inArray[vi + 2] - inArray[vj + 2]; float d = (float)Math.Sqrt(dx * dx + dz * dz); int nn = 1 + (int)Math.Floor(d / sampleDist); if (nn >= MaxVertsPerEdge) { nn = MaxVertsPerEdge - 1; } if (nverts + nn >= MaxVerts) { nn = MaxVerts - 1 - nverts; } for (int k = 0; k <= nn; k++) { float u = (float)k / (float)nn; int pos = k * 3; edge[pos + 0] = inArray[vj + 0] + dx * u; edge[pos + 1] = inArray[vj + 1] + dy * u; edge[pos + 2] = inArray[vj + 2] + dz * u; edge[pos + 1] = GetHeight(edge[pos + 0], edge[pos + 1], edge[pos + 2], cs, ics, chf.Ch, hp) * chf.Ch; } int[] idx = new int[MaxVertsPerEdge]; idx[0] = 0; idx[1] = nn; int nidx = 2; for (int k = 0; k < nidx - 1;) { int a = idx[k]; int b = idx[k + 1]; int va = a * 3; int vb = b * 3; float maxd = 0; int maxi = -1; for (int m = a + 1; m < b; m++) { float dev = DistancePtSeg(edge[m * 3 + 0], edge[m * 3 + 1], edge[m * 3 + 2], edge[va + 0], edge[va + 1], edge[va + 2], edge[vb + 0], edge[vb + 1], edge[vb + 2]); if (dev > maxd) { maxd = dev; maxi = m; } } if (maxi != -1 && maxd > sampleMaxError * sampleMaxError) { for (int m = nidx; m > k; m--) { idx[m] = idx[m - 1]; } idx[k + 1] = maxi; nidx++; } else { k++; } } hull[nhull++] = j; if (swapped) { for (int k = nidx - 2; k > 0; k--) { Array.Copy(edge, idx[k] * 3, verts, nverts * 3, 3); hull[nhull++] = nverts; nverts++; } } else { for (int k = 1; k < nidx - 1; k++) { Array.Copy(edge, idx[k] * 3, verts, nverts * 3, 3); hull[nhull++] = nverts; nverts++; } } } } edges.Resize(0); tris.Resize(0); DelaunayHull(nverts, verts, nhull, hull, ref tris, ref edges); if (tris.Size == 0) { // error add default data for (int i = 2; i < nverts; i++) { tris.Push(0); tris.Push(i - 1); tris.Push(i); tris.Push(0); } return(true); } if (sampleDist > 0) { float[] bmin = new float[3], bmax = new float[3]; Array.Copy(inArray, 0, bmin, 0, 3); Array.Copy(inArray, 0, bmax, 0, 3); for (int i = 1; i < nin; i++) { bmin[0] = Math.Min(bmin[0], inArray[i * 3 + 0]); bmin[1] = Math.Min(bmin[1], inArray[i * 3 + 1]); bmin[2] = Math.Min(bmin[2], inArray[i * 3 + 2]); bmax[0] = Math.Max(bmax[0], inArray[i * 3 + 0]); bmax[1] = Math.Max(bmax[1], inArray[i * 3 + 1]); bmax[2] = Math.Max(bmax[2], inArray[i * 3 + 2]); } int x0 = (int)Math.Floor(bmin[0] / sampleDist); int x1 = (int)Math.Ceiling(bmax[0] / sampleDist); int z0 = (int)Math.Floor(bmin[2] / sampleDist); int z1 = (int)Math.Ceiling(bmax[2] / sampleDist); samples.Resize(0); for (int z = z0; z < z1; z++) { for (int x = x0; x < x1; x++) { float[] pt = new float[3]; pt[0] = x * sampleDist; pt[1] = (bmax[1] + bmin[1]) * 0.5f; pt[2] = z * sampleDist; if (DistToPoly(nin, inArray, pt[0], pt[1], pt[2]) > -sampleDist / 2) { continue; } samples.Push(x); samples.Push(GetHeight(pt[0], pt[1], pt[2], cs, ics, chf.Ch, hp)); samples.Push(z); samples.Push(0); } } int nsamples = samples.Size / 4; for (int iter = 0; iter < nsamples; iter++) { if (nverts >= MaxVerts) { break; } float[] bestpt = { 0, 0, 0 }; float bestd = 0; int besti = -1; for (int i = 0; i < nsamples; i++) { int s = i * 4; if (samples[s + 3] != 0) { continue; } float[] pt = new float[3]; pt[0] = samples[s + 0] * sampleDist + GetJitterX(i) * cs * 0.1f; pt[1] = samples[s + 1] * chf.Ch; pt[2] = samples[s + 2] * sampleDist + GetJitterY(i) * cs * 0.1f; float d = DistToTriMesh(pt[0], pt[1], pt[2], verts, nverts, tris, tris.Size / 4); if (d < 0) { continue; // didn't hit the mesh } if (d > bestd) { bestd = d; besti = i; Array.Copy(pt, 0, bestpt, 0, 3); } } if (bestd <= sampleMaxError || besti == -1) { break; } samples[besti * 4 + 3] = 1; Array.Copy(bestpt, 0, verts, nverts * 3, 3); nverts++; edges.Resize(0); tris.Resize(0); DelaunayHull(nverts, verts, nhull, hull, ref tris, ref edges); } } int ntris = tris.Size / 4; if (ntris > MaxTris) { // error, shrink tris.Resize(MaxTris * 4); } 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 void SimplifyContour(ref IntArray points, ref IntArray simplified, float maxError, int maxEdgeLen, int buildFlags) { bool hasConnections = false; for (int i = 0; i < points.Size; i += 4) { if ((points[i + 3] & ContourRegMask) != 0) { hasConnections = true; break; } } if (hasConnections) { for (int i = 0, ni = points.Size / 4; i < ni; i++) { int ii = (i + 1) % ni; bool differentRegs = (points[i * 4 + 3] & ContourRegMask) != (points[ii * 4 + 3] & ContourRegMask); bool areaBorders = (points[i * 4 + 3] & AreaBorder) != (points[ii * 4 + 3] & AreaBorder); if (differentRegs || areaBorders) { simplified.Push(points[i * 4 + 0]); simplified.Push(points[i * 4 + 1]); simplified.Push(points[i * 4 + 2]); simplified.Push(i); } } } if (simplified.Size == 0) { int llx = points[0]; int lly = points[1]; int llz = points[2]; int lli = 0; int urx = points[0]; int ury = points[1]; int urz = points[2]; int uri = 0; for (int i = 0; i < points.Size; i += 4) { int x = points[i + 0]; int y = points[i + 1]; int z = points[i + 2]; if (x < llx || (x == llx && z < llz)) { llx = x; lly = y; llz = z; lli = i / 4; } if (x > urx || (x == urx && z > urz)) { urx = x; ury = y; urz = z; uri = i / 4; } } simplified.Push(llx); simplified.Push(lly); simplified.Push(llz); simplified.Push(lli); simplified.Push(urx); simplified.Push(ury); simplified.Push(urz); simplified.Push(uri); } int pn = points.Size / 4; // go through all the points, as more points are added on to the end for (int i = 0; i < simplified.Size / 4;) { int ii = (i + 1) % (simplified.Size / 4); int ax = simplified[i * 4 + 0]; int az = simplified[i * 4 + 2]; int ai = simplified[i * 4 + 3]; int bx = simplified[ii * 4 + 0]; int bz = simplified[ii * 4 + 2]; int bi = simplified[ii * 4 + 3]; float maxd = 0; int maxi = -1; int ci, cinc, endi; if (bx > ax || (bx == ax && bz > az)) { cinc = 1; ci = (ai + cinc) % pn; endi = bi; } else { cinc = pn - 1; ci = (bi + cinc) % pn; endi = ai; } if ((points[ci * 4 + 3] & ContourRegMask) == 0 || (points[ci * 4 + 3] & AreaBorder) != 0) { while (ci != endi) { float d = DistancePtSeg(points[ci * 4 + 0], points[ci * 4 + 2], ax, az, bx, bz); if (d > maxd) { maxd = d; maxi = ci; } ci = (ci + cinc) % pn; } } // if max deviation is larger than accepted error add new point float errorSqrd = maxError * maxError; if (maxi != -1 && maxd > (maxError * maxError)) { simplified.Resize(simplified.Size + 4); int n = simplified.Size / 4; for (int j = n - 1; j > i; --j) { simplified[j * 4 + 0] = simplified[(j - 1) * 4 + 0]; simplified[j * 4 + 1] = simplified[(j - 1) * 4 + 1]; simplified[j * 4 + 2] = simplified[(j - 1) * 4 + 2]; simplified[j * 4 + 3] = simplified[(j - 1) * 4 + 3]; } simplified[(i + 1) * 4 + 0] = points[maxi * 4 + 0]; simplified[(i + 1) * 4 + 1] = points[maxi * 4 + 1]; simplified[(i + 1) * 4 + 2] = points[maxi * 4 + 2]; simplified[(i + 1) * 4 + 3] = maxi; } else { i++; } } // split too long edges if (maxEdgeLen > 0 && (buildFlags & (BuildContourFlags.ContourTessWallEdges | BuildContourFlags.ContourTessAreaEdges)) != 0) { for (int i = 0; i < simplified.Size / 4;) { int ii = (i + 1) % (simplified.Size / 4); int ax = simplified[i * 4 + 0]; int az = simplified[i * 4 + 2]; int ai = simplified[i * 4 + 3]; int bx = simplified[ii * 4 + 0]; int bz = simplified[ii * 4 + 2]; int bi = simplified[ii * 4 + 3]; int maxi = -1; int ci = (ai + 1) % pn; bool tess = false; if ((buildFlags & BuildContourFlags.ContourTessWallEdges) != 0 && (points[ci * 4 + 3] & ContourRegMask) == 0) { tess = true; } if ((buildFlags & BuildContourFlags.ContourTessAreaEdges) != 0 && (points[ci * 4 + 3] & AreaBorder) != 0) { tess = true; } if (tess) { int dx = bx - ax; int dz = bz - az; if (dx * dx + dz * dz > maxEdgeLen * maxEdgeLen) { int n = bi < ai ? (bi + pn - ai) : (bi - ai); if (n > 1) { if (bx > ax || (bx == ax && bz > az)) { maxi = (int)(ai + n / 2f) % pn; } else { maxi = (int)(ai + (n + 1) / 2f) % pn; } } } } if (maxi != -1) { simplified.Resize(simplified.Size + 4); int n = simplified.Size / 4; for (int j = n - 1; j > i; j--) { simplified[j * 4 + 0] = simplified[(j - 1) * 4 + 0]; simplified[j * 4 + 1] = simplified[(j - 1) * 4 + 1]; simplified[j * 4 + 2] = simplified[(j - 1) * 4 + 2]; simplified[j * 4 + 3] = simplified[(j - 1) * 4 + 3]; } simplified[(i + 1) * 4 + 0] = points[maxi * 4 + 0]; simplified[(i + 1) * 4 + 1] = points[maxi * 4 + 1]; simplified[(i + 1) * 4 + 2] = points[maxi * 4 + 2]; simplified[(i + 1) * 4 + 3] = maxi; } else { i++; } } } for (int i = 0; i < simplified.Size / 4; i++) { int ai = (simplified[i * 4 + 3] + 1) % pn; int bi = simplified[i * 4 + 3]; simplified[i * 4 + 3] = (points[ai * 4 + 3] & (ContourRegMask | AreaBorder)) | (points[bi * 4 + 3] & BorderVertex); } }
public ContourSet(CompactHeightfield cfh, float maxError, int maxEdgeLen, int buildFlags = BuildContourFlags.ContourTessWallEdges) { int w = cfh.Width; int h = cfh.Height; int borderSize = cfh.BorderSize; BMin = new float[3]; BMax = new float[3]; Array.Copy(cfh.BMin, BMin, 3); Array.Copy(cfh.BMax, BMax, 3); if (borderSize > 0) { float pad = borderSize * cfh.Cs; BMin[0] += pad; BMin[2] += pad; BMax[0] -= pad; BMax[2] -= pad; } Cs = cfh.Cs; Ch = cfh.Ch; Width = cfh.Width - cfh.BorderSize * 2; Height = cfh.Height - cfh.BorderSize * 2; BorderSize = cfh.BorderSize; int maxContours = Math.Max(cfh.MaxRegions, 8); Conts = new Contour[maxContours]; for (int i = 0; i < maxContours; i++) { Conts[i] = new Contour(); } NConts = 0; char[] flags = new char[cfh.SpanCount]; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { CompactCell c = cfh.Cells[x + y * w]; for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++) { if (i == 4782) { int z = 0; } int res = 0; CompactSpan s = cfh.Spans[i]; if (s.Reg == 0 || (s.Reg & CompactHeightfield.BorderReg) != 0) { flags[i] = (char)0; continue; } for (int dir = 0; dir < 4; dir++) { int r = 0; if (s.GetCon(dir) != CompactHeightfield.NotConnected) { int ax = x + Helper.GetDirOffsetX(dir); int ay = y + Helper.GetDirOffsetY(dir); int ai = (int)cfh.Cells[ax + ay * w].Index + s.GetCon(dir); r = cfh.Spans[ai].Reg; } if (r == cfh.Spans[i].Reg) { res |= (1 << dir); } } flags[i] = (char)(res ^ 0xf); } } } IntArray verts = new IntArray(256); IntArray simplified = new IntArray(64); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { CompactCell c = cfh.Cells[x + y * w]; for (int i = (int)c.Index, ni = (int)(c.Index + c.Count); i < ni; i++) { if (flags[i] == 0 || flags[i] == 0xf) { flags[i] = (char)0; continue; } int reg = cfh.Spans[i].Reg; if (reg == 0 || (reg & CompactHeightfield.BorderReg) != 0) { continue; } uint area = cfh.Areas[i]; verts.Resize(0); simplified.Resize(0); WalkContour(x, y, i, cfh, ref flags, ref verts); SimplifyContour(ref verts, ref simplified, maxError, maxEdgeLen, buildFlags); RemoveDegenerateSegments(ref simplified); if ((simplified.Size / 4) >= 3) { // We need more space than we allocated... if (NConts >= maxContours) { int oldMax = maxContours; maxContours *= 2; Contour[] newConts = new Contour[maxContours]; for (int j = 0; j < maxContours; j++) { newConts[j] = new Contour(); } for (int j = 0; j < NConts; j++) { newConts[j] = Conts[j]; } Conts = newConts; } Contour cont = Conts[NConts++]; cont.NVerts = simplified.Size / 4; cont.Verts = new int[cont.NVerts * 4]; Array.Copy(simplified.ToArray(), cont.Verts, cont.NVerts * 4); if (borderSize > 0) { for (int j = 0; j < cont.NVerts; j++) { int v = j * 4; cont.Verts[v + 0] -= borderSize; cont.Verts[v + 2] -= borderSize; } } cont.NRVerts = verts.Size / 4; cont.RVerts = new int[cont.NRVerts * 4]; Array.Copy(verts.ToArray(), cont.RVerts, cont.NRVerts * 4); if (borderSize > 0) { for (int j = 0; j < cont.NRVerts; j++) { int v = j * 4; cont.RVerts[v + 0] -= borderSize; cont.RVerts[v + 2] -= borderSize; } } cont.Reg = reg; cont.Area = (short)area; } } } } // check and merge droppings for (int i = 0; i < NConts; i++) { Contour cont = Conts[i]; if (CalcAreaOfPolygon2D(cont.Verts, cont.NVerts) < 0) { int mergeIdx = -1; for (int j = 0; j < NConts; j++) { if (i == j) { continue; } if (Conts[j].NVerts > 0 && Conts[j].Reg == cont.Reg) { if (CalcAreaOfPolygon2D(Conts[j].Verts, Conts[j].NVerts) > 0) { mergeIdx = j; break; } } } if (mergeIdx == -1) { // error } else { Contour mcont = Conts[mergeIdx]; int ia = 0, ib = 0; GetClosestIndices(mcont.Verts, mcont.NVerts, cont.Verts, cont.NVerts, ref ia, ref ib); if (ia == -1 || ib == -1) { // bad merge continue; } if (!MergeContours(ref mcont, ref cont, ia, ib)) { // Merge failed continue; } } } } }
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 int[] ExpandRegions(int maxIter, int level, ref int[] srcReg, ref int[] srcDist, ref int[] dstReg, ref int[] dstDist, ref IntArray stack) { int w = Width; int h = Height; stack.Resize(0); 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++) { if (Dist[i] >= level && srcReg[i] == 0 && Areas[i] != HeightField.NullArea) { stack.Push(x); stack.Push(y); stack.Push(i); } } } } int iter = 0; while (stack.Size > 0) { int failed = 0; Buffer.BlockCopy(srcReg, 0, dstReg, 0, sizeof(int) * SpanCount); Buffer.BlockCopy(srcDist, 0, dstDist, 0, sizeof(int) * SpanCount); //Array.Copy(srcReg, dstReg, SpanCount); //Array.Copy(srcDist, dstDist, SpanCount); for (int j = 0; j < stack.Size; j += 3) { int x = stack[j + 0]; int y = stack[j + 1]; int i = stack[j + 2]; if (i < 0) { failed++; continue; } int r = srcReg[i]; int d2 = short.MaxValue; uint area = Areas[i]; CompactSpan s = Spans[i]; for (int dir = 0; dir < 4; dir++) { if (s.GetCon(dir) == NotConnected) { continue; } int ax = x + Helper.GetDirOffsetX(dir); int ay = y + Helper.GetDirOffsetY(dir); int ai = (int)Cells[ax + ay * w].Index + s.GetCon(dir); if (Areas[ai] != area) { continue; } if (srcReg[ai] > 0 && (srcReg[ai] & BorderReg) == 0) { if (srcDist[ai] + 2 < d2) { r = srcReg[ai]; d2 = srcDist[ai] + 2; } } } if (r != 0) { stack[j + 2] = -1; dstReg[i] = r; dstDist[i] = d2; } else { failed++; } } int[] temp = srcReg; srcReg = dstReg; dstReg = temp; temp = srcDist; srcDist = dstDist; dstDist = temp; if (failed * 3 == stack.Size) { break; } if (level > 0) { ++iter; if (iter >= maxIter) { break; } } } return(srcReg); }
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); }