#include "orient_outward_ao.h" #include "../per_face_normals.h" #include "../doublearea.h" #include "../random_dir.h" #include "EmbreeIntersector.h" #include #include #include template < typename DerivedV, typename DerivedF, typename DerivedC, typename DerivedFF, typename DerivedI> IGL_INLINE void igl::orient_outward_ao( const Eigen::PlainObjectBase & V, const Eigen::PlainObjectBase & F, const Eigen::PlainObjectBase & C, const int min_num_rays_per_component, const int total_num_rays, Eigen::PlainObjectBase & FF, Eigen::PlainObjectBase & I) { using namespace Eigen; using namespace std; assert(C.rows() == F.rows()); assert(F.cols() == 3); assert(V.cols() == 3); // pass both sides of faces to Embree MatrixXi F2; F2.resize(F.rows()*2,F.cols()); F2 << F, F.rowwise().reverse().eval(); EmbreeIntersector ei; ei.init(V,F2); // number of faces const int m = F.rows(); // number of patches const int num_cc = C.maxCoeff()+1; I.resize(num_cc); if(&FF != &F) { FF = F; } // face normal PlainObjectBase N; per_face_normals(V,F,N); // face area Matrix A; doublearea(V,F,A); double area_min = A.minCoeff(); double area_total = A.sum(); // determine number of rays per component according to its area VectorXd area_per_component; area_per_component.setZero(num_cc); for (int f = 0; f < m; ++f) { area_per_component(C(f)) += A(f); } VectorXi num_rays_per_component; num_rays_per_component.setZero(num_cc); for (int c = 0; c < num_cc; ++c) { num_rays_per_component(c) = max(min_num_rays_per_component, static_cast(total_num_rays * area_per_component(c) / area_total)); } // generate all the rays cout << "generating rays... "; uniform_real_distribution rdist; mt19937 prng; prng.seed(time(0)); vector ray_face; vector ray_ori; vector ray_dir; ray_face.reserve(total_num_rays); ray_ori .reserve(total_num_rays); ray_dir .reserve(total_num_rays); for (int c = 0; c < num_cc; ++c) { vector CF; // set of faces per component vector CF_area; for (int f = 0; f < m; ++f) { if (C(f)==c) { CF.push_back(f); CF_area.push_back(static_cast(100 * A(f) / area_min)); } } // discrete distribution for random selection of faces with probability proportional to their areas auto ddist_func = [&] (double i) { return CF_area[static_cast(i)]; }; discrete_distribution ddist(CF.size(), 0, CF.size(), ddist_func); // simple ctor of (Iter, Iter) not provided by the stupid VC11 impl... for (int i = 0; i < num_rays_per_component[c]; ++i) { int f = CF[ddist(prng)]; // select face with probability proportional to face area double t0 = rdist(prng); // random barycentric coordinate double t1 = rdist(prng); double t2 = rdist(prng); double t_sum = t0 + t1 + t2; t0 /= t_sum; t1 /= t_sum; t2 /= t_sum; Vector3d p = t0 * V.row(F(f,0)) // be careful with the index!!! + t1 * V.row(F(f,1)) + t2 * V.row(F(f,2)); Vector3d n = N.row(f); Vector3d d = random_dir(); if (n.dot(d) < 0) { d *= -1; } ray_face.push_back(f); ray_ori .push_back(p); ray_dir .push_back(d); } } // per component accumulation of occlusion distance double dist_large = (V.colwise().maxCoeff() - V.colwise().minCoeff()).norm() * 1000; vector C_occlude_dist_front(num_cc, 0); vector C_occlude_dist_back (num_cc, 0); auto get_dist = [&] (Hit hit, const Vector3d& origin) { Vector3d p0 = V.row(F2(hit.id, 0)); Vector3d p1 = V.row(F2(hit.id, 1)); Vector3d p2 = V.row(F2(hit.id, 2)); Vector3d p = (1 - hit.u - hit.v) * p0 + hit.u * p1 + hit.v * p2; return (p - origin).norm(); }; cout << "shooting rays... "; #pragma omp parallel for for (int i = 0; i < (int)ray_face.size(); ++i) { int f = ray_face[i]; Vector3d o = ray_ori [i]; Vector3d d = ray_dir [i]; int c = C(f); Hit hit_front; Hit hit_back; double dist_front = ei.intersectRay(o, d, hit_front) ? get_dist(hit_front, o) : dist_large; double dist_back = ei.intersectRay(o, -d, hit_back ) ? get_dist(hit_back , o) : dist_large; #pragma omp atomic C_occlude_dist_front[c] += dist_front; #pragma omp atomic C_occlude_dist_back [c] += dist_back; } for(int c = 0;c IGL_INLINE void igl::orient_outward_ao( const Eigen::PlainObjectBase & V, const Eigen::PlainObjectBase & F, const Eigen::PlainObjectBase & C, Eigen::PlainObjectBase & FF, Eigen::PlainObjectBase & I) { return orient_outward_ao(V, F, C, 100, F.rows() * 100, FF, I); } #ifndef IGL_HEADER_ONLY // Explicit template specialization template void igl::orient_outward_ao, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, Eigen::PlainObjectBase > const&, int, int, Eigen::PlainObjectBase >&, Eigen::PlainObjectBase >&); #endif