int main(int argc, char* argv[]) { if(argc != 4) { std::cout << "SDFGen - A utility for converting closed oriented triangle meshes into grid-based signed distance fields.\n"; std::cout << "\nThe output file format is:"; std::cout << "<ni> <nj> <nk>\n"; std::cout << "<origin_x> <origin_y> <origin_z>\n"; std::cout << "<dx>\n"; std::cout << "<value_1> <value_2> <value_3> [...]\n\n"; std::cout << "(ni,nj,nk) are the integer dimensions of the resulting distance field.\n"; std::cout << "(origin_x,origin_y,origin_z) is the 3D position of the grid origin.\n"; std::cout << "<dx> is the grid spacing.\n\n"; std::cout << "<value_n> are the signed distance data values, in ascending order of i, then j, then k.\n"; std::cout << "The output filename will match that of the input, with the OBJ suffix replaced with SDF.\n\n"; std::cout << "Usage: SDFGen <filename> <dx> <padding>\n\n"; std::cout << "Where:\n"; std::cout << "\t<filename> specifies a Wavefront OBJ (text) file representing a *triangle* mesh (no quad or poly meshes allowed). File must use the suffix \".obj\".\n"; std::cout << "\t<dx> specifies the length of grid cell in the resulting distance field.\n"; std::cout << "\t<padding> specifies the number of cells worth of padding between the object bound box and the boundary of the distance field grid. Minimum is 1.\n\n"; exit(-1); } std::string filename(argv[1]); if(filename.size() < 5 || filename.substr(filename.size()-4) != std::string(".obj")) { std::cerr << "Error: Expected OBJ file with filename of the form <name>.obj.\n"; exit(-1); } std::stringstream arg2(argv[2]); float dx; arg2 >> dx; std::stringstream arg3(argv[3]); int padding; arg3 >> padding; if(padding < 1) padding = 1; //start with a massive inside out bound box. Vec3f min_box(std::numeric_limits<float>::max(),std::numeric_limits<float>::max(),std::numeric_limits<float>::max()), max_box(-std::numeric_limits<float>::max(),-std::numeric_limits<float>::max(),-std::numeric_limits<float>::max()); std::cout << "Reading data.\n"; std::ifstream infile(argv[1]); if(!infile) { std::cerr << "Failed to open. Terminating.\n"; exit(-1); } int ignored_lines = 0; std::string line; std::vector<Vec3f> vertList; std::vector<Vec3ui> faceList; while(!infile.eof()) { std::getline(infile, line); //.obj files sometimes contain vertex normals indicated by "vn" if(line.substr(0,1) == std::string("v") && line.substr(0,2) != std::string("vn")){ std::stringstream data(line); char c; Vec3f point; data >> c >> point[0] >> point[1] >> point[2]; vertList.push_back(point); update_minmax(point, min_box, max_box); } else if(line.substr(0,1) == std::string("f")) {
void ModelMesher::generateOffsetSurface(double offset) { if(m->activeNode == nullptr) return; auto n = m->activeNode; n->vis_property["isSmoothShading"].setValue(true); switch(m->QObject::property("meshingIsThick").toInt()){ case 0: break; case 1: offset *= 1.5; break; case 2: offset *= 2; break; } double dx = 0.015; std::vector<SDFGen::Vec3f> vertList; std::vector<SDFGen::Vec3ui> faceList; //start with a massive inside out bound box. SDFGen::Vec3f min_box(std::numeric_limits<float>::max(),std::numeric_limits<float>::max(),std::numeric_limits<float>::max()), max_box(-std::numeric_limits<float>::max(),-std::numeric_limits<float>::max(),-std::numeric_limits<float>::max()); Structure::Curve* curve = dynamic_cast<Structure::Curve*>(n); Structure::Sheet* sheet = dynamic_cast<Structure::Sheet*>(n); //generate "tri-mesh" from skeleton geometry if(curve) { QVector<QVector3D> cpts; for(auto p : curve->controlPoints()) cpts << QVector3D(p[0],p[1],p[2]); cpts = GeometryHelper::uniformResampleCount(cpts, cpts.size() * 5); int vi = 0; for(size_t i = 1; i < cpts.size(); i++){ SDFGen::Vec3f p0 (cpts[i-1][0],cpts[i-1][1],cpts[i-1][2]); SDFGen::Vec3f p1 (cpts[i][0],cpts[i][1],cpts[i][2]); SDFGen::Vec3f p2 = (p0 + p1) * 0.5; vertList.push_back(p0); vertList.push_back(p1); vertList.push_back(p2); faceList.push_back(SDFGen::Vec3ui(vi+0,vi+1,vi+2)); vi += 3; update_minmax(p0, min_box, max_box); update_minmax(p1, min_box, max_box); update_minmax(p2, min_box, max_box); } } if(sheet) { // Build surface geometry if needed auto & surface = sheet->surface; if(surface.quads.empty()){ double resolution = (surface.mCtrlPoint.front().front() - surface.mCtrlPoint.back().back()).norm() * 0.1; surface.generateSurfaceQuads( resolution ); } int vi = 0; for(auto quad : surface.quads) { QVector<SDFGen::Vec3f> p; for(int i = 0; i < 4; i++){ SDFGen::Vec3f point(quad.p[i][0],quad.p[i][1],quad.p[i][2]); p << point; update_minmax(point, min_box, max_box); } vertList.push_back(p[0]); vertList.push_back(p[1]); vertList.push_back(p[2]); faceList.push_back(SDFGen::Vec3ui(vi+0,vi+1,vi+2)); vi += 3; vertList.push_back(p[0]); vertList.push_back(p[2]); vertList.push_back(p[3]); faceList.push_back(SDFGen::Vec3ui(vi+0,vi+1,vi+2)); vi += 3; } } int padding = 10; SDFGen::Vec3f unit(1,1,1); min_box -= unit*padding*dx; max_box += unit*padding*dx; SDFGen::Vec3ui sizes = SDFGen::Vec3ui((max_box - min_box)/dx); if (faceList.empty() || vertList.empty()) return; Array3f phi_grid; SDFGen::make_level_set3(faceList, vertList, min_box, dx, sizes[0], sizes[1], sizes[2], phi_grid, false, offset * 2.0); ScalarVolume volume = initScalarVolume(sizes[0], sizes[1], sizes[2], (sizes[0] + sizes[1] + sizes[2])*dx); for(int i = 0; i < sizes[0]; i++){ for(int j = 0; j < sizes[1]; j++){ for(int k = 0; k < sizes[2]; k++){ volume[k][j][i] = phi_grid(i,j,k); } } } // Mesh surface from volume using marching cubes auto mesh = march(volume, offset); QSharedPointer<SurfaceMeshModel> newMesh = QSharedPointer<SurfaceMeshModel>(new SurfaceMeshModel()); int vi = 0; for(auto tri : mesh){ std::vector<SurfaceMeshModel::Vertex> verts; for(auto p : tri){ Vector3 voxel(p.x, p.y, p.z); Vector3 pos = (voxel * dx) + Vector3(min_box[0],min_box[1],min_box[2]); newMesh->add_vertex(pos); verts.push_back(SurfaceMeshModel::Vertex(vi++)); } newMesh->add_face(verts); } GeometryHelper::meregeVertices<Vector3>(newMesh.data()); newMesh->updateBoundingBox(); newMesh->update_face_normals(); newMesh->update_vertex_normals(); n->property["mesh"].setValue(newMesh); n->property["mesh_filename"].setValue(QString("meshes/%1.obj").arg(n->id)); }