* lipshitz_octree tutorial example * better comments * revert to shallow default * better names and documentation, dont return I * long long -> std::int64_t * asdd tutorail chapter 10 * precious windows * int64_t -> std::int64_t * batch version * fix template chain from overzealous index typing * bump to c++17 (if constexpr)
238 lines
7.8 KiB
C++
238 lines
7.8 KiB
C++
#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;
|
|
}
|