static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb, unsigned short* tmp, const int nvp) { const int na = countPolyVerts(pa, nvp); const int nb = countPolyVerts(pb, nvp); // Merge polygons. memset(tmp, 0xff, sizeof(unsigned short)*nvp); int n = 0; // Add pa for (int i = 0; i < na-1; ++i) tmp[n++] = pa[(ea+1+i) % na]; // Add pb for (int i = 0; i < nb-1; ++i) tmp[n++] = pb[(eb+1+i) % nb]; memcpy(pa, tmp, sizeof(unsigned short)*nvp); }
static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem, const int maxTris) { const int nvp = mesh.nvp; // Count number of polygons to remove. int numRemovedVerts = 0; for (int i = 0; i < mesh.npolys; ++i) { unsigned short* p = &mesh.polys[i*nvp*2]; const int nv = countPolyVerts(p, nvp); for (int j = 0; j < nv; ++j) { if (p[j] == rem) numRemovedVerts++; } } int nedges = 0; rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP); if (!edges) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4); return false; } int nhole = 0; rcScopedDelete<int> hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); if (!hole) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp); return false; } int nhreg = 0; rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); if (!hreg) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp); return false; } int nharea = 0; rcScopedDelete<int> harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); if (!harea) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp); return false; } for (int i = 0; i < mesh.npolys; ++i) { unsigned short* p = &mesh.polys[i*nvp*2]; const int nv = countPolyVerts(p, nvp); bool hasRem = false; for (int j = 0; j < nv; ++j) if (p[j] == rem) hasRem = true; if (hasRem) { // Collect edges which does not touch the removed vertex. for (int j = 0, k = nv-1; j < nv; k = j++) { if (p[j] != rem && p[k] != rem) { int* e = &edges[nedges*4]; e[0] = p[k]; e[1] = p[j]; e[2] = mesh.regs[i]; e[3] = mesh.areas[i]; nedges++; } } // Remove the polygon. unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2]; memcpy(p,p2,sizeof(unsigned short)*nvp); memset(p+nvp,0xff,sizeof(unsigned short)*nvp); mesh.regs[i] = mesh.regs[mesh.npolys-1]; mesh.areas[i] = mesh.areas[mesh.npolys-1]; mesh.npolys--; --i; } } // Remove vertex. for (int i = (int)rem; i < mesh.nverts; ++i) { mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0]; mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1]; mesh.verts[i*3+2] = mesh.verts[(i+1)*3+2]; } mesh.nverts--; // Adjust indices to match the removed vertex layout. for (int i = 0; i < mesh.npolys; ++i) { unsigned short* p = &mesh.polys[i*nvp*2]; const int nv = countPolyVerts(p, nvp); for (int j = 0; j < nv; ++j) if (p[j] > rem) p[j]--; } for (int i = 0; i < nedges; ++i) { if (edges[i*4+0] > rem) edges[i*4+0]--; if (edges[i*4+1] > rem) edges[i*4+1]--; } if (nedges == 0) return true; // Start with one vertex, keep appending connected // segments to the start and end of the hole. pushBack(edges[0], hole, nhole); pushBack(edges[2], hreg, nhreg); pushBack(edges[3], harea, nharea); while (nedges) { bool match = false; for (int i = 0; i < nedges; ++i) { const int ea = edges[i*4+0]; const int eb = edges[i*4+1]; const int r = edges[i*4+2]; const int a = edges[i*4+3]; bool add = false; if (hole[0] == eb) { // The segment matches the beginning of the hole boundary. pushFront(ea, hole, nhole); pushFront(r, hreg, nhreg); pushFront(a, harea, nharea); add = true; } else if (hole[nhole-1] == ea) { // The segment matches the end of the hole boundary. pushBack(eb, hole, nhole); pushBack(r, hreg, nhreg); pushBack(a, harea, nharea); add = true; } if (add) { // The edge segment was added, remove it. edges[i*4+0] = edges[(nedges-1)*4+0]; edges[i*4+1] = edges[(nedges-1)*4+1]; edges[i*4+2] = edges[(nedges-1)*4+2]; edges[i*4+3] = edges[(nedges-1)*4+3]; --nedges; match = true; --i; } } if (!match) break; } rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP); if (!tris) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3); return false; } rcScopedDelete<int> tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP); if (!tverts) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4); return false; } rcScopedDelete<int> thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP); if (!tverts) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole); return false; } // Generate temp vertex array for triangulation. for (int i = 0; i < nhole; ++i) { const int pi = hole[i]; tverts[i*4+0] = mesh.verts[pi*3+0]; tverts[i*4+1] = mesh.verts[pi*3+1]; tverts[i*4+2] = mesh.verts[pi*3+2]; tverts[i*4+3] = 0; thole[i] = i; } // Triangulate the hole. int ntris = triangulate(nhole, &tverts[0], &thole[0], tris); if (ntris < 0) { ntris = -ntris; ctx->log(RC_LOG_WARNING, "removeVertex: triangulate() returned bad results."); } // Merge the hole triangles back to polygons. rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP); if (!polys) { ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp); return false; } rcScopedDelete<unsigned short> pregs = (unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP); if (!pregs) { ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris); return false; } rcScopedDelete<unsigned char> pareas = (unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP); if (!pregs) { ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris); return false; } unsigned short* tmpPoly = &polys[ntris*nvp]; // Build initial polygons. int npolys = 0; memset(polys, 0xff, ntris*nvp*sizeof(unsigned short)); for (int j = 0; j < ntris; ++j) { int* t = &tris[j*3]; if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2]) { polys[npolys*nvp+0] = (unsigned short)hole[t[0]]; polys[npolys*nvp+1] = (unsigned short)hole[t[1]]; polys[npolys*nvp+2] = (unsigned short)hole[t[2]]; pregs[npolys] = (unsigned short)hreg[t[0]]; pareas[npolys] = (unsigned char)harea[t[0]]; npolys++; } } if (!npolys) return true; // 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) { unsigned short* pj = &polys[j*nvp]; for (int k = j+1; k < npolys; ++k) { unsigned short* pk = &polys[k*nvp]; int ea, eb; int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp); if (v > bestMergeVal) { bestMergeVal = v; bestPa = j; bestPb = k; bestEa = ea; bestEb = eb; } } } if (bestMergeVal > 0) { // Found best, merge. unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); pregs[bestPb] = pregs[npolys-1]; pareas[bestPb] = pareas[npolys-1]; npolys--; } else { // Could not merge any polygons, stop. break; } } } // Store polygons. for (int i = 0; i < npolys; ++i) { if (mesh.npolys >= maxTris) break; unsigned short* p = &mesh.polys[mesh.npolys*nvp*2]; memset(p,0xff,sizeof(unsigned short)*nvp*2); for (int j = 0; j < nvp; ++j) p[j] = polys[i*nvp+j]; mesh.regs[mesh.npolys] = pregs[i]; mesh.areas[mesh.npolys] = pareas[i]; mesh.npolys++; if (mesh.npolys > maxTris) { ctx->log(RC_LOG_ERROR, "removeVertex: Too many polygons %d (max:%d).", mesh.npolys, maxTris); return false; } } return true; }
static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem) { const int nvp = mesh.nvp; // Count number of polygons to remove. int numRemovedVerts = 0; int numTouchedVerts = 0; int numRemainingEdges = 0; for (int i = 0; i < mesh.npolys; ++i) { unsigned short* p = &mesh.polys[i*nvp*2]; const int nv = countPolyVerts(p, nvp); int numRemoved = 0; int numVerts = 0; for (int j = 0; j < nv; ++j) { if (p[j] == rem) { numTouchedVerts++; numRemoved++; } numVerts++; } if (numRemoved) { numRemovedVerts += numRemoved; numRemainingEdges += numVerts-(numRemoved+1); } } // There would be too few edges remaining to create a polygon. // This can happen for example when a tip of a triangle is marked // as deletion, but there are no other polys that share the vertex. // In this case, the vertex should not be removed. if (numRemainingEdges <= 2) return false; // Find edges which share the removed vertex. const int maxEdges = numTouchedVerts*2; int nedges = 0; rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP); if (!edges) { ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3); return false; } for (int i = 0; i < mesh.npolys; ++i) { unsigned short* p = &mesh.polys[i*nvp*2]; const int nv = countPolyVerts(p, nvp); // Collect edges which touches the removed vertex. for (int j = 0, k = nv-1; j < nv; k = j++) { if (p[j] == rem || p[k] == rem) { // Arrange edge so that a=rem. int a = p[j], b = p[k]; if (b == rem) rcSwap(a,b); // Check if the edge exists bool exists = false; for (int k = 0; k < nedges; ++k) { int* e = &edges[k*3]; if (e[1] == b) { // Exists, increment vertex share count. e[2]++; exists = true; } } // Add new edge. if (!exists) { int* e = &edges[nedges*3]; e[0] = a; e[1] = b; e[2] = 1; nedges++; } } } } // There should be no more than 2 open edges. // This catches the case that two non-adjacent polygons // share the removed vertex. In that case, do not remove the vertex. int numOpenEdges = 0; for (int i = 0; i < nedges; ++i) { if (edges[i*3+2] < 2) numOpenEdges++; } if (numOpenEdges > 2) return false; return true; }
static int getPolyMergeValue(unsigned short* pa, unsigned short* pb, const unsigned short* verts, int& ea, int& eb, const int nvp) { const int na = countPolyVerts(pa, nvp); const int nb = countPolyVerts(pb, nvp); // If the merged polygon would be too big, do not merge. if (na+nb-2 > nvp) return -1; // Check if the polygons share an edge. ea = -1; eb = -1; for (int i = 0; i < na; ++i) { unsigned short va0 = pa[i]; unsigned short va1 = pa[(i+1) % na]; if (va0 > va1) rcSwap(va0, va1); for (int j = 0; j < nb; ++j) { unsigned short vb0 = pb[j]; unsigned short vb1 = pb[(j+1) % nb]; if (vb0 > vb1) rcSwap(vb0, vb1); if (va0 == vb0 && va1 == vb1) { ea = i; eb = j; break; } } } // No common edge, cannot merge. if (ea == -1 || eb == -1) return -1; // Check to see if the merged polygon would be convex. unsigned short va, vb, vc; va = pa[(ea+na-1) % na]; vb = pa[ea]; vc = pb[(eb+2) % nb]; if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3])) return -1; va = pb[(eb+nb-1) % nb]; vb = pb[eb]; vc = pa[(ea+2) % na]; if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3])) return -1; va = pa[ea]; vb = pa[(ea+1)%na]; int dx = (int)verts[va*3+0] - (int)verts[vb*3+0]; int dy = (int)verts[va*3+2] - (int)verts[vb*3+2]; return dx*dx + dy*dy; }
static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int maxTris) { static const int nvp = mesh.nvp; int* edges = 0; int nedges = 0; int* hole = 0; int nhole = 0; int* hreg = 0; int nhreg = 0; int* tris = 0; int* tverts = 0; int* thole = 0; unsigned short* polys = 0; unsigned short* pregs = 0; int npolys = 0; // Count number of polygons to remove. int nrem = 0; for (int i = 0; i < mesh.npolys; ++i) { unsigned short* p = &mesh.polys[i*nvp*2]; for (int j = 0; j < nvp; ++j) if (p[j] == rem) { nrem++; break; } } edges = new int[nrem*nvp*3]; if (!edges) { if (rcGetLog()) rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", nrem*nvp*3); goto failure; } hole = new int[nrem*nvp]; if (!hole) { if (rcGetLog()) rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", nrem*nvp); goto failure; } hreg = new int[nrem*nvp]; if (!hreg) { if (rcGetLog()) rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", nrem*nvp); goto failure; } for (int i = 0; i < mesh.npolys; ++i) { unsigned short* p = &mesh.polys[i*nvp*2]; const int nv = countPolyVerts(p, nvp); bool hasRem = false; for (int j = 0; j < nv; ++j) if (p[j] == rem) hasRem = true; if (hasRem) { // Collect edges which does not touch the removed vertex. for (int j = 0, k = nv-1; j < nv; k = j++) { if (p[j] != rem && p[k] != rem) { int* e = &edges[nedges*3]; e[0] = p[k]; e[1] = p[j]; e[2] = mesh.regs[i]; nedges++; } } // Remove the polygon. unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2]; memcpy(p,p2,sizeof(unsigned short)*nvp); mesh.regs[i] = mesh.regs[mesh.npolys-1]; mesh.npolys--; --i; } } // Remove vertex. for (int i = (int)rem; i < mesh.nverts; ++i) { mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0]; mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1]; mesh.verts[i*3+2] = mesh.verts[(i+1)*3+2]; } mesh.nverts--; // Adjust indices to match the removed vertex layout. for (int i = 0; i < mesh.npolys; ++i) { unsigned short* p = &mesh.polys[i*nvp*2]; const int nv = countPolyVerts(p, nvp); for (int j = 0; j < nv; ++j) if (p[j] > rem) p[j]--; } for (int i = 0; i < nedges; ++i) { if (edges[i*3+0] > rem) edges[i*3+0]--; if (edges[i*3+1] > rem) edges[i*3+1]--; } if (nedges == 0) return true; hole[nhole] = edges[0]; hreg[nhole] = edges[2]; nhole++; while (nedges) { bool match = false; for (int i = 0; i < nedges; ++i) { const int ea = edges[i*3+0]; const int eb = edges[i*3+1]; const int r = edges[i*3+2]; bool add = false; if (hole[0] == eb) { pushFront(ea, hole, nhole); pushFront(r, hreg, nhreg); add = true; } else if (hole[nhole-1] == ea) { pushBack(eb, hole, nhole); pushBack(r, hreg, nhreg); add = true; } if (add) { // Remove edge. edges[i*3+0] = edges[(nedges-1)*3+0]; edges[i*3+1] = edges[(nedges-1)*3+1]; edges[i*3+2] = edges[(nedges-1)*3+2]; --nedges; match = true; --i; } } if (!match) break; } tris = new int[nhole*3]; if (!tris) { if (rcGetLog()) rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3); goto failure; } tverts = new int[nhole*4]; if (!tverts) { if (rcGetLog()) rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4); goto failure; } thole = new int[nhole]; if (!tverts) { if (rcGetLog()) rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole); goto failure; } // Generate temp vertex array for triangulation. for (int i = 0; i < nhole; ++i) { const int pi = hole[i]; tverts[i*4+0] = mesh.verts[pi*3+0]; tverts[i*4+1] = mesh.verts[pi*3+1]; tverts[i*4+2] = mesh.verts[pi*3+2]; tverts[i*4+3] = 0; thole[i] = i; } // Triangulate the hole. int ntris = triangulate(nhole, &tverts[0], &thole[0], tris); // Merge the hole triangles back to polygons. polys = new unsigned short[(ntris+1)*nvp]; if (!polys) { if (rcGetLog()) rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp); goto failure; } pregs = new unsigned short[ntris]; if (!pregs) { if (rcGetLog()) rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'pregs' (%d).", ntris); goto failure; } unsigned short* tmpPoly = &polys[ntris*nvp]; // Build initial polygons. memset(polys, 0xff, ntris*nvp*sizeof(unsigned short)); for (int j = 0; j < ntris; ++j) { int* t = &tris[j*3]; if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2]) { polys[npolys*nvp+0] = (unsigned short)hole[t[0]]; polys[npolys*nvp+1] = (unsigned short)hole[t[1]]; polys[npolys*nvp+2] = (unsigned short)hole[t[2]]; pregs[npolys] = hreg[t[0]]; npolys++; } } if (!npolys) return true; // Merge polygons. if (nvp > 3) { while (true) { // Find best polygons to merge. int bestMergeVal = 0; int bestPa, bestPb, bestEa, bestEb; for (int j = 0; j < npolys-1; ++j) { unsigned short* pj = &polys[j*nvp]; for (int k = j+1; k < npolys; ++k) { unsigned short* pk = &polys[k*nvp]; int ea, eb; int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp); if (v > bestMergeVal) { bestMergeVal = v; bestPa = j; bestPb = k; bestEa = ea; bestEb = eb; } } } if (bestMergeVal > 0) { // Found best, merge. unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; mergePolys(pa, pb, mesh.verts, bestEa, bestEb, tmpPoly, nvp); memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); pregs[bestPb] = pregs[npolys-1]; npolys--; } else { // Could not merge any polygons, stop. break; } } } // Store polygons. for (int i = 0; i < npolys; ++i) { if (mesh.npolys >= maxTris) break; unsigned short* p = &mesh.polys[mesh.npolys*nvp*2]; memset(p,0xff,sizeof(unsigned short)*nvp*2); for (int j = 0; j < nvp; ++j) p[j] = polys[i*nvp+j]; mesh.regs[mesh.npolys] = pregs[i]; mesh.npolys++; } delete [] edges; delete [] hole; delete [] hreg; delete [] tris; delete [] thole; delete [] tverts; delete [] polys; delete [] pregs; return true; failure: delete [] edges; delete [] hole; delete [] hreg; delete [] tris; delete [] thole; delete [] tverts; delete [] polys; delete [] pregs; return false; }