| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- #include <igl/read_triangle_mesh.h>
- #include <igl/lipschitz_octree.h>
- #include <igl/opengl/glfw/Viewer.h>
- #include <igl/AABB.h>
- #include <igl/fast_winding_number.h>
- #include <igl/signed_distance.h>
- #include <igl/grid.h>
- #include <igl/colormap.h>
- #include <igl/matlab_format.h>
- #include <igl/marching_cubes.h>
- #include <igl/unique_sparse_voxel_corners.h>
- #include <igl/get_seconds.h>
- // Edges of a cube box with minimum corner at origin and side length h.
- void box_edges(
- const Eigen::RowVector3d & origin,
- const double h,
- Eigen::MatrixXd & V,
- Eigen::MatrixXi & E)
- {
- V.resize(8,3);
- V<< 0,0,0, 0,0,1, 0,1,0, 0,1,1, 1,0,0, 1,0,1, 1,1,0, 1,1,1;
- V *= h;
- V.rowwise() += origin;
- E.resize(12,2);
- E<< 0,1, 0,2, 0,4, 1,3, 1,5, 2,3, 2,6, 3,7, 4,5, 4,6, 5,7, 6,7;
- }
- // Edges of cubes placed at all corners of octree with origin and cell side length h.
- void all_box_edges(
- const Eigen::RowVector3d & origin,
- const double h,
- const Eigen::Matrix<int,Eigen::Dynamic,3,Eigen::RowMajor> & ijk,
- Eigen::MatrixXd & V,
- Eigen::MatrixXi & E)
- {
- V.resize(ijk.rows()*8,3);
- E.resize(ijk.rows()*12,2);
- for(int c = 0;c<ijk.rows();c++)
- {
- Eigen::MatrixXd Vc;
- Eigen::MatrixXi Ec;
- box_edges(
- origin + h * Eigen::RowVector3d(ijk(c,1),ijk(c,0),ijk(c,2)), h, Vc,Ec);
- V.middleRows(c*8,8) = Vc;
- E.middleRows(c*12,12) = Ec.array() + c*8;
- }
- }
- int main(int argc, char * argv[])
- {
- IGL_TICTOC_LAMBDA;
- // Read mesh
- Eigen::MatrixXd V;
- Eigen::MatrixXi F;
- if(!igl::read_triangle_mesh
- (argc>1?argv[1]: TUTORIAL_SHARED_PATH "/decimated-knight.off",V,F)) {
- std::cout << "Failed to load mesh." << std::endl;
- }
- // Prepare unsigned and signed distance function handles
- igl::AABB<Eigen::MatrixXd,3> aabb;
- aabb.init(V,F);
- const double bbd = (V.colwise().maxCoeff() - V.colwise().minCoeff()).norm();
- double offset_factor = 0.05;
- igl::FastWindingNumberBVH fwn_bvh;
- igl::fast_winding_number(V.cast<float>().eval(), F, 2, fwn_bvh);
- const std::function<double(const Eigen::RowVector3d &)>
- sdf = [&]( const Eigen::RowVector3d & p) -> double
- {
- double w = fast_winding_number(fwn_bvh, 2, p.template cast<float>().eval());
- double s = 1.-2.*std::abs(w);
- int i;
- Eigen::RowVector3d c;
- return s*std::sqrt(aabb.squared_distance(V,F,p,i,c)) - offset_factor*bbd;
- };
- // This can't be auto when using static library or compiler gets confused.
- const std::function<double(const Eigen::RowVector3d &)>
- udf = [&]( const Eigen::RowVector3d & p) -> double
- {
- return std::abs(sdf(p));
- };
- // Centered bounding cube (root of octree) with padding.
- double h0 = (V.colwise().maxCoeff() - V.colwise().minCoeff()).maxCoeff();
- int max_depth = 5;
- // Pad bounding box by max_depth
- //{
- // double leaf_h = h0 / (1 << max_depth);
- // h0 += leaf_h*2;
- //}
- // Pad to root
- h0 *= 2;
- Eigen::RowVector3d origin = 0.5*(V.colwise().maxCoeff() + V.colwise().minCoeff()).array() - 0.5*h0;
- igl::opengl::glfw::Viewer viewer;
- viewer.data().set_mesh(V,F);
- const int main_index = viewer.selected_data_index;
- Eigen::MatrixXd rV;
- Eigen::MatrixXi rE;
- box_edges(origin,h0,rV,rE);
- viewer.data().set_edges(rV,rE,Eigen::RowVector3d(1,1,0));
- viewer.data().set_face_based(true);
- viewer.data().show_lines = false;
- viewer.append_mesh();
- const int aux_index = viewer.selected_data_index;
- const auto update = [&]()
- {
- if(max_depth<=8)
- {
- // For comparison, how long would it take to compute on the dense grid
- tictoc();
- Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> GV;
- Eigen::RowVector3i res(1<<max_depth,1<<max_depth,1<<max_depth);
- igl::grid(res,GV);
- GV *= h0;
- GV.rowwise() += origin;
- Eigen::VectorXd GS(GV.rows());
- igl::parallel_for( GV.rows(), [&](const int u)
- {
- // evaluate the function at the corner
- GS(u) = sdf(GV.row(u));
- },1000);
- printf(" n³ grid: %0.7f seconds\n",tictoc());
- }else
- {
- printf(" n³ grid: [omitted]\n");
- }
- tictoc();
- // Identify the octree cells that could contain the surface
- Eigen::Matrix<int,Eigen::Dynamic,3,Eigen::RowMajor> ijk;
- igl::lipschitz_octree(origin,h0,max_depth,udf,ijk);
- printf(" lipschitz_octree: %0.7f seconds\n",tictoc());
- // Gather the corners of those leaf cells
- const double h = h0 / (1 << max_depth);
- Eigen::Matrix<int,Eigen::Dynamic,8,Eigen::RowMajor> J;
- Eigen::Matrix<int,Eigen::Dynamic,3,Eigen::RowMajor> unique_ijk;
- Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> unique_corner_positions;
- igl::unique_sparse_voxel_corners(origin,h0,max_depth,ijk,unique_ijk,J,unique_corner_positions);
- printf("unique_sparse_voxel_corners: %0.7f seconds\n",tictoc());
- /// Evaluate the signed distance function at the corners
- Eigen::VectorXd S(unique_corner_positions.rows());
- //for(int u = 0;u<unique_corner_positions.rows();u++)
- igl::parallel_for(
- unique_corner_positions.rows(),
- [&](const int u)
- {
- // evaluate the function at the corner
- S(u) = sdf(unique_corner_positions.row(u));
- },1000);
- printf(" sdf: %0.7f seconds\n",tictoc());
- // Run marching cubes on the sparse set of leaf cells
- Eigen::Matrix<double,Eigen::Dynamic,3> mV;
- Eigen::Matrix<int,Eigen::Dynamic,3> mF;
- igl::marching_cubes( S,unique_corner_positions,J, 0, mV,mF);
- printf(" marching_cubes: %0.7f seconds\n",tictoc());
- // Visualize
- viewer.data(aux_index).clear();
- viewer.data(aux_index).set_mesh(mV,mF);
- viewer.data(aux_index).set_face_based(true);
- viewer.data().show_lines = max_depth <= 7;
- viewer.data(aux_index).set_colors(Eigen::RowVector3d(0.941176,0.305882,0.282353));
- if(max_depth <= 7)
- {
- // If tree is shallow enough, visualize the edges of the octree cells
- Eigen::MatrixXd gV;
- Eigen::MatrixXi gE;
- all_box_edges(origin,h,ijk,gV,gE);
- viewer.data(aux_index).set_edges(gV,gE,Eigen::RowVector3d(1,1,1));
- Eigen::MatrixXd point_colors;
- igl::colormap(
- igl::ColorMapType::COLOR_MAP_TYPE_TURBO,
- S,-S.array().abs().maxCoeff(),S.array().abs().maxCoeff(),
- point_colors);
- viewer.data(aux_index).add_points(unique_corner_positions,point_colors);
- viewer.data(aux_index).point_size = 8;
- }
- printf(" |ijk| : %ld\n",ijk.rows());
- };
- viewer.callback_key_pressed =
- [&](igl::opengl::glfw::Viewer & viewer, unsigned int key, int mod)->bool
- {
- switch(key) {
- default:
- return false;
- case '=':
- case '+':
- case '-':
- case '_':
- max_depth = std::max(0,max_depth + ((key == '-'||key=='_') ? -1 : 1));
- printf("max_depth = %d\n",max_depth);
- update();
- break;
- case '}':
- case '{':
- offset_factor =
- std::min(std::max(-1.0,offset_factor+(key=='{'?-0.01:0.01)),1.0);
- printf("offset_factor = %f\n",offset_factor);
- update();
- break;
- }
- return true;
- };
- update();
- // Trivial batched function
- const std::function<Eigen::VectorXd(const Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> &)>
- udf_batch = [&](const Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> & P) -> Eigen::VectorXd
- {
- Eigen::VectorXd U(P.rows());
- for(int i = 0;i<P.rows();i++)
- {
- U(i) = udf(P.row(i));
- }
- return U;
- };
- {
- // The batched version should produce the same size output
- Eigen::Matrix<int,Eigen::Dynamic,3,Eigen::RowMajor> ijk_batch;
- igl::lipschitz_octree<true>(origin,h0,max_depth,udf_batch,ijk_batch);
- printf(" |ijk_batch| : %ld\n",ijk_batch.rows());
- }
- std::cout << R"(Usage:
- -,+ Decrease,Increase the depth of the octree and recompute
- {,} Decrease,Increase the offset factor for the signed distance function
- )";
- viewer.launch();
- return 0;
- }
|