private bool MergeContours(ref Contour ca, ref Contour cb, int ia, int ib) { int maxVerts = ca.NVerts + cb.NVerts + 2; int[] verts = new int[maxVerts * 4]; int nv = 0; for (int i = 0; i < ca.NVerts; i++) { int dst = nv * 4; int src = ((ia + i) % ca.NVerts) * 4; verts[dst + 0] = ca.Verts[src + 0]; verts[dst + 1] = ca.Verts[src + 1]; verts[dst + 2] = ca.Verts[src + 2]; verts[dst + 3] = ca.Verts[src + 3]; nv++; } for (int i = 0; i < cb.NVerts; i++) { int dst = nv * 4; int src = ((ib + i) % cb.NVerts) * 4; verts[dst + 0] = cb.Verts[src + 0]; verts[dst + 1] = cb.Verts[src + 1]; verts[dst + 2] = cb.Verts[src + 2]; verts[dst + 3] = cb.Verts[src + 3]; nv++; } ca.Verts = verts; ca.NVerts = nv; cb.Verts = null; cb.NVerts = 0; return(true); }
public PolyMesh(ContourSet cset, int nvp) { Array.Copy(cset.BMin, BMin, 3); Array.Copy(cset.BMax, BMax, 3); Cs = cset.Cs; Ch = cset.Ch; BorderSize = cset.BorderSize; int maxVertices = 0; int maxTris = 0; int maxVertsPerCont = 0; for (int i = 0; i < cset.NConts; i++) { if (cset.Conts[i].NVerts < 3) { continue; } maxVertices += cset.Conts[i].NVerts; maxTris += cset.Conts[i].NVerts - 2; maxVertsPerCont = Math.Max(maxVertsPerCont, cset.Conts[i].NVerts); } short[] vflags = new short[maxVertices]; Verts = new int[maxVertices * 3]; Polys = new int[maxTris * nvp * 2]; Regs = new int[maxTris]; Areas = new short[maxTris]; NVerts = 0; NPolys = 0; Nvp = nvp; MaxPolys = maxTris; for (int i = 0; i < maxTris * nvp * 2; i++) { Polys[i] = 0xffff; // memset(mesh.polys, 0xff) } int[] nextVert = new int[maxVertices]; int[] firstVert = new int[VertexBucketCount]; for (int i = 0; i < firstVert.Length; i++) { firstVert[i] = -1; } int[] indices = new int[maxVertsPerCont]; int[] tris = new int[maxVertsPerCont * 3]; int[] polys = new int[(maxVertsPerCont + 1) * nvp]; int[] tmpPoly = new int[nvp]; for (int i = 0; i < cset.NConts; i++) { Contour cont = cset.Conts[i]; if (cont.NVerts < 3) { continue; } for (int j = 0; j < cont.NVerts; j++) { indices[j] = j; } int ntris = Triangulate(cont.NVerts, cont.Verts, ref indices, ref tris); if (ntris <= 0) { // error ntris = -ntris; } for (int j = 0; j < cont.NVerts; j++) { int v = j * 4; indices[j] = AddVertex(cont.Verts[v + 0], cont.Verts[v + 1], cont.Verts[v + 2], ref firstVert, ref nextVert); if ((cont.Verts[v + 3] & ContourSet.BorderVertex) != 0) { vflags[indices[j]] = 1; } } //build initial polygons int npolys = 0; for (int j = 0; j < polys.Length; j++) { polys[j] = 0xffff; } for (int j = 0; j < ntris; j++) { int t = j * 3; if (tris[t + 0] != tris[t + 1] && tris[t + 0] != tris[t + 2] && tris[t + 1] != tris[t + 2]) { polys[npolys * nvp + 0] = indices[tris[t + 0]]; polys[npolys * nvp + 1] = indices[tris[t + 1]]; polys[npolys * nvp + 2] = indices[tris[t + 2]]; npolys++; } } if (npolys == 0) { continue; } if (nvp > 3) { for (;;) { int bestMergeVal = 0; int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0; for (int j = 0; j < npolys - 1; j++) { int pj = j * nvp; // polys[j*nvp] for (int k = j + 1; k < npolys; k++) { int pk = k * nvp; int ea = 0, eb = 0; int v = GetPolyMergeValue(polys, pj, pk, Verts, ref ea, ref eb, nvp); if (v > bestMergeVal) { bestMergeVal = v; bestPa = j; bestPb = k; bestEa = ea; bestEb = eb; } } } if (bestMergeVal > 0) { int pa = bestPa * nvp; int pb = bestPb * nvp; MergePolys(ref polys, pa, pb, bestEa, bestEb, tmpPoly, nvp); // fill in the hole at pb with the last poly Array.Copy(polys, (npolys - 1) * nvp, polys, pb, nvp); npolys--; } else { break; } } } for (int j = 0; j < npolys; j++) { int p = NPolys * nvp * 2; int q = j * nvp; for (int k = 0; k < nvp; k++) { Polys[p + k] = polys[q + k]; } Regs[NPolys] = cont.Reg; Areas[NPolys] = cont.Area; NPolys++; } } for (int i = 0; i < NVerts; i++) { //if (vflags[i] > 0) //{ // if (!CanRemoveVertex(i)) continue; // if (!RemoveVertex(i, maxTris)) // { // // error // } // for (int j = i; j < NVerts; j++) // { // vflags[j] = vflags[j + 1]; // } // i--; //} } if (!BuildMeshAdjacency(nvp)) { // error } // find portal edges if (BorderSize > 0) // defaults to 0 { int w = cset.Width; int h = cset.Height; for (int i = 0; i < NPolys; i++) { int p = i * 2 * nvp; for (int j = 0; j < nvp; j++) { if (Polys[p + j] == MeshNullIdx) { break; } if (Polys[p + nvp + j] != MeshNullIdx) { continue; } int nj = j + 1; if (nj >= nvp || Polys[p + nj] == MeshNullIdx) { nj = 0; } int va = Polys[p + j] * 3; int vb = Polys[p + nj] * 3; if (Verts[va + 0] == 0 && Verts[vb + 0] == 0) { Polys[p + nvp + j] = 0x8000 | 0; } else if (Verts[va + 2] == h && Verts[vb + 2] == h) { Polys[p + nvp + j] = 0x8000 | 1; } else if (Verts[va + 0] == w && Verts[vb + 0] == w) { Polys[p + nvp + j] = 0x8000 | 2; } else if (Verts[va + 2] == 0 && Verts[vb + 2] == 0) { Polys[p + nvp + j] = 0x8000 | 3; } } } } // user fills in this data Flags = new int[NPolys]; }
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; } } } } }