bool write_off(const Surface_mesh& mesh, const std::string& filename) { FILE* out = fopen(filename.c_str(), "w"); if (!out) return false; // header fprintf(out, "OFF\n%d %d 0\n", mesh.n_vertices(), mesh.n_faces()); // vertices Surface_mesh::Vertex_property<Surface_mesh::Point> points = mesh.get_vertex_property<Point>("v:point"); for (Surface_mesh::Vertex_iterator vit=mesh.vertices_begin(); vit!=mesh.vertices_end(); ++vit) { const Point& p = points[vit]; fprintf(out, "%.10f %.10f %.10f\n", p[0], p[1], p[2]); } // faces for (Surface_mesh::Face_iterator fit=mesh.faces_begin(); fit!=mesh.faces_end(); ++fit) { int nV = mesh.valence(fit); fprintf(out, "%d", nV); Surface_mesh::Vertex_around_face_circulator fvit=mesh.vertices(fit), fvend=fvit; do { fprintf(out, " %d", ((Surface_mesh::Vertex)fvit).idx()); } while (++fvit != fvend); fprintf(out, "\n"); } fclose(out); return true; }
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 Surface_mesh 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; }
bool read_obj(Surface_mesh& mesh, const std::string& filename) { char s[200]; float x, y, z; std::vector<Surface_mesh::Vertex> vertices; std::vector<Texture_coordinate> all_tex_coords; //individual texture coordinates std::vector<int> halfedge_tex_idx; //texture coordinates sorted for halfedges Surface_mesh::Halfedge_property <Texture_coordinate> tex_coords = mesh.halfedge_property<Texture_coordinate>("h:texcoord"); bool with_tex_coord=false; // clear mesh mesh.clear(); // open file (in ASCII mode) FILE* in = fopen(filename.c_str(), "r"); if (!in) return false; // clear line once memset(&s, 0, 200); // parse line by line (currently only supports vertex positions & faces while(in && !feof(in) && fgets(s, 200, in)) { // comment if (s[0] == '#' || isspace(s[0])) continue; // vertex else if (strncmp(s, "v ", 2) == 0) { if (sscanf(s, "v %f %f %f", &x, &y, &z)) { mesh.add_vertex(Point(x,y,z)); } } // normal else if (strncmp(s, "vn ", 3) == 0) { if (sscanf(s, "vn %f %f %f", &x, &y, &z)) { // problematic as it can be either a vertex property when interpolated // or a halfedge property for hard edges } } // texture coordinate else if (strncmp(s, "vt ", 3) == 0) { if (sscanf(s, "vt %f %f", &x, &y)) { z=1; all_tex_coords.push_back(Texture_coordinate(x,y,z)); } } // face else if (strncmp(s, "f ", 2) == 0) { int component(0), nV(0); bool endOfVertex(false); char *p0, *p1(s+1); vertices.clear(); halfedge_tex_idx.clear(); // skip white-spaces while (*p1==' ') ++p1; while (p1) { p0 = p1; // overwrite next separator // skip '/', '\n', ' ', '\0', '\r' <-- don't forget Windows while (*p1!='/' && *p1!='\r' && *p1!='\n' && *p1!=' ' && *p1!='\0') ++p1; // detect end of vertex if (*p1 != '/') { endOfVertex = true; } // replace separator by '\0' if (*p1 != '\0') { *p1 = '\0'; p1++; // point to next token } // detect end of line and break if (*p1 == '\0' || *p1 == '\n') { p1 = 0; } // read next vertex component if (*p0 != '\0') { switch (component) { case 0: // vertex { vertices.push_back( Surface_mesh::Vertex(atoi(p0) - 1) ); break; } case 1: // texture coord { int idx = atoi(p0)-1; halfedge_tex_idx.push_back(idx); with_tex_coord=true; break; } case 2: // normal break; } } ++component; if (endOfVertex) { component = 0; nV++; endOfVertex = false; } } Surface_mesh::Face f=mesh.add_face(vertices); // add texture coordinates if(with_tex_coord) { Surface_mesh::Halfedge_around_face_circulator h_fit = mesh.halfedges(f); Surface_mesh::Halfedge_around_face_circulator h_end = h_fit; unsigned v_idx =0; do { tex_coords[*h_fit]=all_tex_coords.at(halfedge_tex_idx.at(v_idx)); ++v_idx; ++h_fit; } while(h_fit!=h_end); } } // clear line memset(&s, 0, 200); } fclose(in); return true; }
bool write_obj(const Surface_mesh& mesh, const std::string& filename) { FILE* out = fopen(filename.c_str(), "w"); if (!out) return false; // comment fprintf(out, "# OBJ export from Surface_mesh\n"); //vertices Surface_mesh::Vertex_property<Point> points = mesh.get_vertex_property<Point>("v:point"); for (Surface_mesh::Vertex_iterator vit=mesh.vertices_begin(); vit!=mesh.vertices_end(); ++vit) { const Point& p = points[*vit]; fprintf(out, "v %.10f %.10f %.10f\n", p[0], p[1], p[2]); } //normals Surface_mesh::Vertex_property<Point> normals = mesh.get_vertex_property<Point>("v:normal"); for (Surface_mesh::Vertex_iterator vit=mesh.vertices_begin(); vit!=mesh.vertices_end(); ++vit) { const Point& p = normals[*vit]; fprintf(out, "vn %.10f %.10f %.10f\n", p[0], p[1], p[2]); } //optionally texture coordinates // do we have them? std::vector<std::string> h_props= mesh.halfedge_properties(); bool with_tex_coord = false; std::vector<std::string>::iterator h_prop_end = h_props.end(); std::vector<std::string>::iterator h_prop_start= h_props.begin(); while(h_prop_start!=h_prop_end) { if(0==(*h_prop_start).compare("h:texcoord")) { with_tex_coord=true; } ++h_prop_start; } //if so then add if(with_tex_coord) { Surface_mesh::Halfedge_property<Texture_coordinate> tex_coord = mesh.get_halfedge_property<Texture_coordinate>("h:texcoord"); for (Surface_mesh::Halfedge_iterator hit=mesh.halfedges_begin(); hit!=mesh.halfedges_end(); ++hit) { const Texture_coordinate& pt = tex_coord[*hit]; fprintf(out, "vt %.10f %.10f %.10f\n", pt[0], pt[1], pt[2]); } } //faces for (Surface_mesh::Face_iterator fit=mesh.faces_begin(); fit!=mesh.faces_end(); ++fit) { fprintf(out, "f"); Surface_mesh::Vertex_around_face_circulator fvit=mesh.vertices(*fit), fvend=fvit; Surface_mesh::Halfedge_around_face_circulator fhit=mesh.halfedges(*fit); do { if(with_tex_coord) { // write vertex index, tex_coord index and normal index fprintf(out, " %d/%d/%d", (*fvit).idx()+1, (*fhit).idx()+1, (*fvit).idx()+1); ++fhit; } else { // write vertex index and normal index fprintf(out, " %d//%d", (*fvit).idx()+1, (*fvit).idx()+1); } } while (++fvit != fvend); fprintf(out, "\n"); } fclose(out); return true; }
bool read_off(Surface_mesh& mesh, const std::string& filename) { int err = 0; unsigned int i, j, idx; unsigned int nV, nF, nE; Eigen::Vector3d p, n; Eigen::Vector2d t; Surface_mesh::Vertex v; char line[100], *c; bool has_texcoords = false; bool has_normals = false; bool has_colors = false; bool has_hcoords = false; bool has_dim = false; bool is_binary = false; // if mesh is not empty we need an offset for vertex indices const unsigned int voffset = mesh.n_vertices(); // open file (in ASCII mode) FILE* in = fopen(filename.c_str(), "r"); if (!in) return false; // read header: [ST][C][N][4][n]OFF BINARY c = fgets(line, 100, in); assert(c != NULL); c = line; if (c[0] == 'S' && c[1] == 'T') { has_texcoords = true; c += 2; } if (c[0] == 'C') { has_colors = true; ++c; } if (c[0] == 'N') { has_normals = true; ++c; } if (c[0] == '4') { has_hcoords = true; ++c; } if (c[0] == 'n') { has_dim = true; ++c; } if (strncmp(c, "OFF", 3) != 0) { fclose(in); // no OFF return false; } if (strncmp(c+4, "BINARY", 6) == 0) is_binary = true; const bool binary = is_binary; // const might speed things up // colors, homogeneous coords, and vertex dimension != 3 are not supported if (has_colors || has_hcoords || has_dim) { fclose(in); return false; } // properties Surface_mesh::Vertex_property<Surface_mesh::Normal> normals; Surface_mesh::Vertex_property<Surface_mesh::Texture_coordinate> texcoords; if (has_normals) normals = mesh.vertex_property<Surface_mesh::Normal>("v:normal"); if (has_texcoords) texcoords = mesh.vertex_property<Surface_mesh::Texture_coordinate>("v:texcoord"); // if binary: reopen file in binary mode if (binary) { fclose(in); in = fopen(filename.c_str(), "rb"); c = fgets(line, 100, in); assert(c != NULL); } // (ennetws) bug fix for comments, empty lines.. while(!binary && true && !feof(in)) { fgets(line, 100, in); if(strlen(line) > 4 && line[0] != '#') break; } // #Vertice, #Faces, #Edges if (binary) { read(in, nV); read(in, nF); read(in, nE); } else { err = sscanf(line, "%d %d %d", (int*)&nV, (int*)&nF, (int*)&nE); // (ennetws) bug fix } mesh.reserve(nV, std::max(3*nV, nE), nF); // read vertices: pos [norma] [texcoord] if (has_normals && has_texcoords) { for (i=0; i<nV && !feof(in); ++i) { if (binary) { read(in, p); read(in, n); read(in, t); } else { err = fscanf(in, "%lf %lf %lf %lf %lf %lf %lf %lf", &p[0], &p[1], &p[2], &n[0], &n[1], &n[2], &t[0], &t[1]); } v = mesh.add_vertex(p); normals[v] = n; texcoords[v][0] = t[0]; texcoords[v][1] = t[1]; } } else if (has_normals) { for (i=0; i<nV && !feof(in); ++i) { if (binary) { read(in, p); read(in, n); } else { err = fscanf(in, "%lf %lf %lf %lf %lf %lf", &p[0], &p[1], &p[2], &n[0], &n[1], &n[2]); } v = mesh.add_vertex(p); normals[v] = n; } } else if (has_texcoords) { for (i=0; i<nV && !feof(in); ++i) { if (binary) { read(in, p); read(in, t); } else { err = fscanf(in, "%lf %lf %lf %lf %lf", &p[0], &p[1], &p[2], &t[0], &t[1]); } v = mesh.add_vertex(p); texcoords[v][0] = t[0]; texcoords[v][1] = t[1]; } } else { for (i=0; i<nV && !feof(in); ++i) { if (binary) { read(in, p); } else { err = fscanf(in, "%lf %lf %lf", &p[0], &p[1], &p[2]); } mesh.add_vertex(p); } } // read faces: #N v[1] v[2] ... v[n-1] std::vector<Surface_mesh::Vertex> vertices; for (i=0; i<nF; ++i) { if (binary) { read(in, nV); vertices.resize(nV); for (j=0; j<nV; ++j) { read(in, idx); vertices[j] = Surface_mesh::Vertex(idx+voffset); } } else { err = fscanf(in, "%d", (int*)&nV); vertices.resize(nV); for (j=0; j<nV; ++j) { err = fscanf(in, "%d", (int*)&idx); vertices[j] = Surface_mesh::Vertex(idx+voffset); } } mesh.add_face(vertices); } // File was successfully parsed. fclose(in); return true; }
bool read_off_ascii(Surface_mesh& mesh, FILE* in, const bool has_normals, const bool has_texcoords, const bool has_colors, NamedParameters& np) { char line[100], *lp; unsigned int i, j, items, idx, nc; unsigned int nV, nF, nE; Vec3f p, n, c; Vec2f t; Surface_mesh::Vertex v; typename CGAL::Polygon_mesh_processing::GetVertexPointMap<Surface_mesh, NamedParameters>::const_type vpm = choose_param(get_param(np, CGAL::boost::internal_np::vertex_point), get_const_property_map(CGAL::vertex_point, mesh)); // properties Surface_mesh::Vertex_property<Normal> normals; Surface_mesh::Vertex_property<Texture_coordinate> texcoords; Surface_mesh::Vertex_property<Color> colors; if (has_normals) normals = mesh.vertex_property<Normal>("v:normal"); if (has_texcoords) texcoords = mesh.vertex_property<Texture_coordinate>("v:texcoord"); if (has_colors) colors = mesh.vertex_property<Color>("v:color"); // #Vertice, #Faces, #Edges items = fscanf(in, "%d %d %d\n", (int*)&nV, (int*)&nF, (int*)&nE); mesh.clear(); mesh.reserve(nV, std::max(3*nV, nE), nF); // read vertices: pos [normal] [color] [texcoord] for (i=0; i<nV && !feof(in); ++i) { // read line lp = fgets(line, 100, in); // position items = sscanf(lp, "%f %f %f%n", &p[0], &p[1], &p[2], &nc); assert(items==3); Surface_mesh::Vertex v = mesh.add_vertex(); put(vpm, v, (Point)p); lp += nc; // normal if (has_normals) { if (sscanf(lp, "%f %f %f%n", &n[0], &n[1], &n[2], &nc) == 3) { normals[v] = n; } lp += nc; } // color if (has_colors) { if (sscanf(lp, "%f %f %f%n", &c[0], &c[1], &c[2], &nc) == 3) { if (c[0]>1.0f || c[1]>1.0f || c[2]>1.0f) c *= (1.0/255.0); colors[v] = c; } lp += nc; } // tex coord if (has_texcoords) { items = sscanf(lp, "%f %f%n", &t[0], &t[1], &nc); assert(items == 2); texcoords[v][0] = t[0]; texcoords[v][1] = t[1]; lp += nc; } } // read faces: #N v[1] v[2] ... v[n-1] std::vector<Surface_mesh::Vertex> vertices; for (i=0; i<nF; ++i) { // read line lp = fgets(line, 100, in); // #vertices items = sscanf(lp, "%d%n", (int*)&nV, &nc); assert(items == 1); vertices.resize(nV); lp += nc; // indices for (j=0; j<nV; ++j) { items = sscanf(lp, "%d%n", (int*)&idx, &nc); assert(items == 1); vertices[j] = Surface_mesh::Vertex(idx); lp += nc; } mesh.add_face(vertices); } return true; }
bool read_off_binary(Surface_mesh& mesh, FILE* in, const bool has_normals, const bool has_texcoords, const bool has_colors, NamedParameters& np) { unsigned int i, j, idx; unsigned int nV, nF, nE; Vec3f p, n, c; Vec2f t; Surface_mesh::Vertex v; // binary cannot (yet) read colors if (has_colors) return false; // properties Surface_mesh::Vertex_property<Normal> normals; Surface_mesh::Vertex_property<Texture_coordinate> texcoords; if (has_normals) normals = mesh.vertex_property<Normal>("v:normal"); if (has_texcoords) texcoords = mesh.vertex_property<Texture_coordinate>("v:texcoord"); typename CGAL::Polygon_mesh_processing::GetVertexPointMap<Surface_mesh, NamedParameters>::const_type vpm = choose_param(get_param(np, CGAL::boost::internal_np::vertex_point), get_const_property_map(CGAL::vertex_point, mesh)); // #Vertice, #Faces, #Edges read(in, nV); read(in, nF); read(in, nE); mesh.clear(); mesh.reserve(nV, std::max(3*nV, nE), nF); // read vertices: pos [normal] [color] [texcoord] for (i=0; i<nV && !feof(in); ++i) { // position read(in, p); Surface_mesh::Vertex v = mesh.add_vertex(); put(vpm, v, (Point)p); // normal if (has_normals) { read(in, n); normals[v] = n; } // tex coord if (has_texcoords) { read(in, t); texcoords[v][0] = t[0]; texcoords[v][1] = t[1]; } } // read faces: #N v[1] v[2] ... v[n-1] std::vector<Surface_mesh::Vertex> vertices; for (i=0; i<nF; ++i) { read(in, nV); vertices.resize(nV); for (j=0; j<nV; ++j) { read(in, idx); vertices[j] = Surface_mesh::Vertex(idx); } mesh.add_face(vertices); } return true; }