bool Chart::closeHoles() { Array<HalfEdge::Edge *> boundaryEdges; getBoundaryEdges(m_unifiedMesh.ptr(), boundaryEdges); uint boundaryCount = boundaryEdges.count(); if (boundaryCount <= 1) { // Nothing to close. return true; } // Compute lengths and areas. Array<float> boundaryLengths; //Array<Vector3> boundaryCentroids; for (uint i = 0; i < boundaryCount; i++) { const HalfEdge::Edge * startEdge = boundaryEdges[i]; nvCheck(startEdge->face == NULL); //float boundaryEdgeCount = 0; float boundaryLength = 0.0f; //Vector3 boundaryCentroid(zero); const HalfEdge::Edge * edge = startEdge; do { Vector3 t0 = edge->from()->pos; Vector3 t1 = edge->to()->pos; //boundaryEdgeCount++; boundaryLength += length(t1 - t0); //boundaryCentroid += edge->vertex()->pos; edge = edge->next; } while(edge != startEdge); boundaryLengths.append(boundaryLength); //boundaryCentroids.append(boundaryCentroid / boundaryEdgeCount); } // Find disk boundary. uint diskBoundary = 0; float maxLength = boundaryLengths[0]; for (uint i = 1; i < boundaryCount; i++) { if (boundaryLengths[i] > maxLength) { maxLength = boundaryLengths[i]; diskBoundary = i; } } // Sew holes. /*for (uint i = 0; i < boundaryCount; i++) { if (diskBoundary == i) { // Skip disk boundary. continue; } HalfEdge::Edge * startEdge = boundaryEdges[i]; nvCheck(startEdge->face() == NULL); boundaryEdges[i] = m_unifiedMesh->sewBoundary(startEdge); } exportMesh(m_unifiedMesh.ptr(), "debug_sewn.obj");*/ //bool hasNewHoles = false; // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // @@ Close loop is wrong, after closing a loop, we do not only have to add the face, but make sure that every edge in he loop is pointing to the right place. // Close holes. for (uint i = 0; i < boundaryCount; i++) { if (diskBoundary == i) { // Skip disk boundary. continue; } HalfEdge::Edge * startEdge = boundaryEdges[i]; nvDebugCheck(startEdge != NULL); nvDebugCheck(startEdge->face == NULL); #if 1 Array<HalfEdge::Vertex *> vertexLoop; Array<HalfEdge::Edge *> edgeLoop; HalfEdge::Edge * edge = startEdge; do { HalfEdge::Vertex * vertex = edge->next->vertex; // edge->to() uint i; for (i = 0; i < vertexLoop.count(); i++) { if (vertex->isColocal(vertexLoop[i])) { break; } } bool isCrossing = (i != vertexLoop.count()); if (isCrossing) { HalfEdge::Edge * prev = edgeLoop[i]; // Previous edge before the loop. HalfEdge::Edge * next = edge->next; // Next edge after the loop. nvDebugCheck(prev->to()->isColocal(next->from())); // Close loop. edgeLoop.append(edge); closeLoop(i+1, edgeLoop); // Link boundary loop. prev->setNext(next); vertex->setEdge(next); // Start over again. vertexLoop.clear(); edgeLoop.clear(); edge = startEdge; vertex = edge->to(); } vertexLoop.append(vertex); edgeLoop.append(edge); edge = edge->next; } while(edge != startEdge); closeLoop(0, edgeLoop); #endif /* // Add face and connect boundary edges. HalfEdge::Face * face = m_unifiedMesh->addFace(); face->setEdge(startEdge); HalfEdge::Edge * edge = startEdge; do { edge->setFace(face); edge = edge->next(); } while(edge != startEdge); */ /* uint edgeCount = 0; HalfEdge::Edge * edge = startEdge; do { edgeCount++; edge = edge->next(); } while(edge != startEdge); // Count edges in this boundary. uint edgeCount = 0; HalfEdge::Edge * edge = startEdge; do { edgeCount++; edge = edge->next(); } while(edge != startEdge); // Trivial hole, fill with one triangle. This actually works for all convex boundaries with non colinear vertices. if (edgeCount == 3) { // Add face and connect boundary edges. HalfEdge::Face * face = m_unifiedMesh->addFace(); face->setEdge(startEdge); edge = startEdge; do { edge->setFace(face); edge = edge->next(); } while(edge != startEdge); // @@ Implement the above using addFace, it should now work with existing edges, as long as their face pointers is zero. } else { // Ideally we should: // - compute best fit plane of boundary vertices. // - project boundary polygon onto plane. // - triangulate boundary polygon. // - add faces of the resulting triangulation. // I don't have a good triangulator available. A more simple solution that works in more (but not all) cases: // - compute boundary centroid. // - add vertex centroid. // - connect centroid vertex with boundary vertices. // - connect radial edges with boundary edges. // This should work for non-convex boundaries with colinear vertices as long as the kernel of the polygon is not empty. // Compute boundary centroid: Vector3 centroid_pos(0); Vector2 centroid_tex(0); HalfEdge::Edge * edge = startEdge; do { centroid_pos += edge->vertex()->pos; centroid_tex += edge->vertex()->tex; edge = edge->next(); } while(edge != startEdge); centroid_pos *= (1.0f / edgeCount); centroid_tex *= (1.0f / edgeCount); HalfEdge::Vertex * centroid = m_unifiedMesh->addVertex(centroid_pos); centroid->tex = centroid_tex; // Add one pair of edges for each boundary vertex. edge = startEdge; do { HalfEdge::Edge * next = edge->next(); nvCheck(edge->face() == NULL); HalfEdge::Face * face = m_unifiedMesh->addFace(centroid->id(), edge->from()->id(), edge->to()->id()); if (face != NULL) { nvCheck(edge->face() == face); } else { hasNewHoles = true; } edge = next; } while(edge != startEdge); } */ } /*nvDebugCheck(!hasNewHoles); if (hasNewHoles) { // Link boundary again, in case closeHoles created new holes! m_unifiedMesh->linkBoundary(); }*/ // Because some algorithms do not expect sparse edge buffers. //m_unifiedMesh->compactEdges(); // In case we messed up: //m_unifiedMesh->linkBoundary(); getBoundaryEdges(m_unifiedMesh.ptr(), boundaryEdges); boundaryCount = boundaryEdges.count(); nvDebugCheck(boundaryCount == 1); //exportMesh(m_unifiedMesh.ptr(), "debug_hole_filled.obj"); return boundaryCount == 1; }