private int GetCornerHeight(int x, int y, int i, int dir, CompactHeightfield cfh, ref bool isBorderVertex) { CompactSpan s = cfh.Spans[i]; int ch = s.Y; int dirp = (dir + 1) & 0x3; uint[] regs = { 0, 0, 0, 0 }; regs[0] = (uint)(cfh.Spans[i].Reg | (cfh.Areas[i] << 16)); 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 * cfh.Width].Index + s.GetCon(dir); CompactSpan aspan = cfh.Spans[ai]; ch = Math.Max(ch, aspan.Y); regs[1] = (uint)(cfh.Spans[ai].Reg | (cfh.Areas[ai] << 16)); if (aspan.GetCon(dirp) != CompactHeightfield.NotConnected) { int ax2 = ax + Helper.GetDirOffsetX(dirp); int ay2 = ay + Helper.GetDirOffsetY(dirp); int ai2 = (int)cfh.Cells[ax2 + ay2 * cfh.Width].Index + aspan.GetCon(dirp); CompactSpan as2 = cfh.Spans[ai2]; ch = Math.Max(ch, as2.Y); regs[2] = (uint)(cfh.Spans[ai2].Reg | (cfh.Areas[ai2] << 16)); } } if (s.GetCon(dirp) != CompactHeightfield.NotConnected) { int ax = x + Helper.GetDirOffsetX(dirp); int ay = y + Helper.GetDirOffsetY(dirp); int ai = (int)cfh.Cells[ax + ay * cfh.Width].Index + s.GetCon(dirp); CompactSpan aspan = cfh.Spans[ai]; ch = Math.Max(ch, aspan.Y); regs[3] = (uint)(cfh.Spans[ai].Reg | (cfh.Areas[ai] << 16)); if (aspan.GetCon(dir) != CompactHeightfield.NotConnected) { int ax2 = ax + Helper.GetDirOffsetX(dir); int ay2 = ay + Helper.GetDirOffsetY(dir); int ai2 = (int)cfh.Cells[ax2 + ay2 * cfh.Width].Index + aspan.GetCon(dir); CompactSpan as2 = cfh.Spans[ai2]; ch = Math.Max(ch, as2.Y); regs[2] = (uint)(cfh.Spans[ai2].Reg | (cfh.Areas[ai2] << 16)); } } for (int j = 0; j < 4; j++) { int a = j; int b = (j + 1) & 0x3; int c = (j + 2) & 0x3; int d = (j + 3) & 0x3; bool twoSameExts = (regs[a] & regs[b] & CompactHeightfield.BorderReg) != 0 && regs[a] == regs[b]; bool twoInts = ((regs[c] | regs[d]) & CompactHeightfield.BorderReg) == 0; bool intsSameArea = (regs[c] >> 16) == (regs[d] >> 16); bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0; if (twoSameExts && twoInts && intsSameArea && noZeros) { isBorderVertex = true; break; } } return(ch); }
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 void WalkContour(int x, int y, int i, CompactHeightfield cfh, ref char[] flags, ref IntArray points) { char dir = (char)0; while ((flags[i] & (1 << dir)) == 0) { dir++; } char startDir = dir; char tempDir = dir; int starti = i; uint area = cfh.Areas[i]; int iter = 0; while (++iter < 40000) { if ((flags[i] & (1 << dir)) > 0) { bool isBorderVertex = false; bool isAreaBorder = false; int px = x; int py = GetCornerHeight(x, y, i, tempDir, cfh, ref isBorderVertex); int pz = y; if (dir == (char)0) { pz++; } else if (dir == (char)1) { px++; pz++; } else if (dir == (char)2) { px++; } int r = 0; CompactSpan s = cfh.Spans[i]; 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 * cfh.Width].Index + s.GetCon(dir); r = cfh.Spans[ai].Reg; if (area != cfh.Areas[ai]) { isAreaBorder = true; } } if (isBorderVertex) { r |= BorderVertex; } if (isAreaBorder) { r |= AreaBorder; } points.Push(px); points.Push(py); points.Push(pz); points.Push(r); flags[i] &= (char)~(1 << dir); dir = (char)((dir + 1) & 0x3); // rotate CW } else { int ni = -1; int nx = x + Helper.GetDirOffsetX(dir); int ny = y + Helper.GetDirOffsetY(dir); CompactSpan s = cfh.Spans[i]; if (s.GetCon(dir) != CompactHeightfield.NotConnected) { CompactCell nc = cfh.Cells[nx + ny * cfh.Width]; ni = (int)nc.Index + s.GetCon(dir); } if (ni == -1) { // error return; } x = nx; y = ny; i = ni; dir = (char)((dir + 3) & 0x3); } if (starti == i && startDir == dir) { break; } } }