void CPUSideTriangleMesh::generateSmoothingGroups(float maxSmoothAngle, bool regenerateNormals, bool regenerateAdjacency) { if (regenerateNormals) { generateFaceNormals(); } if (regenerateAdjacency) { generateFaceAdjacency(); } // split faces float cSMAngle = cos(maxSmoothAngle); unsigned smGroup = 0; std::vector<unsigned> faceSMGroup(getNumFaces(), 0); //std::vector<unsigned> smGroupRepresentative(1); for (unsigned iFace = 0; iFace < getNumFaces(); ++iFace) { // sm group for face already set if (faceSMGroup[iFace] != 0) { continue; } // make new smoothing group faceSMGroup[iFace] = ++smGroup; //smGroupRepresentative.push_back(iFace); // run DFS to gather faces belonging to same smoothing group std::stack<unsigned> toVisit; toVisit.push(iFace); while ( !toVisit.empty() ) { unsigned curFace = toVisit.top(); toVisit.pop(); const unsigned* adjFaces = getAdjacentFaces(curFace); for (unsigned i = 0; i<getNumAdjacentFaces(curFace); ++i) { // if angle between normals is less than maxSmoothAngle put adjacent face into same smoothing group unsigned adjFace = adjFaces[i]; if ( faceSMGroup[adjFace] == 0 && math::dot(faceNormals[curFace], faceNormals[adjFace]) > cSMAngle ) { faceSMGroup[adjFace] = smGroup; toVisit.push(adjFace); } } } } // once smoothing groups are set try to reduce their quantity to 32 using greedy algorithm smoothingGroups.resize( getNumFaces() ); for (size_t i = 0; i<getNumFaces(); ++i) { smoothingGroups[i] = faceSMGroup[i] % 32; } }
// Returns the boundary edge that is not e. Vertex WingedEdge::getOtherBoundaryVertice(Vertex &a, Edge &forbidden_edge) { std::set<Edge> edges = vertexList[a]; for(Edge e : edges) { if(e != forbidden_edge && getNumAdjacentFaces(e) == 1) { return getOtherVertex(e, a); } } //throw RuntimeError("No other boundary edge was found. Something might be wrong with your mesh."); return a; }
// Subdivides only the edges on the boundary. WingedEdge WingedEdge::BoundaryTrianglularSubdivide(float min_len) { WingedEdge mesh;// = *this; for (auto face_iter = faceList.begin(); face_iter != faceList.end(); ++face_iter) { const Face face = face_iter -> first; /* massive assumption that there is 3 edges in our face */ Edge e1 = face.E1(); Edge e2 = face.E2(); Edge e3 = face.E3(); // Compute the vertices opposite the cooresponding indiced edges. bool success = true; Vertex v1 = GetAdjacentVertex(face, e1, success); Vertex v2 = GetAdjacentVertex(face, e2, success); Vertex v3 = GetAdjacentVertex(face, e3, success); if(!success) { throw new RuntimeError("Error : Winged Edge topology is malformed!"); } // -- Compute boundary predicates that answer whether or not an edge should be divided. bool b1, b2, b3; b1 = getNumAdjacentFaces(e1) == 1; b2 = getNumAdjacentFaces(e2) == 1; b3 = getNumAdjacentFaces(e3) == 1; // Non Boundary --> do not subdivide the face. if(!(b1 || b2 || b3)) { e1 = mesh.AddEdge(e1); e2 = mesh.AddEdge(e2); e2 = mesh.AddEdge(e2); mesh.AddFace(e1, e2, e3); continue; } // FIXME : Only compute butterfly midpoints, if the original predicate is true. // Compute interpolated midpoints. Vertex mid_b1, mid_b2, mid_b3; mid_b1 = SubdivideEdge(face, e1, v1, false); mid_b2 = SubdivideEdge(face, e2, v2, false); mid_b3 = SubdivideEdge(face, e3, v3, false); // Bound the change in midpoint. if(min_len > 0) { // Compute linear mid points. Vertex mid_l1, mid_l2, mid_l3; mid_l1 = SubdivideEdge(face, e1, v1, true); mid_l2 = SubdivideEdge(face, e2, v2, true); mid_l3 = SubdivideEdge(face, e3, v3, true); float sqr_len_min = min_len > 1 ? min_len*min_len : min_len; b1 = b1 && computeSqrOffset(mid_b1, mid_l1) > sqr_len_min; b2 = b2 && computeSqrOffset(mid_b2, mid_l2) > sqr_len_min; b3 = b3 && computeSqrOffset(mid_b3, mid_l3) > sqr_len_min; } // -- Count the number of edges that are on the boundary. int boundary_count = 0; boundary_count = b1 ? boundary_count + 1 : boundary_count; boundary_count = b2 ? boundary_count + 1 : boundary_count; boundary_count = b3 ? boundary_count + 1 : boundary_count; // Non Boundary --> do not subdivide the face. if(boundary_count == 0) { e1 = mesh.AddEdge(e1); e2 = mesh.AddEdge(e2); e2 = mesh.AddEdge(e2); mesh.AddFace(e1, e2, e3); continue; } // 1 boundary --> subdivide into 2 vertices. if(boundary_count == 1) { Vertex v_new, v_old1, v_old2, v_old3; if(b1) { v_new = mid_b1; v_old1 = v1; v_old2 = v2; v_old3 = v3; } else if(b2) { v_new = mid_b2; v_old1 = v2; v_old2 = v3; v_old3 = v1; } else { v_new = mid_b3; v_old1 = v3; v_old2 = v1; v_old3 = v2; } // face1 e1 = mesh.AddEdge(v_new, v_old1); e2 = mesh.AddEdge(v_new, v_old2); e3 = mesh.AddEdge(v_old1, v_old2); mesh.AddFace(e1, e2, e3); // Face 2. e2 = mesh.AddEdge(v_new, v_old3); e3 = mesh.AddEdge(v_old1, v_old3); mesh.AddFace(e1, e2, e3); continue; } // 2 - 3 boundaries. Perform the full 4 face triangulation. if(boundary_count == 3) { performTriangulation(mesh, v1, v2, v3, mid_b1, mid_b2, mid_b3); continue; } if(boundary_count > 3) { throw new RuntimeError("Face has more than 3 edges. This is not a triangle!"); } // -- 2 boundary case. Subdivide into 3 triangles. Vertex v_new1, v_new2, v_old1, v_old2, v_old3; if(!b1) { v_old1 = v1; v_old2 = v2; v_old3 = v3; v_new1 = mid_b2; v_new2 = mid_b3; } else if(!b2) { v_old1 = v2; v_old2 = v3; v_old3 = v1; v_new1 = mid_b3; v_new2 = mid_b1; } else { v_old1 = v3; v_old2 = v1; v_old3 = v2; v_new1 = mid_b1; v_new2 = mid_b2; } // Boundary triangles. e1 = mesh.AddEdge(v_old1, v_new1); e2 = mesh.AddEdge(v_old1, v_new2); e3 = mesh.AddEdge(v_new1, v_new2); mesh.AddFace(e1, e2, e3); e1 = mesh.AddEdge(v_old3, v_new1); e2 = mesh.AddEdge(v_old3, v_new2); e3 = mesh.AddEdge(v_new1, v_new2); mesh.AddFace(e1, e2, e3); // Constant edge triangle. e1 = mesh.AddEdge(v_old2, v_new2); e2 = mesh.AddEdge(v_old3, v_new2); e3 = mesh.AddEdge(v_old3, v_old2); mesh.AddFace(e1, e2, e3); } return mesh; }
// Private work function. WingedEdge WingedEdge::Subdivide(bool linear, bool pascal, std::map<Vertex, std::vector<Vertex> > &derivations) { WingedEdge mesh; std::set<Edge> edges; for (auto face = faceList.begin(); face != faceList.end(); ++face) { /* massive assumption that there is 3 edges in our face */ Edge e1 = face -> first.E1(); Edge e2 = face -> first.E2(); Edge e3 = face -> first.E3(); /* might need to verify this doesn't pick duplicates */ Vertex v1 = e1.V1(); Vertex v2 = e1.V2(); Vertex v3 = (e2.V1() == v1 || e2.V1() == v2) ? e2.V2() : e2.V1(); /* guarantee we know what e2 is */ if (v1 == e3.V1() || v1 == e3.V2()) { Edge tmp = e3; e3 = e2; e2 = tmp; } int f1, f2, f3; f1 = getNumAdjacentFaces(e1); f2 = getNumAdjacentFaces(e2); f3 = getNumAdjacentFaces(e3); // Do not subdivide and do not incorporate non boundary faces. // This is the part the creates the pascal behavior, if(pascal && f1 == 2 && f2 == 2 && f3 == 2) { continue; } bool success = true; Vertex v4 = SubdivideEdge(face->first, e1, GetAdjacentVertex(face->first, e1, success), linear, derivations); Vertex v5 = SubdivideEdge(face->first, e2, GetAdjacentVertex(face->first, e2, success), linear, derivations); Vertex v6 = SubdivideEdge(face->first, e3, GetAdjacentVertex(face->first, e3, success), linear, derivations); // A half hearted success check. if(!success) { throw RuntimeError("WindgedEdge Error: Something is wrong with the mesh to be subdivided."); } { e1 = mesh.AddEdge(v1, v4); e2 = mesh.AddEdge(v1, v5); e3 = mesh.AddEdge(v5, v4); mesh.AddFace(e1, e2, e3); } { e1 = mesh.AddEdge(v4, v2); e2 = mesh.AddEdge(v4, v6); e3 = mesh.AddEdge(v6, v2); mesh.AddFace(e1, e2, e3); } { e1 = mesh.AddEdge(v5, v6); e2 = mesh.AddEdge(v5, v3); e3 = mesh.AddEdge(v3, v6); mesh.AddFace(e1, e2, e3); } { e1 = mesh.AddEdge(v6, v5); e2 = mesh.AddEdge(v6, v4); e3 = mesh.AddEdge(v4, v5); mesh.AddFace(e1, e2, e3); } } /* std::cout << "Subdivide info: " << std::endl; std::cout << "VertexList: " << mesh.NumVertices() << std::endl; std::cout << "EdgeList: " << mesh.NumEdges() << std::endl; std::cout << "FaceList: " << mesh.NumFaces() << std::endl; */ return mesh; }