tensor<double, 3, 2> getTangent(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<3> faceID) { auto cover = mesh.get_name(faceID); auto vertexID = mesh.get_simplex_up({cover[0]}); std::set<SurfaceMesh::KeyType> next(std::begin(cover)+1, std::end(cover)); return surfacemesh_detail::getTangentF(mesh, (*vertexID).position, vertexID, next); }
Vector getNormal(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<3> faceID) { Vector norm; auto name = mesh.get_name(faceID); // Get the three vertices auto a = *mesh.get_simplex_up({name[0]}); auto b = *mesh.get_simplex_up({name[1]}); auto c = *mesh.get_simplex_up({name[2]}); if ((*faceID).orientation == 1) { norm = cross(c-b, a-b); } else if ((*faceID).orientation == -1) { norm = cross(a-b, c-b); } else { // std::cerr << "ERROR(getNormal): Orientation undefined, cannot compute " // << "normal. Did you call compute_orientation()?" << std::endl; throw std::runtime_error("ERROR(getNormal): Orientation undefined, cannot compute normal. Did you call compute_orientation()?"); } return norm; }
int main(int argc, char** argv) { QApplication application(argc,argv); if(argc!=2){ cout << "One argument necessary, given: " << (argc-1) << endl; return EXIT_FAILURE; } SurfaceMesh mesh; bool ok = mesh.read(argv[1]); if(!ok){ cout << "could not open file: " << argv[1] << endl; return EXIT_FAILURE; } ///--- Preprocess mesh.triangulate(); mesh.update_vertex_normals(); ///--- Shutdown on close GUI QObject::connect(&application, &QApplication::lastWindowClosed, &application, &QApplication::quit); Viewer viewer(mesh); viewer.setWindowTitle("OpenGP/apps/qglviewer"); viewer.show(); return application.exec(); }
double getArea(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<3> faceID) { auto name = mesh.get_name(faceID); auto a = *mesh.get_simplex_up({name[0]}); auto b = *mesh.get_simplex_up({name[1]}); auto c = *mesh.get_simplex_up({name[2]}); return getArea(a, b, c); }
Vector getNormal(const SurfaceMesh &mesh, SurfaceMesh::SimplexID<1> vertexID) { Vector norm; auto faces = mesh.up(mesh.up(vertexID)); for (auto faceID : faces) { norm += getNormal(mesh, faceID); } // norm /= faces.size(); return norm; }
void coarse(SurfaceMesh &mesh, double coarseRate, double flatRate, double denseWeight){ // TODO: Check if all polygons are closed (0) std::cout << "Before coarsening: " << mesh.size<1>() << " " << mesh.size<2>() << " " << mesh.size<3>() << std::endl; // Compute the average edge length double avgLen = 0; if (denseWeight > 0){ avgLen = surfacemesh_detail::getMeanEdgeLength(mesh); } double sparsenessRatio = 1; double flatnessRatio = 1; // Construct list of vertices to decimate std::vector<SurfaceMesh::SimplexID<1> > toRemove; for(auto vertexID : mesh.get_level_id<1>()){ // Sparseness as coarsening criteria if(denseWeight > 0){ // Get max length of edges. auto edges = mesh.up(vertexID); double maxLen = 0; for(auto edgeID : edges){ auto name = mesh.get_name(edgeID); auto v = *mesh.get_simplex_down(edgeID, name[0]) - *mesh.get_simplex_down(edgeID, name[1]); double tmpLen = std::sqrt(v|v); if(tmpLen > maxLen) maxLen = tmpLen; } sparsenessRatio = std::pow(maxLen/avgLen, denseWeight); } // Curvature as coarsening criteria if(flatRate > 0){ auto lst = surfacemesh_detail::computeLocalStructureTensor(mesh, vertexID, RINGS); auto eigenvalues = surfacemesh_detail::getEigenvalues(lst).eigenvalues(); // The closer this ratio is to 0 the flatter the local region. flatnessRatio = std::pow(eigenvalues[1]/eigenvalues[2], flatRate); } // Add vertex to delete list if(sparsenessRatio * flatnessRatio < coarseRate){ toRemove.push_back(vertexID); } } std::cout << toRemove.size() << " vertices are marked to be removed." << std::endl; for(auto vertexID : toRemove){ surfacemesh_detail::decimateVertex(mesh, vertexID); } std::cout << "After coarsening: " << mesh.size<1>() << " " << mesh.size<2>() << " " << mesh.size<3>() << std::endl; }
/** * @brief Refine the mesh by quadrisection. * * Note that this function will delete all stored data on edges and faces. But * this can be easily fixed. * * @param mesh The mesh */ std::unique_ptr<SurfaceMesh> refineMesh(const SurfaceMesh &mesh){ std::unique_ptr<SurfaceMesh> refinedMesh(new SurfaceMesh); // Copy over vertices to refinedMesh for (auto vertex : mesh.get_level_id<1>()){ auto key = mesh.get_name(vertex); refinedMesh->insert(key, *vertex); } // Split edges and generate a map of names before to after std::map<std::array<int,2>, int> edgeMap; for(auto edge : mesh.get_level_id<2>()){ auto edgeName = mesh.get_name(edge); auto v1 = *mesh.get_simplex_up({edgeName[0]}); auto v2 = *mesh.get_simplex_up({edgeName[1]}); auto newVertex = refinedMesh->add_vertex(Vertex(0.5*(v1+v2))); edgeMap.emplace(std::make_pair(edgeName, newVertex)); } // Connect edges and face and copy data for(auto face : mesh.get_level_id<3>()){ auto name = mesh.get_name(face); int a, b, c; // Skip checking if found auto it = edgeMap.find({name[0],name[1]}); a = it->second; it = edgeMap.find({name[1],name[2]}); b = it->second; it = edgeMap.find({name[0],name[2]}); c = it->second; refinedMesh->insert({name[0], a}); refinedMesh->insert({a, name[1]}); refinedMesh->insert({name[1], b}); refinedMesh->insert({b, name[2]}); refinedMesh->insert({name[0], c}); refinedMesh->insert({c, name[2]}); refinedMesh->insert({a,b,c}); refinedMesh->insert({name[0],a,c}, *face); refinedMesh->insert({name[1],a,b}, *face); refinedMesh->insert({name[2],b,c}, *face); } return refinedMesh; }
/** * http://research.microsoft.com/en-us/um/people/chazhang/publications/icip01_ChaZhang.pdf * http://chenlab.ece.cornell.edu/Publication/Cha/icip01_Cha.pdf */ double getVolume(const SurfaceMesh &mesh) { bool orientError = false; double volume = 0; for (auto faceID : mesh.get_level_id<3>()) { auto name = mesh.get_name(faceID); auto a = (*mesh.get_simplex_up({name[0]})).position; auto b = (*mesh.get_simplex_up({name[1]})).position; auto c = (*mesh.get_simplex_up({name[2]})).position; Vector norm; double tmp; if ((*faceID).orientation == 1) { // a->b->c norm = cross(b, c); tmp = dot(a, norm); } else if ((*faceID).orientation == -1) { // c->b->a norm = cross(b, a); tmp = dot(c, norm); } else { orientError = true; } // * Probably less efficient way #1 // tmp = dot(a, getNormal(mesh, faceID)); // * Less efficient way #2 // norm = getNormalFromTangent(getTangent(mesh, faceID)); // auto wedge = a^b^c; // tmp = std::sqrt(wedge|wedge); // auto sgn = dot((a+b+c)/3, norm); // if(sgn <= 0) { // tmp = -1*tmp; // } volume += tmp; } if(orientError){ std::cerr << "ERROR getVolume(): Orientation undefined for one or more " << "simplices. Did you call compute_orientation()?" << std::endl; } return volume/6; }
int main(void) { // instantiate a SurfaceMesh object SurfaceMesh mesh; // instantiate 4 vertex handles SurfaceMesh::Vertex v0,v1,v2,v3; // add 4 vertices v0 = mesh.add_vertex(Vec3(0,0,0)); v1 = mesh.add_vertex(Vec3(1,0,0)); v2 = mesh.add_vertex(Vec3(0,1,0)); v3 = mesh.add_vertex(Vec3(0,0,1)); // add 4 triangular faces mesh.add_triangle(v0,v1,v3); mesh.add_triangle(v1,v2,v3); mesh.add_triangle(v2,v0,v3); mesh.add_triangle(v0,v2,v1); std::cout << "vertices: " << mesh.n_vertices() << std::endl; std::cout << "edges: " << mesh.n_edges() << std::endl; std::cout << "faces: " << mesh.n_faces() << std::endl; return 0; }
int main(int argc, char** argv) { std::string file = (argc>1) ? argv[1] : "bunny.obj"; SurfaceMesh mesh; bool success = mesh.read(file); CHECK(success); auto points = mesh.get_vertex_property<Vec3>("v:point"); Vec3 p(0,0,0); for (auto vit: mesh.vertices()) p += points[vit]; p /= mesh.n_vertices(); std::cout << "barycenter: " << p << std::endl; }
void coarseIT(SurfaceMesh &mesh, double coarseRate, double flatRate, double denseWeight){ std::cout << "Before coarsening: " << mesh.size<1>() << " " << mesh.size<2>() << " " << mesh.size<3>() << std::endl; // Compute the average edge length double avgLen = 0; if (denseWeight > 0){ avgLen = surfacemesh_detail::getMeanEdgeLength(mesh); } double sparsenessRatio = 1; double flatnessRatio = 1; // Backup vertices so we can modify in place auto range = mesh.get_level_id<1>(); const std::vector<SurfaceMesh::SimplexID<1> > Vertices(range.begin(), range.end()); for(auto vertexID : Vertices) { // Sparseness as coarsening criteria if(denseWeight > 0){ // Get max length of edges. auto edges = mesh.up(vertexID); double maxLen = 0; for(auto edgeID : edges){ auto name = mesh.get_name(edgeID); auto v = *mesh.get_simplex_down(edgeID, name[0]) - *mesh.get_simplex_down(edgeID, name[1]); double tmpLen = std::sqrt(v|v); if(tmpLen > maxLen) maxLen = tmpLen; } sparsenessRatio = std::pow(maxLen/avgLen, denseWeight); } // Curvature as coarsening criteria if(flatRate > 0){ auto lst = surfacemesh_detail::computeLocalStructureTensor(mesh, vertexID, RINGS); auto eigenvalues = surfacemesh_detail::getEigenvalues(lst).eigenvalues(); // The closer this ratio is to 0 the flatter the local region. flatnessRatio = std::pow(eigenvalues[1]/eigenvalues[2], flatRate); } // Check if we should delete if(sparsenessRatio * flatnessRatio < coarseRate){ surfacemesh_detail::decimateVertex(mesh, vertexID); } } std::cout << "After coarsening: " << mesh.size<1>() << " " << mesh.size<2>() << " " << mesh.size<3>() << std::endl; }
bool simulation_load_render_mesh(const char* filename) { SurfaceMesh* mesh = new SurfaceMesh; if (!read_mesh_obj(filename, mesh->positions, mesh->triangles, (render_meshes.empty() ? &mesh->texcoords : NULL))) // we use the texture only for the first OBJ { delete mesh; return false; } mesh->filename = filename; mesh->updateNormals(); if (render_meshes.size()&1) mesh->color = TColor(0.15f, 0.15f, 0.15f, 1.0f); else mesh->color = TColor(0.8f, 0.8f, 0.8f, 1.0f); render_meshes.push_back(mesh); return true; }
bool hasHole(const SurfaceMesh &mesh){ for(auto eID : mesh.get_level_id<2>()){ auto cover = mesh.get_cover(eID); if(cover.size() != 2){ return true; } } return false; }
std::tuple<double, double, int, int> getMinMaxAngles( const SurfaceMesh &mesh, int maxMinAngle, int minMaxAngle) { double minAngle = 360; double maxAngle = 0; int small = 0; int large = 0; // for each triangle for(auto nid : mesh.get_level_id<3>()){ auto name = mesh.get_name(nid); auto a = *mesh.get_simplex_up({name[0]}); auto b = *mesh.get_simplex_up({name[1]}); auto c = *mesh.get_simplex_up({name[2]}); std::array<double, 3> angles; try{ angles[0] = angle(a,b,c); angles[1] = angle(b,a,c); angles[2] = angle(a,c,b); } catch (std::runtime_error& e){ throw std::runtime_error("ERROR(getMinMaxAngles): Cannot compute angles of face with zero area."); } for(double angle : angles){ if (angle < minAngle){ minAngle = angle; } if (angle > maxAngle){ maxAngle = angle; } if (angle < maxMinAngle){ ++small; } if (angle > minMaxAngle){ ++large; } } } return std::make_tuple(minAngle, maxAngle, small, large); }
void print(const SurfaceMesh &mesh) { std::cout << "Level: 1" << std::endl; for (auto node : mesh.get_level_id<1>()) { std::cout << " " << *node << std::endl; } std::cout << "Level: 2" << std::endl; for (auto node : mesh.get_level_id<2>()) { auto name = mesh.get_name(node); std::cout << " " << casc::to_string(name) << std::endl; } std::cout << "Level: 3" << std::endl; for (auto node : mesh.get_level_id<3>()) { auto name = mesh.get_name(node); std::cout << " " << casc::to_string(name) << std::endl; } }
void VertexScalar::loadScalars() { if (!mi) return; SurfaceMesh *mesh = mi->getMesh(); if (!mesh) return; unsigned int i = 0; for (SurfaceMesh::VertexIter v_it = mesh->vertices_begin(); v_it!=mesh->vertices_end(); ++v_it) { if (i >= ps->size()) { ps->addParticle(); } SurfaceMesh::TexCoord2D tc = mesh->texcoord2D(v_it); setScalar(i,tc[0]); i++; } for (unsigned int j = ps->size() - 1; j >= i; j--) ps->removeParticle(j); }
/// Entry point int main(int argc, char** argv) { if(argc!=2) mFatal("usage: glfwviewer bunny.obj"); int success = mesh.read(argv[1]); if(!success) mFatal() << "File not found"; mesh.triangulate(); mesh.update_vertex_normals(); cout << "input: '" << argv[1] << "' num vertices " << mesh.vertices_size() << endl; cout << "BBOX: " << bounding_box(mesh) << endl; glfwInitWindowSize(_width, _height); if (glfwMakeWindow(__FILE__) == EXIT_FAILURE) return EXIT_FAILURE; glfwDisplayFunc(display); glfwTrackball(update_matrices, update_projection_matrix); init(); glfwMainLoop(); return EXIT_SUCCESS; }
int main(int argc, char *argv[]) { //Create and show the Qt OpenGL window QApplication app(argc, argv); OpenGLWidget openGLWidget(0); openGLWidget.show(); //Create an empty volume and then place a sphere in it SimpleVolume<Density8> volData(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(63, 63, 63))); createSphereInVolume(volData, 28); //Smooth the data smoothRegion<SimpleVolume, Density8>(volData, volData.getEnclosingRegion()); smoothRegion<SimpleVolume, Density8>(volData, volData.getEnclosingRegion()); smoothRegion<SimpleVolume, Density8>(volData, volData.getEnclosingRegion()); RawVolume<Density8> volDataLowLOD(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(15, 31, 31))); VolumeResampler<SimpleVolume, RawVolume, Density8> volumeResampler(&volData, PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(31, 63, 63)), &volDataLowLOD, volDataLowLOD.getEnclosingRegion()); volumeResampler.execute(); //Extract the surface SurfaceMesh<PositionMaterialNormal> meshLowLOD; SurfaceExtractor<RawVolume, Density8 > surfaceExtractor(&volDataLowLOD, volDataLowLOD.getEnclosingRegion(), &meshLowLOD); surfaceExtractor.execute(); meshLowLOD.scaleVertices(2.0f); //Extract the surface SurfaceMesh<PositionMaterialNormal> meshHighLOD; SurfaceExtractor<SimpleVolume, Density8 > surfaceExtractorHigh(&volData, PolyVox::Region(Vector3DInt32(32,0,0), Vector3DInt32(63, 63, 63)), &meshHighLOD); surfaceExtractorHigh.execute(); meshHighLOD.translateVertices(Vector3DFloat(32, 0, 0)); //Pass the surface to the OpenGL window openGLWidget.setSurfaceMeshToRender(meshHighLOD); openGLWidget.setSurfaceMeshToRenderLowLOD(meshLowLOD); //Run the message pump. return app.exec(); }
int main(int /*argc*/, char** argv) { SurfaceMesh mesh; mesh.read(argv[1]); float mean_valence = 0.0f; unsigned int vertex_valence; // instantiate iterator and circulators SurfaceMesh::Vertex_iterator vit; SurfaceMesh::Vertex_around_vertex_circulator vc, vc_end; // loop over all vertices for (vit = mesh.vertices_begin(); vit != mesh.vertices_end(); ++vit) { // initialize circulators vc = mesh.vertices(*vit); vc_end = vc; // reset counter vertex_valence = 0; // loop over all incident vertices do { ++vertex_valence; } while (++vc != vc_end); // sum up vertex valences mean_valence += vertex_valence; } mean_valence /= mesh.n_vertices(); std::cout << "mean valence: " << mean_valence << std::endl; }
int main(int argc, char** argv) { std::string in_file = (argc>1) ? argv[1] : "bunny.obj"; std::string out_file = (argc>2) ? argv[2] : "output.obj"; // instantiate a SurfaceMesh object SurfaceMesh mesh; // read a mesh specified as the first command line argument bool success = mesh.read(in_file); CHECK(success); // ... // do fancy stuff with the mesh // ... // write the mesh to the file specified as second argument mesh.write(out_file); CHECK(success); mLogger() << "read:" << in_file << "wrote:" << out_file; return EXIT_SUCCESS; }
void printQualityInfo(const std::string &filename, const SurfaceMesh &mesh){ std::stringstream anglefilename; anglefilename << filename << ".angle"; std::ofstream angleOut(anglefilename.str()); if(!angleOut.is_open()) { std::stringstream ss; ss << "Couldn't open file " << filename << ".angle"; throw std::runtime_error(ss.str()); } std::stringstream areafilename; areafilename << filename << ".area"; std::ofstream areaOut(areafilename.str()); if(!areaOut.is_open()) { std::stringstream ss; ss << "Couldn't open file " << filename << ".area"; throw std::runtime_error(ss.str()); } for(auto faceID : mesh.get_level_id<3>()){ auto name = mesh.get_name(faceID); auto a = *mesh.get_simplex_up({name[0]}); auto b = *mesh.get_simplex_up({name[1]}); auto c = *mesh.get_simplex_up({name[2]}); areaOut << getArea(a,b,c) << "\n"; // Print angles in degrees angleOut << angle(a,b,c) << "\n" << angle(b,a,c) << "\n" << angle(a,c,b) << "\n"; } areaOut.close(); angleOut.close(); }
int main(int /*argc*/, char** argv) { SurfaceMesh mesh; mesh.read(argv[1]); // get (pre-defined) property storing vertex positions SurfaceMesh::Vertex_property<Vec3> points = mesh.get_vertex_property<Vec3>("v:point"); SurfaceMesh::Vertex_iterator vit, vend = mesh.vertices_end(); Vec3 p(0,0,0); for (vit = mesh.vertices_begin(); vit != vend; ++vit) { // access point property like an array p += points[*vit]; } p /= mesh.n_vertices(); std::cout << "barycenter: " << p << std::endl; }
void VertexScalar::saveScalars() { if (!mi) return; SurfaceMesh *mesh = mi->getMesh(); if (!mesh) return; SurfaceMesh::VertexIter v_it = mesh->vertices_begin(); for (unsigned int i = 0; i < ps->size(); i++) { if (v_it == mesh->vertices_end()) { mesh->add_vertex(MyMesh::Point()); } else { SurfaceMesh::TexCoord2D tc = mesh->texcoord2D(v_it); tc[0] = (SurfaceMesh::Scalar) getScalar(i); mesh->set_texcoord2D(v_it, tc); ++v_it; } } while (v_it != mesh->vertices_end()) { SurfaceMesh::VertexIter extra = v_it; ++v_it; // could do this above but may be confusing mesh->delete_vertex(extra); } }
int main(int argc, char** argv){ if(argc!=3){ cout << "usage:" << endl << "isoremesh bunny.obj remeshed.obj" << endl; return EXIT_FAILURE; } std::string input(argv[1]); std::string output(argv[2]); ///--- Load mesh SurfaceMesh mesh; mesh.read(input); if(mesh.n_vertices()==0){ cout << "Input mesh has 0 vertices" << endl; return EXIT_FAILURE; } ///--- Remesher is only for triangulations! mesh.triangulate(); ///--- Compute bounding box Scalar bbox_diag_length = bounding_box(mesh).diagonal().norm(); cout << "#vertices: " << mesh.n_vertices() << endl; cout << "bbox_diag_length: " << bbox_diag_length << endl; ///--- Perform remeshing IsotropicRemesher remesher(mesh); remesher.num_iterations = 10; remesher.sharp_feature_deg = 45; remesher.longest_edge_length = 0.02*bbox_diag_length; remesher.keep_short_edges = false; remesher.reproject_to_surface = true; remesher.myout = &std::cout; ///< print output to... remesher.execute(); ///--- Write to file mesh.write(output); return EXIT_SUCCESS; }
OpenGLSurfaceMesh BuildOpenGLSurfaceMesh(const SurfaceMesh<PositionMaterialNormal>& mesh) { //Represents our filled in OpenGL vertex and index buffer objects. OpenGLSurfaceMesh result; //The source result.sourceMesh = &mesh; //Convienient access to the vertices and indices const vector<PositionMaterialNormal>& vecVertices = mesh.getVertices(); const vector<uint32_t>& vecIndices = mesh.getIndices(); //If we have any indices... if(!vecIndices.empty()) { //Create an OpenGL index buffer glGenBuffers(1, &result.indexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.indexBuffer); //Get a pointer to the first index GLvoid* pIndices = (GLvoid*)(&(vecIndices[0])); //Fill the OpenGL index buffer with our data. glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size() * sizeof(uint32_t), pIndices, GL_STATIC_DRAW); } result.noOfIndices = vecIndices.size(); glGenBuffers(1, &result.vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, result.vertexBuffer); glBufferData(GL_ARRAY_BUFFER, vecVertices.size() * sizeof(GLfloat) * 9, 0, GL_STATIC_DRAW); GLfloat* ptr = (GLfloat*)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); for(vector<PositionMaterialNormal>::const_iterator iterVertex = vecVertices.begin(); iterVertex != vecVertices.end(); ++iterVertex) { const PositionMaterialNormal& vertex = *iterVertex; const Vector3DFloat& v3dVertexPos = vertex.getPosition(); //const Vector3DFloat v3dRegionOffset(uRegionX * g_uRegionSideLength, uRegionY * g_uRegionSideLength, uRegionZ * g_uRegionSideLength); const Vector3DFloat v3dFinalVertexPos = v3dVertexPos + static_cast<Vector3DFloat>(mesh.m_Region.getLowerCorner()); *ptr = v3dFinalVertexPos.getX(); ptr++; *ptr = v3dFinalVertexPos.getY(); ptr++; *ptr = v3dFinalVertexPos.getZ(); ptr++; *ptr = vertex.getNormal().getX(); ptr++; *ptr = vertex.getNormal().getY(); ptr++; *ptr = vertex.getNormal().getZ(); ptr++; uint8_t material = vertex.getMaterial() + 0.5; OpenGLColour colour = convertMaterialIDToColour(material); *ptr = colour.red; ptr++; *ptr = colour.green; ptr++; *ptr = colour.blue; ptr++; } glUnmapBuffer(GL_ARRAY_BUFFER); return result; }
/// OpenGL initialization void init() { ///----------------------- DATA ---------------------------- auto vpoints = mesh.get_vertex_property<Vec3>("v:point"); auto vnormals = mesh.get_vertex_property<Vec3>("v:normal"); assert(vpoints); assert(vnormals); ///---------------------- TRIANGLES ------------------------ triangles.clear(); for(auto f: mesh.faces()) for(auto v: mesh.vertices(f)) triangles.push_back(v.idx()); ///---------------------- OPENGL GLOBALS-------------------- glClearColor(1.0f, 1.0f, 1.0f, 0.0f); ///< background glEnable(GL_DEPTH_TEST); // Enable depth test // glDisable(GL_CULL_FACE); // Cull back-facing /// Compile the shaders programID = load_shaders("vshader.glsl", "fshader.glsl"); if(!programID) exit(EXIT_FAILURE); glUseProgram( programID ); ///---------------------- CAMERA ---------------------------- { typedef Eigen::Vector3f vec3; typedef Eigen::Matrix4f mat4; update_projection_matrix(); /// Define the view matrix (camera extrinsics) vec3 cam_pos(0,0,5); vec3 cam_look(0,0,-1); /// Remember: GL swaps viewdir vec3 cam_up(0,1,0); view = OpenGP::lookAt(cam_pos, cam_look, cam_up); // cout << view << endl; /// Define the modelview matrix model = mat4::Identity(); // cout << model << endl; /// Set initial matrices set_uniform_matrix(programID,"M",model); ///< to get world coordinates set_uniform_matrix(programID,"MV",view*model); ///< to get camera coordinates set_uniform_matrix(programID,"MVP",projection*view*model); ///< to get clip coordinates } ///---------------------- LIGHT ----------------------------- { Vec3 light_dir(0,0,1); set_uniform_vector(programID,"LDIR",light_dir); ///< to get camera coordinates } ///---------------------- VARRAY ---------------------------- { GLuint VertexArrayID; glGenVertexArrays(1, &VertexArrayID); glBindVertexArray(VertexArrayID); } ///---------------------- BUFFERS ---------------------------- GLuint vertexbuffer, normalbuffer, trianglebuffer; { /// Load mesh vertices glGenBuffers(1, &vertexbuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glBufferData(GL_ARRAY_BUFFER, mesh.n_vertices() * sizeof(Vec3), vpoints.data(), GL_STATIC_DRAW); /// Load mesh normals glGenBuffers(1, &normalbuffer); glBindBuffer(GL_ARRAY_BUFFER, normalbuffer); glBufferData(GL_ARRAY_BUFFER, mesh.n_vertices() * sizeof(Vec3), vnormals.data(), GL_STATIC_DRAW); /// Triangle indexes buffer glGenBuffers(1, &trianglebuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, trianglebuffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangles.size() * sizeof(unsigned int), &triangles[0], GL_STATIC_DRAW); } ///---------------------- SHADER ATTRIBUTES ---------------------------- { /// Vertex positions in shader variable "vposition" GLuint vposition = glGetAttribLocation(programID, "vposition"); glEnableVertexAttribArray(vposition); glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); glVertexAttribPointer(vposition, 3, GL_FLOAT, DONT_NORMALIZE, ZERO_STRIDE, ZERO_BUFFER_OFFSET); /// Vertex normals in in shader variable "vnormal" GLuint vnormal = glGetAttribLocation(programID, "vnormal"); glEnableVertexAttribArray(vnormal); glBindBuffer(GL_ARRAY_BUFFER, normalbuffer); glVertexAttribPointer(vnormal, 3, GL_FLOAT, DONT_NORMALIZE, ZERO_STRIDE, ZERO_BUFFER_OFFSET); } }
// unit test int main() { // init output code int OUTPUT_CODE = 0; // 0 indicates success, 1 is failure // create the object SurfaceMesh TM; // non-manifold 2-D mesh example TM.Reserve_Cells(7); TM.Append_Cell(0,1,5); TM.Append_Cell(5,2,1); TM.Append_Cell(1,5,3); TM.Append_Cell(4,5,1); TM.Append_Cell(9,6,5); TM.Append_Cell(9,5,7); TM.Append_Cell(8,9,5); // now add the vertex point coordinates TM.Init_Points(10); TM.Set_Coord(0, -0.5, 0.0,-1.0); TM.Set_Coord(1, -1.0, 0.0, 0.0); TM.Set_Coord(2, -0.5, 1.0, 0.0); TM.Set_Coord(3, -0.5, 0.0, 1.0); TM.Set_Coord(4, -0.5,-1.0, 0.0); TM.Set_Coord(5, 0.0, 0.0, 0.0); TM.Set_Coord(6, 0.5, 0.2, 1.0); TM.Set_Coord(7, 0.5,-0.1,-1.0); TM.Set_Coord(8, 0.5,-1.0, 0.0); TM.Set_Coord(9, 1.0, 0.0, 0.0); // now display coordinates cout << endl; TM.Display_Vtx_Coord(); // we now stop adding cells TM.Finalize_v2hfs_DEBUG(); // now display the half-facets (attached to vertices) of the intermediate structure 'v2hfs' cout << endl; TM.Display_v2hfs(); // check 'v2hfs' against reference data VtxHalfFacetType v2hfs_REF[21]; v2hfs_REF[ 0].Set(1,0,2); v2hfs_REF[ 1].Set(2,1,0); v2hfs_REF[ 2].Set(3,2,1); v2hfs_REF[ 3].Set(4,3,1); v2hfs_REF[ 4].Set(5,0,0); v2hfs_REF[ 5].Set(5,0,1); v2hfs_REF[ 6].Set(5,1,1); v2hfs_REF[ 7].Set(5,1,2); v2hfs_REF[ 8].Set(5,2,0); v2hfs_REF[ 9].Set(5,2,2); v2hfs_REF[10].Set(5,3,0); v2hfs_REF[11].Set(5,3,2); v2hfs_REF[12].Set(6,4,0); v2hfs_REF[13].Set(7,5,0); v2hfs_REF[14].Set(8,6,1); v2hfs_REF[15].Set(9,4,1); v2hfs_REF[16].Set(9,4,2); v2hfs_REF[17].Set(9,5,1); v2hfs_REF[18].Set(9,5,2); v2hfs_REF[19].Set(9,6,0); v2hfs_REF[20].Set(9,6,2); // error check const Vtx2HalfFacet_Mapping& c_V2HF_Map = TM.Get_v2hfs(); const std::vector<VtxHalfFacetType>& c_v2hfs = c_V2HF_Map.Get_VtxMap(); for (VtxIndType jj = 0; jj < c_v2hfs.size(); ++jj) if (!c_v2hfs[jj].Equal(v2hfs_REF[jj])) { cout << "Intermediate data 'v2hfs' failed!" << endl; OUTPUT_CODE = 1; break; } // fill out the sibling half-facet data in 'Cell' TM.Build_Sibling_HalfFacets_DEBUG(); // display that cell connectivity data cout << endl; TM.Display_Cell(); cout << endl; // check 'Cell' against reference data CellSimplexType<2> Cell_REF[7]; HalfFacetType hf; // Cell #1 Cell_REF[ 0].Set(0,0,hf.Set(1,1)); Cell_REF[ 0].Set(1,1,hf.Set()); Cell_REF[ 0].Set(2,5,hf.Set()); // Cell #2 Cell_REF[ 1].Set(0,5,hf.Set()); Cell_REF[ 1].Set(1,2,hf.Set(2,2)); Cell_REF[ 1].Set(2,1,hf.Set()); // Cell #3 Cell_REF[ 2].Set(0,1,hf.Set()); Cell_REF[ 2].Set(1,5,hf.Set()); Cell_REF[ 2].Set(2,3,hf.Set(3,0)); // Cell #4 Cell_REF[ 3].Set(0,4,hf.Set(0,0)); Cell_REF[ 3].Set(1,5,hf.Set()); Cell_REF[ 3].Set(2,1,hf.Set()); // Cell #5 Cell_REF[ 4].Set(0,9,hf.Set()); Cell_REF[ 4].Set(1,6,hf.Set(5,2)); Cell_REF[ 4].Set(2,5,hf.Set()); // Cell #6 Cell_REF[ 5].Set(0,9,hf.Set()); Cell_REF[ 5].Set(1,5,hf.Set()); Cell_REF[ 5].Set(2,7,hf.Set(6,0)); // Cell #7 Cell_REF[ 6].Set(0,8,hf.Set(4,1)); Cell_REF[ 6].Set(1,9,hf.Set()); Cell_REF[ 6].Set(2,5,hf.Set()); // error check for (CellIndType cc = 0; cc < TM.Num_Cells(); ++cc) { const VtxIndType* C_vtx = NULL; const HalfFacetType* C_hf = NULL; TM.Get_Cell(cc, C_vtx, C_hf); if (!Cell_REF[cc].Equal(C_vtx, C_hf)) { cout << "Cell connectivity (and sibling half-facets) is incorrect!" << endl; OUTPUT_CODE = 2; break; } } // generate the Vtx2HalfFacets data struct TM.Build_Vtx2HalfFacets_DEBUG(); TM.Close(); // we can close it now, i.e. no more modifications // now display half-facets attached to vertices for final data structure TM.Display_Vtx2HalfFacets(); cout << endl; // check 'Vtx2HalfFacets' against reference data VtxHalfFacetType Vtx2HalfFacets_REF[11]; Vtx2HalfFacets_REF[ 0].Set(0,0,2); Vtx2HalfFacets_REF[ 1].Set(1,3,1); Vtx2HalfFacets_REF[ 2].Set(2,1,2); Vtx2HalfFacets_REF[ 3].Set(3,2,1); Vtx2HalfFacets_REF[ 4].Set(4,3,2); Vtx2HalfFacets_REF[ 5].Set(5,3,2); Vtx2HalfFacets_REF[ 6].Set(5,6,1); Vtx2HalfFacets_REF[ 7].Set(6,4,2); Vtx2HalfFacets_REF[ 8].Set(7,5,1); Vtx2HalfFacets_REF[ 9].Set(8,6,2); Vtx2HalfFacets_REF[10].Set(9,6,2); // error check const std::vector<VtxHalfFacetType>& c_Vtx2HF = TM.Get_Vtx2HalfFacets().Get_VtxMap(); for (VtxIndType jj = 0; jj < c_Vtx2HF.size(); ++jj) if (!c_Vtx2HF[jj].Equal(Vtx2HalfFacets_REF[jj])) { cout << "'Vtx2HalfFacets' data is incorrect!" << endl; OUTPUT_CODE = 3; break; } // display cells attached to a vertex VtxIndType V_IN = 5; TM.Display_Cells_Attached_To_Vertex(V_IN); cout << endl; // check attached cells against reference data std::vector<CellIndType> cell_ind_1, cell_ind_2; TM.Get_Cells_Attached_To_Vertex(V_IN, 0, cell_ind_1); TM.Get_Cells_Attached_To_Vertex(V_IN, 4, cell_ind_2); const CellIndType cell_ind_1_REF[4] = {0, 1, 2, 3}; const CellIndType cell_ind_2_REF[3] = {4, 5, 6}; for (unsigned int jj = 0; jj < cell_ind_1.size(); ++jj) if (cell_ind_1[jj]!=cell_ind_1_REF[jj]) { cout << "Cell attachment data is incorrect!" << endl; OUTPUT_CODE = 4; break; } for (unsigned int jj = 0; jj < cell_ind_2.size(); ++jj) if (cell_ind_2[jj]!=cell_ind_2_REF[jj]) { cout << "Cell attachment data is incorrect!" << endl; OUTPUT_CODE = 5; break; } // display if two cells are facet connected const VtxIndType V1 = 5; const CellIndType C1 = 0; const CellIndType C2 = 4; TM.Display_Two_Cells_Are_Facet_Connected(V1, C1, C2); cout << endl; // check facet connected cells against reference data const bool CHK_Facet_Connected = TM.Two_Cells_Are_Facet_Connected(V1, C1, C2); const bool CHK_Facet_Connected_REF = false; if (CHK_Facet_Connected!=CHK_Facet_Connected_REF) { cout << "Facet connected cell data is incorrect!" << endl; OUTPUT_CODE = 6; } // display half-facets attached to given half-facet HalfFacetType TEST_attached; TEST_attached.Set(1,1); TM.Display_HalfFacets_Attached_To_HalfFacet(TEST_attached); cout << endl; // check attached half-facets against reference data HalfFacetType attached_REF[4]; attached_REF[0].Set(1,1); attached_REF[1].Set(2,2); attached_REF[2].Set(3,0); attached_REF[3].Set(0,0); std::vector<HalfFacetType> attached; TM.Get_HalfFacets_Attached_To_HalfFacet(TEST_attached, attached); for (unsigned int jj = 0; jj < attached.size(); ++jj) if (!attached[jj].Equal(attached_REF[jj])) { cout << "HalfFacet attachment data is incorrect!" << endl; OUTPUT_CODE = 7; break; } // check that half-facet has no neighbor TEST_attached.Set(4,2); TM.Get_HalfFacets_Attached_To_HalfFacet(TEST_attached, attached); if (attached.size()!=1) { cout << "HalfFacet attachment data is incorrect!" << endl; OUTPUT_CODE = 8; } else if (!TEST_attached.Equal(attached[0])) { cout << "HalfFacet attachment should only include the initial given half-facet..." << endl; cout << " but it does not!" << endl; OUTPUT_CODE = 9; } // display non-manifold facets (edges) TM.Display_Nonmanifold_HalfFacets(); cout << endl; // check non-manifold facets (edges) against reference data HalfFacetType non_manifold_hf_REF[2]; non_manifold_hf_REF[0].Set(3,0); non_manifold_hf_REF[1].Set(6,0); std::vector<HalfFacetType> non_manifold_hf; TM.Get_Nonmanifold_HalfFacets(non_manifold_hf); for (unsigned int jj = 0; jj < non_manifold_hf.size(); ++jj) if (!non_manifold_hf[jj].Equal(non_manifold_hf_REF[jj])) { cout << "Non-manifold facet data is incorrect!" << endl; OUTPUT_CODE = 10; break; } // display non-manifold vertices TM.Display_Nonmanifold_Vertices(); cout << endl; // check non-manifold vertices against reference data const VtxIndType non_manifold_vtx_REF[1] = {5}; std::vector<VtxIndType> non_manifold_vtx; TM.Get_Nonmanifold_Vertices(non_manifold_vtx); for (unsigned int jj = 0; jj < non_manifold_vtx.size(); ++jj) if (non_manifold_vtx[jj]!=non_manifold_vtx_REF[jj]) { cout << "Non-manifold vertex data is incorrect!" << endl; OUTPUT_CODE = 11; break; } TM.Display_Unique_Vertices(); cout << endl; // check unique vertices against reference data std::vector<VtxIndType> uv; TM.Get_Unique_Vertices(uv); const VtxIndType uv_REF[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; for (unsigned int jj = 0; jj < uv.size(); ++jj) if (uv[jj]!=uv_REF[jj]) { cout << "Unique vertex data is incorrect!" << endl; OUTPUT_CODE = 12; break; } // display all edges in the mesh std::vector<MeshEdgeType> edges; TM.Get_Edges(edges); cout << "A unique list of edges in the mesh:" << endl; std::vector<MeshEdgeType>::const_iterator it; for (it = edges.begin(); it!=edges.end(); ++it) { cout << "[" << (*it).vtx[0] << ", " << (*it).vtx[1] << "]" << endl; } cout << endl; // test if a pair of vertices is connected by an edge cout << "Vtx #5 and Vtx #9 are connected." << endl; if (!TM.Is_Connected(5, 9)) { cout << "Incorrect! They are NOT connected!" << endl; OUTPUT_CODE = 13; } // test if a pair of vertices is connected by an edge cout << "Vtx #7 and Vtx #2 are NOT connected." << endl; if (TM.Is_Connected(7, 2)) { cout << "Incorrect! They ARE connected!" << endl; OUTPUT_CODE = 14; } cout << endl; // display all cells attached to an edge std::vector<CellIndType> attached_cells; MeshEdgeType EE; EE.Set(1,5); TM.Get_Cells_Attached_To_Edge(EE, attached_cells); cout << "Here are the cell indices of cells attached to the edge [1, 5]:" << endl; std::vector<CellIndType>::const_iterator ic; for (ic = attached_cells.begin(); ic!=attached_cells.end(); ++ic) cout << "Cell #" << (*ic) << endl; cout << endl; // display the free boundary std::vector<HalfFacetType> free_bdy; TM.Get_FreeBoundary(free_bdy); cout << "Here are the half-facets that lie on the free boundary:" << endl; std::vector<HalfFacetType>::const_iterator hfi; for (hfi = free_bdy.begin(); hfi!=free_bdy.end(); ++hfi) { (*hfi).Print(); cout << endl; } cout << endl; if (OUTPUT_CODE==0) cout << "Unit test is successful!" << endl; else cout << "Unit test failed!" << endl; cout << endl; return OUTPUT_CODE; }
void operator()( const Grid & grid, const unsigned dir, const bool begin, SurfaceMesh & surfaceMesh ) const { //! Sanity check of direction VERIFY_MSG( (dir < Grid::dim), "Input argument for direction wrong" ); //! Dimensions of the grid const MultiIndexType gridSizes = grid.gridSizes(); //! Multi-index component to compare with const int mIndexComp = ( begin == false ? 0 : gridSizes[dir]-1 ); //! Number of elements in the structured grid const std::size_t numElements = MultiIndex::length( gridSizes ); //! Number of elements on the requested surface const std::size_t numElementsOnSurface = numElements / gridSizes[ dir ]; //! Construct parametric geometry of the element const unsigned surfaceID = detail_::convertToSurfaceID<VolumeElement::shape>( dir, begin ); //! List of indices of the parameter space vertices which form the surface typename ParameterSurface::Surface parameterSurfaceIndices = ParameterSurface::surfaceTable[ surfaceID ]; //! Interpolation points of the surface shape function boost::array< typename SurfaceShapeFun::VecDim, SurfaceShapeFun::numFun> surfaceSupportPoints; SurfaceShapeFun::supportPoints( surfaceSupportPoints ); //! Vertices of the parameter volume boost::array< typename LinearVolumeFun::VecDim, LinearVolumeFun::numFun> volumeVertices; LinearVolumeFun::supportPoints( volumeVertices ); // --> Reorder for hiearchical ordering // Then, the object ParameterFaces< shape, dim-1> can be used and // ParamaterSurface discarded. //! Iterator to surface mesh elements typename SurfaceMesh::ElementPtrIter surfElemIter = surfaceMesh.elementsBegin(); //! Linear shape function on the surface simplex LinearSimplexFun linearSimplexFun; //! Hexahedra will have two triangles per face const unsigned numSurfElementsPerElement = detail_::NumSimplicesPerElementSurface<VolumeElement::shape>::value; //! Temporary storage of surface elements and nodes std::vector<SurfaceElement*> surfaceElements; surfaceElements.reserve( numElementsOnSurface * numSurfElementsPerElement ); std::vector<SurfaceNode*> surfaceNodes; //! node counter std::size_t nodeCtr = 0; //! Go through all elements for ( std::size_t e = 0; e < numElements; e ++ ) { //! Construct multi-index from linear counter const MultiIndexType eM = MultiIndex::wrap( e, gridSizes ); //! Check if on requested boundary surface const bool onBoundary = ( eM[ dir ] == mIndexComp ); //! If so, construct the surface element(s) if ( onBoundary ) { //! Get pointer to volume element VolumeElement * vep = grid.elementPtr( eM ); //! Go through elements on the surface of the volume element for ( unsigned se = 0; se < numSurfElementsPerElement; se ++ ) { //! Create new surface element SurfaceElement * surfElem = new SurfaceElement; //! Extract the surface simplex's vertices boost::array<unsigned, numSurfSimplexVertices> surfSimplex; detail_::ExtractSurfaceSimplex<VolumeElement::shape>()( parameterSurfaceIndices, se, surfSimplex ); //! Set volume element pointer surfElem -> setVolumeElementPointer( vep ); //! Access to surface elements geometry nodes typename SurfaceElement::NodePtrIter nodePtrIter = surfElem -> nodesBegin(); //! Go through the parameter points of the surface element typename SurfaceElement::ParamIter paramIter = surfElem -> parametricBegin(); typename SurfaceElement::ParamIter paramEnd = surfElem -> parametricEnd(); for ( unsigned p = 0; paramIter != paramEnd; ++paramIter, ++nodePtrIter, p++ ) { //! .. typename base::Vector<surfaceDim>::Type eta = surfaceSupportPoints[ p ]; typename LinearSimplexFun::FunArray phi; linearSimplexFun.evaluate( eta, phi ); typename base::Vector<volumeDim>::Type xi = base::constantVector<volumeDim>( 0. ); for ( unsigned s = 0; s < phi.size(); s ++ ) { xi += phi[s] * volumeVertices[ surfSimplex[s] ]; } *paramIter = xi; //! evaluate geometry at the parameter point const typename VolumeElement::Node::VecDim x = base::Geometry<VolumeElement>()( vep, xi ); //! convert to vector and pass to node std::vector<double> xV( x.size() ); for ( int d = 0; d < x.size(); d ++ ) xV[d] = x[d]; SurfaceNode * surfNode = new SurfaceNode; surfNode -> setX( xV.begin() ); surfNode -> setID( nodeCtr++ ); *nodePtrIter = surfNode; surfaceNodes.push_back( surfNode ); } // end loop over surface element's nodes //! Store element pointer surfaceElements.push_back( surfElem ); } // end loop over simplices per volume element } // end condition if volume element lies on requested bdry }// end loop over all volume elements surfaceMesh.allocate( surfaceNodes.size(), surfaceElements.size() ); std::copy( surfaceNodes.begin(), surfaceNodes.end(), surfaceMesh.nodesBegin() ); std::copy( surfaceElements.begin(), surfaceElements.end(), surfaceMesh.elementsBegin() ); return; }
int getValence(const SurfaceMesh &mesh, const SurfaceMesh::SimplexID<1> vertexID) { return mesh.get_cover(vertexID).size(); }
void generateHistogram(const SurfaceMesh &mesh) { // compute angle distribution std::array<double, 18> histogram; histogram.fill(0); for (auto face : mesh.get_level_id<3>()) { auto vertexIDs = mesh.get_name(face); // Unpack the ID's for convenience Vertex a = *mesh.get_simplex_up<1>({vertexIDs[0]}); Vertex b = *mesh.get_simplex_up<1>({vertexIDs[1]}); Vertex c = *mesh.get_simplex_up<1>({vertexIDs[2]}); auto binAngle = [&](double angle) -> int{ return std::floor(angle/10); }; histogram[binAngle(angle(a, b, c))]++; histogram[binAngle(angle(b, a, c))]++; histogram[binAngle(angle(c, a, b))]++; } int factor = mesh.size<3>()*3; std::for_each(histogram.begin(), histogram.end(), [&factor](double &n){ n = 100.0*n/factor; }); std::cout << "Angle Distribution:" << std::endl; for (int x = 0; x < 18; x++) std::cout << x*10 << "-" << (x+1)*10 << ": " << std::setprecision(2) << std::fixed << histogram[x] << std::endl; std::cout << std::endl << std::endl; // compute the edge length distribution std::cout << "Edge Length Distribution:" << std::endl; std::vector<double> lengths; for (auto edge : mesh.get_level_id<2>()) { auto vertexIDs = mesh.down(edge); auto t1 = *vertexIDs.cbegin(); auto t2 = *(++vertexIDs.cbegin()); auto v1 = *t1; auto v2 = *t2; double len = magnitude(v2-v1); lengths.push_back(len); } std::sort(lengths.begin(), lengths.end()); std::array<double, 20> histogramLength; histogramLength.fill(0); double interval = (lengths.back() - lengths.front())/20; double low = lengths.front(); if (interval <= 0.0000001) // floating point roundoff prevention { std::cout << lengths.front() << ": " << 100 << std::endl << std::endl; } else { for (auto length : lengths) { histogramLength[std::floor((length-low)/interval)]++; } factor = mesh.size<2>(); std::for_each(histogramLength.begin(), histogramLength.end(), [&factor](double &n){ n = 100.0*n/factor; }); for (int x = 0; x < 20; x++) std::cout << x*interval << "-" << (x+1)*interval << ": " << std::setprecision(2) << std::fixed << histogramLength[x] << std::endl; std::cout << std::endl << std::endl; } // Compute the valence distribution std::array<double, 20> histogramValence; histogramValence.fill(0); for (auto vertexID : mesh.get_level_id<1>()) { // TODO bounds checking here... histogramValence[getValence(mesh, vertexID)]++; } factor = mesh.size<1>(); // std::for_each(histogramValence.begin(), histogramValence.end(), // [&factor](double& n){ // n = 100.0*n/factor;}); std::cout << "Valence distribution:" << std::endl; for (int x = 0; x < 20; x++) std::cout << x << ": " << histogramValence[x] << std::endl; std::cout << std::endl << std::endl; }