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; }
/// @see rcAllocPolyMesh, rcPolyMesh public static PolyMesh mergePolyMeshes(Context ctx, PolyMesh[] meshes, int nmeshes) { if (nmeshes == 0 || meshes == null) return null; ctx.startTimer("MERGE_POLYMESH"); PolyMesh mesh = new PolyMesh(); mesh.nvp = meshes[0].nvp; mesh.cs = meshes[0].cs; mesh.ch = meshes[0].ch; RecastVectors.copy(mesh.bmin, meshes[0].bmin, 0); RecastVectors.copy(mesh.bmax, meshes[0].bmax, 0); int maxVerts = 0; int maxPolys = 0; int maxVertsPerMesh = 0; for (int i = 0; i < nmeshes; ++i) { RecastVectors.min(mesh.bmin, meshes[i].bmin, 0); RecastVectors.max(mesh.bmax, meshes[i].bmax, 0); maxVertsPerMesh = Math.max(maxVertsPerMesh, meshes[i].nverts); maxVerts += meshes[i].nverts; maxPolys += meshes[i].npolys; } mesh.nverts = 0; mesh.verts = new int[maxVerts * 3]; mesh.npolys = 0; mesh.polys = new int[maxPolys * 2 * mesh.nvp]; Arrays.fill(mesh.polys, 0, mesh.polys.length, RC_MESH_NULL_IDX); mesh.regs = new int[maxPolys]; mesh.areas = new int[maxPolys]; mesh.flags = new int[maxPolys]; int[] nextVert = new int[maxVerts]; int[] firstVert = new int[VERTEX_BUCKET_COUNT]; for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) firstVert[i] = -1; int[] vremap = new int[maxVertsPerMesh]; for (int i = 0; i < nmeshes; ++i) { PolyMesh pmesh = meshes[i]; int ox = (int) Math.floor((pmesh.bmin[0] - mesh.bmin[0]) / mesh.cs + 0.5f); int oz = (int) Math.floor((pmesh.bmin[2] - mesh.bmin[2]) / mesh.cs + 0.5f); boolean isMinX = (ox == 0); boolean isMinZ = (oz == 0); boolean isMaxX = (Math.floor((mesh.bmax[0] - pmesh.bmax[0]) / mesh.cs + 0.5f)) == 0; boolean isMaxZ = (Math.floor((mesh.bmax[2] - pmesh.bmax[2]) / mesh.cs + 0.5f)) == 0; boolean isOnBorder = (isMinX || isMinZ || isMaxX || isMaxZ); for (int j = 0; j < pmesh.nverts; ++j) { int v = j * 3; int[] inv = addVertex( pmesh.verts[v + 0] + ox, pmesh.verts[v + 1], pmesh.verts[v + 2] + oz, mesh.verts, firstVert, nextVert, mesh.nverts); vremap[j] = inv[0]; mesh.nverts = inv[1]; } for (int j = 0; j < pmesh.npolys; ++j) { int tgt = mesh.npolys * 2 * mesh.nvp; int src = j * 2 * mesh.nvp; mesh.regs[mesh.npolys] = pmesh.regs[j]; mesh.areas[mesh.npolys] = pmesh.areas[j]; mesh.flags[mesh.npolys] = pmesh.flags[j]; mesh.npolys++; for (int k = 0; k < mesh.nvp; ++k) { if (pmesh.polys[src + k] == RC_MESH_NULL_IDX) break; mesh.polys[tgt + k] = vremap[pmesh.polys[src + k]]; } if (isOnBorder) { for (int k = mesh.nvp; k < mesh.nvp * 2; ++k) { if ((pmesh.polys[src + k] & 0x8000) != 0 && pmesh.polys[src + k] != 0xffff) { int dir = pmesh.polys[src + k] & 0xf; switch (dir) { case 0: // Portal x- if (isMinX) mesh.polys[tgt + k] = pmesh.polys[src + k]; break; case 1: // Portal z+ if (isMaxZ) mesh.polys[tgt + k] = pmesh.polys[src + k]; break; case 2: // Portal x+ if (isMaxX) mesh.polys[tgt + k] = pmesh.polys[src + k]; break; case 3: // Portal z- if (isMinZ) mesh.polys[tgt + k] = pmesh.polys[src + k]; break; } } } } } } // Calculate adjacency. buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp); 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("MERGE_POLYMESH"); return mesh; }