2
0

main.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. #include <igl/read_triangle_mesh.h>
  2. #include <igl/box_simplices.h>
  3. #include <igl/eytzinger_aabb.h>
  4. #include <igl/lipschitz_octree.h>
  5. #include <igl/unique_sparse_voxel_corners.h>
  6. #include <igl/eytzinger_aabb_sdf.h>
  7. #include <igl/unique.h>
  8. #include <igl/readDMAT.h>
  9. #include <igl/grid.h>
  10. #include <igl/edges.h>
  11. #include <igl/marching_cubes.h>
  12. #include <igl/parallel_for.h>
  13. #include <igl/opengl/glfw/Viewer.h>
  14. #include <igl/matlab_format.h>
  15. #include <igl/variable_radius_offset.h>
  16. #include <igl/get_seconds.h>
  17. #include <igl/SphereMeshWedge.h>
  18. #include <limits>
  19. #include <Eigen/Geometry>
  20. // What kind of offset is being computed?
  21. enum class OffsetType
  22. {
  23. EDGE_OFFSET = 0,
  24. TRIANGLE_OFFSET = 1,
  25. VARIABLE_RADIUS_OFFSET = 2
  26. } offset = OffsetType::VARIABLE_RADIUS_OFFSET;
  27. ///////////////////////////////////////////////////////////////////////
  28. /// Some helper functions for triangle and line segment distance
  29. ///////////////////////////////////////////////////////////////////////
  30. template <typename T> inline T sign(const T & x){ return (x>T(0)) - (x<T(0)); }
  31. inline double dot2(const Eigen::RowVector3d & v)
  32. {
  33. return v.dot(v);
  34. }
  35. inline double clamp(const double & x, const double & a, const double & b)
  36. {
  37. return std::max(a, std::min(b, x));
  38. }
  39. // https://iquilezles.org/articles/triangledistance/
  40. template <typename T1, typename T2, typename T3, typename Tp>
  41. typename Tp::Scalar udTriangle(
  42. const T1 & v1, const T2 & v2, const T3 & v3, const Tp & p)
  43. {
  44. using vec3 = Eigen::Matrix<typename Tp::Scalar, 3, 1>;
  45. // prepare data
  46. vec3 v21 = v2 - v1; vec3 p1 = p - v1;
  47. vec3 v32 = v3 - v2; vec3 p2 = p - v2;
  48. vec3 v13 = v1 - v3; vec3 p3 = p - v3;
  49. vec3 nor = v21.cross( v13 );
  50. return sqrt( // inside/outside test
  51. (sign(v21.cross(nor).dot(p1)) +
  52. sign(v32.cross(nor).dot(p2)) +
  53. sign(v13.cross(nor).dot(p3))<2.0)
  54. ?
  55. // 3 edges
  56. std::min( std::min(
  57. dot2(v21*clamp(v21.dot(p1)/dot2(v21),0.0,1.0)-p1),
  58. dot2(v32*clamp(v32.dot(p2)/dot2(v32),0.0,1.0)-p2) ),
  59. dot2(v13*clamp(v13.dot(p3)/dot2(v13),0.0,1.0)-p3) )
  60. :
  61. // 1 face
  62. nor.dot(p1)*nor.dot(p1)/dot2(nor) );
  63. }
  64. template <typename Tp, typename Ta, typename Tb>
  65. typename Tp::Scalar udLineSegment(const Tp & p, const Ta & a, const Tb & b)
  66. {
  67. using vec3 = Eigen::Matrix<typename Tp::Scalar, 3, 1>;
  68. vec3 pa = p - a;
  69. vec3 ba = b - a;
  70. const auto h = clamp( pa.dot(ba)/ba.dot(ba), 0.0, 1.0 );
  71. return ( pa - ba*h ).norm();
  72. }
  73. int main(int argc, char * argv[])
  74. {
  75. IGL_TICTOC_LAMBDA;
  76. // Octree depth
  77. int max_depth = 7;
  78. // Read mesh
  79. Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> V;
  80. Eigen::Matrix<int,Eigen::Dynamic,3,Eigen::RowMajor> F;
  81. if(!igl::read_triangle_mesh
  82. (argc>1?argv[1]: TUTORIAL_SHARED_PATH "/decimated-knight.off",V,F)) {
  83. std::cout << "Failed to load mesh." << std::endl;
  84. }
  85. // Normalize
  86. V.rowwise() -= 0.5* (V.colwise().maxCoeff() + V.colwise().minCoeff());
  87. const double scale = 0.8/V.array().abs().maxCoeff();
  88. V *= scale;
  89. // Per-vertex radius for VARIABLE_RADIUS_OFFSET
  90. // if second arg exists read R from .dmat
  91. Eigen::VectorXd R;
  92. if(argc>2)
  93. {
  94. igl::readDMAT( argv[2],R);
  95. R *= scale;
  96. }else
  97. {
  98. R = V.col(1);
  99. R.array() -= R.minCoeff();
  100. R /= R.maxCoeff();
  101. R.array() += 1.0;
  102. R = (R.array().pow(4)).eval();
  103. R *= 0.01;
  104. }
  105. ////////////////////////////////////////////////////////////////////////
  106. /// Box up simplices (and, for VARIABLE_RADIUS_OFFSET, precompute data)
  107. ////////////////////////////////////////////////////////////////////////
  108. tictoc();
  109. Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> PB1,PB2;
  110. Eigen::Matrix<int,Eigen::Dynamic,2,Eigen::RowMajor> E;
  111. igl::edges(F,E);
  112. double isovalue = 0.02;
  113. std::function<double(const Eigen::RowVector3d &,const int i)> primitive;
  114. std::vector<igl::SphereMeshWedge<double>> data;
  115. switch(offset)
  116. {
  117. case OffsetType::EDGE_OFFSET:
  118. {
  119. igl::box_simplices(V,E,PB1,PB2);
  120. primitive = [&](const Eigen::RowVector3d & p, const int i)
  121. {
  122. return udLineSegment(p, V.row(E(i,0)), V.row(E(i,1)));
  123. };
  124. break;
  125. }
  126. case OffsetType::TRIANGLE_OFFSET:
  127. {
  128. igl::box_simplices(V,F,PB1,PB2);
  129. primitive = [&](const Eigen::RowVector3d & p, const int i)
  130. {
  131. return udTriangle( V.row(F(i,0)), V.row(F(i,1)), V.row(F(i,2)), p);
  132. };
  133. break;
  134. }
  135. case OffsetType::VARIABLE_RADIUS_OFFSET:
  136. {
  137. isovalue = 0;
  138. igl::variable_radius_offset(V,F,R,PB1,PB2,data,primitive);
  139. break;
  140. }
  141. }
  142. printf("%-20s: %g secs\n","PB",tictoc());
  143. ////////////////////////////////////////////////////////////////////////
  144. /// Put boxes into eytzinger AABB tree
  145. ////////////////////////////////////////////////////////////////////////
  146. tictoc();
  147. Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> B1,B2;
  148. Eigen::VectorXi leaf;
  149. igl::eytzinger_aabb(PB1,PB2,B1,B2,leaf);
  150. printf("%-20s: %g secs\n","eytzinger_aabb",tictoc());
  151. //{
  152. // Eigen::VectorXi U;
  153. // igl::unique(leaf,U);
  154. // printf("%d → %d\n",PB1.rows(),U.size()-2);
  155. //}
  156. igl::opengl::glfw::Viewer viewer;
  157. Eigen::Matrix<double,Eigen::Dynamic,3> mV,oV;
  158. Eigen::Matrix<int,Eigen::Dynamic,3> mF,oF;
  159. viewer.data().set_mesh(V,F);
  160. viewer.data().set_face_based(true);
  161. viewer.data().show_lines = false;
  162. if(offset == OffsetType::VARIABLE_RADIUS_OFFSET)
  163. {
  164. viewer.data().set_colors(R);
  165. }
  166. viewer.append_mesh();
  167. const int m_index = viewer.selected_data_index;
  168. viewer.append_mesh();
  169. const int o_index = viewer.selected_data_index;
  170. viewer.data(m_index).set_face_based(true);
  171. viewer.data(m_index).show_lines = true;
  172. viewer.data(o_index).set_face_based(true);
  173. viewer.data(o_index).show_lines = false;
  174. const auto update = [&]()
  175. {
  176. ////////////////////////////////////////////////////////////////////////
  177. /// If the grid isn't that big then compute a dense M.C. mesh
  178. /// Call batched eytzinger_aabb_sdf directly on grid nodes.
  179. ////////////////////////////////////////////////////////////////////////
  180. tictoc();
  181. const int ns = (1 << max_depth) + 1;
  182. if(ns <= 128+1)
  183. {
  184. Eigen::RowVector3i res(ns,ns,ns);
  185. Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> GV;
  186. igl::grid(res,GV);
  187. GV *= 2;
  188. GV.array() -= 1;
  189. Eigen::VectorXd S;
  190. igl::eytzinger_aabb_sdf(GV,primitive,B1,B2,leaf,S);
  191. printf("%-20s: %g secs\n","eytzinger_aabb_sdf",tictoc());
  192. tictoc();
  193. igl::marching_cubes(S,GV,res(0),res(1),res(2),isovalue,mV,mF);
  194. printf("%-20s: %g secs\n","marching_cubes",tictoc());
  195. }else
  196. {
  197. mV.resize(0,3);
  198. mF.resize(0,3);
  199. }
  200. ////////////////////////////////////////////////////////////////////////
  201. /// Prepare an unsigned distance and signed distance function handle.
  202. ////////////////////////////////////////////////////////////////////////
  203. tictoc();
  204. const std::function<double(const Eigen::RowVector3d &)>
  205. sdf = [&](const Eigen::RowVector3d & p) -> double
  206. {
  207. const std::function<double(const int)> primitive_p = [&](const int j)
  208. {
  209. return primitive(p,j);
  210. };
  211. double f;
  212. igl::eytzinger_aabb_sdf(p,primitive_p,B1,B2,leaf,f);
  213. return f - isovalue;
  214. };
  215. const std::function<double(const Eigen::RowVector3d &)>
  216. udf = [&](const Eigen::RowVector3d & p) -> double
  217. {
  218. return std::abs(sdf(p));
  219. };
  220. ////////////////////////////////////////////////////////////////////////
  221. /// Use udf to build sparse voxel octree around the zero level set
  222. ////////////////////////////////////////////////////////////////////////
  223. Eigen::RowVector3d origin(-1,-1,-1);
  224. const double h0 = 2;
  225. Eigen::Matrix<int,Eigen::Dynamic,3,Eigen::RowMajor> ijk;
  226. igl::lipschitz_octree( origin,h0,max_depth,udf,ijk);
  227. printf("%-20s: %g secs\n","lipschitz_octree",tictoc());
  228. ////////////////////////////////////////////////////////////////////////
  229. /// Gather the corners of those leaf cells, compute sdf there and run
  230. /// (sparse) marching cubes
  231. ////////////////////////////////////////////////////////////////////////
  232. {
  233. tictoc();
  234. // Gather the corners of those leaf cells
  235. const double h = h0 / (1 << max_depth);
  236. Eigen::Matrix<int,Eigen::Dynamic,8,Eigen::RowMajor> J;
  237. Eigen::Matrix<int,Eigen::Dynamic,3,Eigen::RowMajor> unique_ijk;
  238. Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> unique_corner_positions;
  239. igl::unique_sparse_voxel_corners(origin,h0,max_depth,ijk,unique_ijk,J,unique_corner_positions);
  240. //printf("unique_sparse_voxel_corners: %0.7f seconds\n",tictoc());
  241. printf("%-20s: %g secs\n","unique_sparse_vo...",tictoc());
  242. /// Evaluate the signed distance function at the corners
  243. Eigen::VectorXd S(unique_corner_positions.rows());
  244. //for(int u = 0;u<unique_corner_positions.rows();u++)
  245. igl::parallel_for(
  246. unique_corner_positions.rows(),
  247. [&](const int u)
  248. {
  249. // evaluate the function at the corner
  250. S(u) = sdf(unique_corner_positions.row(u));
  251. },1000);
  252. //printf(" sdf: %0.7f seconds\n",tictoc());
  253. printf("%-20s: %g secs\n","sdf",tictoc());
  254. // Run marching cubes on the sparse set of leaf cells
  255. igl::marching_cubes( S,unique_corner_positions,J, 0, oV,oF);
  256. //printf(" marching_cubes: %0.7f seconds\n",tictoc());
  257. printf("%-20s: %g secs\n","marching_cubes",tictoc());
  258. }
  259. viewer.data(m_index).clear();
  260. viewer.data(m_index).set_mesh(mV,mF);
  261. viewer.data(m_index).set_colors(Eigen::RowVector3d(0.8,0.2,0.2));
  262. viewer.data(o_index).clear();
  263. viewer.data(o_index).set_mesh(oV,oF);
  264. viewer.data(o_index).set_colors(Eigen::RowVector3d(0.2,0.8,0.2));
  265. };
  266. viewer.callback_key_pressed =
  267. [&](igl::opengl::glfw::Viewer & viewer, unsigned int key, int mod)->bool
  268. {
  269. switch(key) {
  270. default:
  271. return false;
  272. case '=':
  273. case '+':
  274. case '-':
  275. case '_':
  276. max_depth = std::max(0,max_depth + ((key == '-'||key=='_') ? -1 : 1));
  277. printf("max_depth = %d\n",max_depth);
  278. update();
  279. break;
  280. }
  281. return true;
  282. };
  283. update();
  284. viewer.launch();
  285. return 0;
  286. }