| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- #include <igl/read_triangle_mesh.h>
- #include <igl/box_simplices.h>
- #include <igl/eytzinger_aabb.h>
- #include <igl/lipschitz_octree.h>
- #include <igl/unique_sparse_voxel_corners.h>
- #include <igl/eytzinger_aabb_sdf.h>
- #include <igl/unique.h>
- #include <igl/readDMAT.h>
- #include <igl/grid.h>
- #include <igl/edges.h>
- #include <igl/marching_cubes.h>
- #include <igl/parallel_for.h>
- #include <igl/opengl/glfw/Viewer.h>
- #include <igl/matlab_format.h>
- #include <igl/variable_radius_offset.h>
- #include <igl/get_seconds.h>
- #include <igl/SphereMeshWedge.h>
- #include <limits>
- #include <Eigen/Geometry>
- // What kind of offset is being computed?
- enum class OffsetType
- {
- EDGE_OFFSET = 0,
- TRIANGLE_OFFSET = 1,
- VARIABLE_RADIUS_OFFSET = 2
- } offset = OffsetType::VARIABLE_RADIUS_OFFSET;
- ///////////////////////////////////////////////////////////////////////
- /// Some helper functions for triangle and line segment distance
- ///////////////////////////////////////////////////////////////////////
- template <typename T> inline T sign(const T & x){ return (x>T(0)) - (x<T(0)); }
- inline double dot2(const Eigen::RowVector3d & v)
- {
- return v.dot(v);
- }
- inline double clamp(const double & x, const double & a, const double & b)
- {
- return std::max(a, std::min(b, x));
- }
- // https://iquilezles.org/articles/triangledistance/
- template <typename T1, typename T2, typename T3, typename Tp>
- typename Tp::Scalar udTriangle(
- const T1 & v1, const T2 & v2, const T3 & v3, const Tp & p)
- {
- using vec3 = Eigen::Matrix<typename Tp::Scalar, 3, 1>;
- // prepare data
- vec3 v21 = v2 - v1; vec3 p1 = p - v1;
- vec3 v32 = v3 - v2; vec3 p2 = p - v2;
- vec3 v13 = v1 - v3; vec3 p3 = p - v3;
- vec3 nor = v21.cross( v13 );
- return sqrt( // inside/outside test
- (sign(v21.cross(nor).dot(p1)) +
- sign(v32.cross(nor).dot(p2)) +
- sign(v13.cross(nor).dot(p3))<2.0)
- ?
- // 3 edges
- std::min( std::min(
- dot2(v21*clamp(v21.dot(p1)/dot2(v21),0.0,1.0)-p1),
- dot2(v32*clamp(v32.dot(p2)/dot2(v32),0.0,1.0)-p2) ),
- dot2(v13*clamp(v13.dot(p3)/dot2(v13),0.0,1.0)-p3) )
- :
- // 1 face
- nor.dot(p1)*nor.dot(p1)/dot2(nor) );
- }
-
- template <typename Tp, typename Ta, typename Tb>
- typename Tp::Scalar udLineSegment(const Tp & p, const Ta & a, const Tb & b)
- {
- using vec3 = Eigen::Matrix<typename Tp::Scalar, 3, 1>;
- vec3 pa = p - a;
- vec3 ba = b - a;
- const auto h = clamp( pa.dot(ba)/ba.dot(ba), 0.0, 1.0 );
- return ( pa - ba*h ).norm();
- }
- int main(int argc, char * argv[])
- {
- IGL_TICTOC_LAMBDA;
- // Octree depth
- int max_depth = 7;
- // Read mesh
- Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> V;
- Eigen::Matrix<int,Eigen::Dynamic,3,Eigen::RowMajor> 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;
- }
- // Normalize
- V.rowwise() -= 0.5* (V.colwise().maxCoeff() + V.colwise().minCoeff());
- const double scale = 0.8/V.array().abs().maxCoeff();
- V *= scale;
- // Per-vertex radius for VARIABLE_RADIUS_OFFSET
- // if second arg exists read R from .dmat
- Eigen::VectorXd R;
- if(argc>2)
- {
- igl::readDMAT( argv[2],R);
- R *= scale;
- }else
- {
- R = V.col(1);
- R.array() -= R.minCoeff();
- R /= R.maxCoeff();
- R.array() += 1.0;
- R = (R.array().pow(4)).eval();
- R *= 0.01;
- }
- ////////////////////////////////////////////////////////////////////////
- /// Box up simplices (and, for VARIABLE_RADIUS_OFFSET, precompute data)
- ////////////////////////////////////////////////////////////////////////
- tictoc();
- Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> PB1,PB2;
- Eigen::Matrix<int,Eigen::Dynamic,2,Eigen::RowMajor> E;
- igl::edges(F,E);
- double isovalue = 0.02;
- std::function<double(const Eigen::RowVector3d &,const int i)> primitive;
- std::vector<igl::SphereMeshWedge<double>> data;
- switch(offset)
- {
- case OffsetType::EDGE_OFFSET:
- {
- igl::box_simplices(V,E,PB1,PB2);
- primitive = [&](const Eigen::RowVector3d & p, const int i)
- {
- return udLineSegment(p, V.row(E(i,0)), V.row(E(i,1)));
- };
- break;
- }
- case OffsetType::TRIANGLE_OFFSET:
- {
- igl::box_simplices(V,F,PB1,PB2);
- primitive = [&](const Eigen::RowVector3d & p, const int i)
- {
- return udTriangle( V.row(F(i,0)), V.row(F(i,1)), V.row(F(i,2)), p);
- };
- break;
- }
- case OffsetType::VARIABLE_RADIUS_OFFSET:
- {
- isovalue = 0;
- igl::variable_radius_offset(V,F,R,PB1,PB2,data,primitive);
- break;
- }
- }
- printf("%-20s: %g secs\n","PB",tictoc());
- ////////////////////////////////////////////////////////////////////////
- /// Put boxes into eytzinger AABB tree
- ////////////////////////////////////////////////////////////////////////
- tictoc();
- Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> B1,B2;
- Eigen::VectorXi leaf;
- igl::eytzinger_aabb(PB1,PB2,B1,B2,leaf);
- printf("%-20s: %g secs\n","eytzinger_aabb",tictoc());
- //{
- // Eigen::VectorXi U;
- // igl::unique(leaf,U);
- // printf("%d → %d\n",PB1.rows(),U.size()-2);
- //}
- igl::opengl::glfw::Viewer viewer;
- Eigen::Matrix<double,Eigen::Dynamic,3> mV,oV;
- Eigen::Matrix<int,Eigen::Dynamic,3> mF,oF;
- viewer.data().set_mesh(V,F);
- viewer.data().set_face_based(true);
- viewer.data().show_lines = false;
- if(offset == OffsetType::VARIABLE_RADIUS_OFFSET)
- {
- viewer.data().set_colors(R);
- }
- viewer.append_mesh();
- const int m_index = viewer.selected_data_index;
- viewer.append_mesh();
- const int o_index = viewer.selected_data_index;
- viewer.data(m_index).set_face_based(true);
- viewer.data(m_index).show_lines = true;
- viewer.data(o_index).set_face_based(true);
- viewer.data(o_index).show_lines = false;
- const auto update = [&]()
- {
- ////////////////////////////////////////////////////////////////////////
- /// If the grid isn't that big then compute a dense M.C. mesh
- /// Call batched eytzinger_aabb_sdf directly on grid nodes.
- ////////////////////////////////////////////////////////////////////////
- tictoc();
- const int ns = (1 << max_depth) + 1;
- if(ns <= 128+1)
- {
- Eigen::RowVector3i res(ns,ns,ns);
- Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> GV;
- igl::grid(res,GV);
- GV *= 2;
- GV.array() -= 1;
- Eigen::VectorXd S;
- igl::eytzinger_aabb_sdf(GV,primitive,B1,B2,leaf,S);
- printf("%-20s: %g secs\n","eytzinger_aabb_sdf",tictoc());
- tictoc();
- igl::marching_cubes(S,GV,res(0),res(1),res(2),isovalue,mV,mF);
- printf("%-20s: %g secs\n","marching_cubes",tictoc());
- }else
- {
- mV.resize(0,3);
- mF.resize(0,3);
- }
- ////////////////////////////////////////////////////////////////////////
- /// Prepare an unsigned distance and signed distance function handle.
- ////////////////////////////////////////////////////////////////////////
- tictoc();
- const std::function<double(const Eigen::RowVector3d &)>
- sdf = [&](const Eigen::RowVector3d & p) -> double
- {
- const std::function<double(const int)> primitive_p = [&](const int j)
- {
- return primitive(p,j);
- };
- double f;
- igl::eytzinger_aabb_sdf(p,primitive_p,B1,B2,leaf,f);
- return f - isovalue;
- };
- const std::function<double(const Eigen::RowVector3d &)>
- udf = [&](const Eigen::RowVector3d & p) -> double
- {
- return std::abs(sdf(p));
- };
- ////////////////////////////////////////////////////////////////////////
- /// Use udf to build sparse voxel octree around the zero level set
- ////////////////////////////////////////////////////////////////////////
- Eigen::RowVector3d origin(-1,-1,-1);
- const double h0 = 2;
- Eigen::Matrix<int,Eigen::Dynamic,3,Eigen::RowMajor> ijk;
- igl::lipschitz_octree( origin,h0,max_depth,udf,ijk);
- printf("%-20s: %g secs\n","lipschitz_octree",tictoc());
- ////////////////////////////////////////////////////////////////////////
- /// Gather the corners of those leaf cells, compute sdf there and run
- /// (sparse) marching cubes
- ////////////////////////////////////////////////////////////////////////
- {
- 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());
- printf("%-20s: %g secs\n","unique_sparse_vo...",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());
- printf("%-20s: %g secs\n","sdf",tictoc());
- // Run marching cubes on the sparse set of leaf cells
- igl::marching_cubes( S,unique_corner_positions,J, 0, oV,oF);
- //printf(" marching_cubes: %0.7f seconds\n",tictoc());
- printf("%-20s: %g secs\n","marching_cubes",tictoc());
- }
- viewer.data(m_index).clear();
- viewer.data(m_index).set_mesh(mV,mF);
- viewer.data(m_index).set_colors(Eigen::RowVector3d(0.8,0.2,0.2));
- viewer.data(o_index).clear();
- viewer.data(o_index).set_mesh(oV,oF);
- viewer.data(o_index).set_colors(Eigen::RowVector3d(0.2,0.8,0.2));
- };
- 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;
- }
- return true;
- };
- update();
- viewer.launch();
- return 0;
- }
|