public static PolyMesh copyPolyMesh(Context ctx, PolyMesh src) { PolyMesh dst = new PolyMesh(); dst.nverts = src.nverts; dst.npolys = src.npolys; dst.maxpolys = src.npolys; dst.nvp = src.nvp; RecastVectors.copy(dst.bmin, src.bmin, 0); RecastVectors.copy(dst.bmax, src.bmax, 0); dst.cs = src.cs; dst.ch = src.ch; dst.borderSize = src.borderSize; dst.verts = new int[src.nverts * 3]; System.arraycopy(src.verts, 0, dst.verts, 0, dst.verts.length); dst.polys = new int[src.npolys * 2 * src.nvp]; System.arraycopy(src.polys, 0, dst.polys, 0, dst.polys.length); dst.regs = new int[src.npolys]; System.arraycopy(src.regs, 0, dst.regs, 0, dst.regs.length); dst.areas = new int[src.npolys]; System.arraycopy(src.areas, 0, dst.areas, 0, dst.areas.length); dst.flags = new int[src.npolys]; System.arraycopy(src.flags, 0, dst.flags, 0, dst.flags.length); return dst; }
/// @par /// /// @note If the mesh data is to be used to construct a Detour navigation mesh, then the upper /// limit must be retricted to <= #DT_VERTS_PER_POLYGON. /// /// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig public static PolyMesh buildPolyMesh(Context ctx, ContourSet cset, int nvp) { ctx.startTimer("BUILD_POLYMESH"); PolyMesh mesh = new PolyMesh(); RecastVectors.copy(mesh.bmin, cset.bmin, 0); RecastVectors.copy(mesh.bmax, cset.bmax, 0); mesh.cs = cset.cs; mesh.ch = cset.ch; mesh.borderSize = cset.borderSize; int maxVertices = 0; int maxTris = 0; int maxVertsPerCont = 0; for (int i = 0; i < cset.conts.size(); ++i) { // Skip null contours. if (cset.conts.get(i).nverts < 3) continue; maxVertices += cset.conts.get(i).nverts; maxTris += cset.conts.get(i).nverts - 2; maxVertsPerCont = Math.max(maxVertsPerCont, cset.conts.get(i).nverts); } if (maxVertices >= 0xfffe) { throw new RuntimeException("rcBuildPolyMesh: Too many vertices " + maxVertices); } int[] vflags = new int[maxVertices]; mesh.verts = new int[maxVertices * 3]; mesh.polys = new int[maxTris * nvp * 2]; Arrays.fill(mesh.polys, RC_MESH_NULL_IDX); mesh.regs = new int[maxTris]; mesh.areas = new int[maxTris]; mesh.nverts = 0; mesh.npolys = 0; mesh.nvp = nvp; mesh.maxpolys = maxTris; int[] nextVert = new int[maxVertices]; int[] firstVert = new int[VERTEX_BUCKET_COUNT]; for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) firstVert[i] = -1; int[] indices = new int[maxVertsPerCont]; int[] tris = new int[maxVertsPerCont * 3]; int[] polys = new int[(maxVertsPerCont + 1) * nvp]; int tmpPoly = maxVertsPerCont * nvp; for (int i = 0; i < cset.conts.size(); ++i) { Contour cont = cset.conts.get(i); // Skip null contours. if (cont.nverts < 3) continue; // Triangulate contour for (int j = 0; j < cont.nverts; ++j) indices[j] = j; int ntris = triangulate(cont.nverts, cont.verts, indices, tris); if (ntris <= 0) { // Bad triangulation, should not happen. ctx.warn("buildPolyMesh: Bad triangulation Contour " + i + "."); ntris = -ntris; } // Add and merge vertices. for (int j = 0; j < cont.nverts; ++j) { int v = j * 4; int[] inv = addVertex( cont.verts[v + 0], cont.verts[v + 1], cont.verts[v + 2], mesh.verts, firstVert, nextVert, mesh.nverts); indices[j] = inv[0]; mesh.nverts = inv[1]; if ((cont.verts[v + 3] & RC_BORDER_VERTEX) != 0) { // This vertex should be removed. vflags[indices[j]] = 1; } } // Build initial polygons. int npolys = 0; Arrays.fill(polys, RC_MESH_NULL_IDX); 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; // Merge polygons. if (nvp > 3) { for (; ; ) { // Find best polygons to merge. int bestMergeVal = 0; int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0; for (int j = 0; j < npolys - 1; ++j) { int pj = j * nvp; for (int k = j + 1; k < npolys; ++k) { int pk = k * nvp; int[] veaeb = getPolyMergeValue(polys, pj, pk, mesh.verts, nvp); int v = veaeb[0]; int ea = veaeb[1]; int eb = veaeb[2]; if (v > bestMergeVal) { bestMergeVal = v; bestPa = j; bestPb = k; bestEa = ea; bestEb = eb; } } } if (bestMergeVal > 0) { // Found best, merge. int pa = bestPa * nvp; int pb = bestPb * nvp; mergePolys(polys, pa, pb, bestEa, bestEb, tmpPoly, nvp); int lastPoly = (npolys - 1) * nvp; if (pb != lastPoly) { System.arraycopy(polys, lastPoly, polys, pb, nvp); } npolys--; } else { // Could not merge any polygons, stop. break; } } } // Store polygons. for (int j = 0; j < npolys; ++j) { int p = mesh.npolys * nvp * 2; int q = j * nvp; for (int k = 0; k < nvp; ++k) mesh.polys[p + k] = polys[q + k]; mesh.regs[mesh.npolys] = cont.reg; mesh.areas[mesh.npolys] = cont.area; mesh.npolys++; if (mesh.npolys > maxTris) { throw new RuntimeException( "rcBuildPolyMesh: Too many polygons " + mesh.npolys + " (max:" + maxTris + ")."); } } } // Remove edge vertices. for (int i = 0; i < mesh.nverts; ++i) { if (vflags[i] != 0) { if (!canRemoveVertex(ctx, mesh, i)) continue; removeVertex(ctx, mesh, i, maxTris); // Remove vertex // Note: mesh.nverts is already decremented inside removeVertex()! // Fixup vertex flags for (int j = i; j < mesh.nverts; ++j) vflags[j] = vflags[j + 1]; --i; } } // Calculate adjacency. buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp); // Find portal edges if (mesh.borderSize > 0) { int w = cset.width; int h = cset.height; for (int i = 0; i < mesh.npolys; ++i) { int p = i * 2 * nvp; for (int j = 0; j < nvp; ++j) { if (mesh.polys[p + j] == RC_MESH_NULL_IDX) break; // Skip connected edges. if (mesh.polys[p + nvp + j] != RC_MESH_NULL_IDX) continue; int nj = j + 1; if (nj >= nvp || mesh.polys[p + nj] == RC_MESH_NULL_IDX) nj = 0; int va = mesh.polys[p + j] * 3; int vb = mesh.polys[p + nj] * 3; if (mesh.verts[va + 0] == 0 && mesh.verts[vb + 0] == 0) mesh.polys[p + nvp + j] = 0x8000 | 0; else if (mesh.verts[va + 2] == h && mesh.verts[vb + 2] == h) mesh.polys[p + nvp + j] = 0x8000 | 1; else if (mesh.verts[va + 0] == w && mesh.verts[vb + 0] == w) mesh.polys[p + nvp + j] = 0x8000 | 2; else if (mesh.verts[va + 2] == 0 && mesh.verts[vb + 2] == 0) mesh.polys[p + nvp + j] = 0x8000 | 3; } } } // Just allocate the mesh flags array. The user is resposible to fill it. mesh.flags = new int[mesh.npolys]; if (mesh.nverts > 0xffff) { throw new RuntimeException( "rcBuildPolyMesh: The resulting mesh has too many vertices " + mesh.nverts + " (max " + 0xffff + "). Data can be corrupted."); } if (mesh.npolys > 0xffff) { throw new RuntimeException( "rcBuildPolyMesh: The resulting mesh has too many polygons " + mesh.npolys + " (max " + 0xffff + "). Data can be corrupted."); } ctx.stopTimer("BUILD_POLYMESH"); return mesh; }