Fix of paint on supports don't work for object that has been scaled up #6718

The triangle-ray intersection function used a hard coded epsilon,
which did not work for triangle meshes, that were either too small
or too large. Newly the epsilon may be provided to the AABBTreeIndirect
search functions externally and IndexedMesh calculates a suitable
epsilon on demand from an average triangle mesh edge length.
This commit is contained in:
Vojtech Bubnik 2021-08-27 21:04:11 +02:00
parent d9f2fd7501
commit 1c76df89ea
6 changed files with 127 additions and 50 deletions

View File

@ -15,11 +15,6 @@
#include "Utils.hpp" // for next_highest_power_of_2()
extern "C"
{
// Ray-Triangle Intersection Test Routines by Tomas Moller, May 2000
#include <igl/raytri.c>
}
// Definition of the ray intersection hit structure.
#include <igl/Hit.h>
@ -231,6 +226,9 @@ namespace detail {
const VectorType origin;
const VectorType dir;
const VectorType invdir;
// epsilon for ray-triangle intersection, see intersect_triangle1()
const double eps;
};
template<typename VertexType, typename IndexedFaceType, typename TreeType, typename VectorType>
@ -283,44 +281,91 @@ namespace detail {
return tmin < t1 && tmax > t0;
}
// The following intersect_triangle() is derived from raytri.c routine intersect_triangle1()
// Ray-Triangle Intersection Test Routines
// Different optimizations of my and Ben Trumbore's
// code from journals of graphics tools (JGT)
// http://www.acm.org/jgt/
// by Tomas Moller, May 2000
template<typename V, typename W>
std::enable_if_t<std::is_same<typename V::Scalar, double>::value && std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
return intersect_triangle1(const_cast<double*>(origin.data()), const_cast<double*>(dir.data()),
const_cast<double*>(v0.data()), const_cast<double*>(v1.data()), const_cast<double*>(v2.data()),
&t, &u, &v);
std::enable_if_t<std::is_same<typename V::Scalar, double>::value&& std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &orig, const V &dir, const W &vert0, const W &vert1, const W &vert2, double &t, double &u, double &v, double eps)
{
// find vectors for two edges sharing vert0
const V edge1 = vert1 - vert0;
const V edge2 = vert2 - vert0;
// begin calculating determinant - also used to calculate U parameter
const V pvec = dir.cross(edge2);
// if determinant is near zero, ray lies in plane of triangle
const double det = edge1.dot(pvec);
V qvec;
if (det > eps) {
// calculate distance from vert0 to ray origin
V tvec = orig - vert0;
// calculate U parameter and test bounds
u = tvec.dot(pvec);
if (u < 0.0 || u > det)
return false;
// prepare to test V parameter
qvec = tvec.cross(edge1);
// calculate V parameter and test bounds
v = dir.dot(qvec);
if (v < 0.0 || u + v > det)
return false;
} else if (det < -eps) {
// calculate distance from vert0 to ray origin
V tvec = orig - vert0;
// calculate U parameter and test bounds
u = tvec.dot(pvec);
if (u > 0.0 || u < det)
return false;
// prepare to test V parameter
qvec = tvec.cross(edge1);
// calculate V parameter and test bounds
v = dir.dot(qvec);
if (v > 0.0 || u + v < det)
return false;
} else
// ray is parallel to the plane of the triangle
return false;
double inv_det = 1.0 / det;
// calculate t, ray intersects triangle
t = edge2.dot(qvec) * inv_det;
u *= inv_det;
v *= inv_det;
return true;
}
template<typename V, typename W>
std::enable_if_t<std::is_same<typename V::Scalar, double>::value && !std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
using Vector = Eigen::Matrix<double, 3, 1>;
Vector w0 = v0.template cast<double>();
Vector w1 = v1.template cast<double>();
Vector w2 = v2.template cast<double>();
return intersect_triangle1(const_cast<double*>(origin.data()), const_cast<double*>(dir.data()),
w0.data(), w1.data(), w2.data(), &t, &u, &v);
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v, double eps) {
return intersect_triangle(origin, dir, v0.template cast<double>(), v1.template cast<double>(), v2.template cast<double>(), t, u, v, eps);
}
template<typename V, typename W>
std::enable_if_t<! std::is_same<typename V::Scalar, double>::value && std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
using Vector = Eigen::Matrix<double, 3, 1>;
Vector o = origin.template cast<double>();
Vector d = dir.template cast<double>();
return intersect_triangle1(o.data(), d.data(), const_cast<double*>(v0.data()), const_cast<double*>(v1.data()), const_cast<double*>(v2.data()), &t, &u, &v);
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v, double eps) {
return intersect_triangle(origin.template cast<double>(), dir.template cast<double>(), v0, v1, v2, t, u, v, eps);
}
template<typename V, typename W>
std::enable_if_t<! std::is_same<typename V::Scalar, double>::value && ! std::is_same<typename W::Scalar, double>::value, bool>
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v) {
using Vector = Eigen::Matrix<double, 3, 1>;
Vector o = origin.template cast<double>();
Vector d = dir.template cast<double>();
Vector w0 = v0.template cast<double>();
Vector w1 = v1.template cast<double>();
Vector w2 = v2.template cast<double>();
return intersect_triangle1(o.data(), d.data(), w0.data(), w1.data(), w2.data(), &t, &u, &v);
intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W &v2, double &t, double &u, double &v, double eps) {
return intersect_triangle(origin.template cast<double>(), dir.template cast<double>(), v0.template cast<double>(), v1.template cast<double>(), v2.template cast<double>(), t, u, v, eps);
}
template<typename Tree>
double intersect_triangle_epsilon(const Tree &tree) {
double eps = 0.000001;
if (! tree.empty()) {
const typename Tree::BoundingBox &bbox = tree.nodes().front().bbox;
double l = (bbox.max() - bbox.min()).cwiseMax();
if (l > 0)
eps /= (l * l);
}
return eps;
}
template<typename RayIntersectorType, typename Scalar>
@ -343,7 +388,7 @@ namespace detail {
if (intersect_triangle(
ray_intersector.origin, ray_intersector.dir,
ray_intersector.vertices[face(0)], ray_intersector.vertices[face(1)], ray_intersector.vertices[face(2)],
t, u, v)
t, u, v, ray_intersector.eps)
&& t > 0.) {
hit = igl::Hit { int(node.idx), -1, float(u), float(v), float(t) };
return true;
@ -388,7 +433,7 @@ namespace detail {
if (intersect_triangle(
ray_intersector.origin, ray_intersector.dir,
ray_intersector.vertices[face(0)], ray_intersector.vertices[face(1)], ray_intersector.vertices[face(2)],
t, u, v)
t, u, v, ray_intersector.eps)
&& t > 0.) {
ray_intersector.hits.emplace_back(igl::Hit{ int(node.idx), -1, float(u), float(v), float(t) });
}
@ -623,12 +668,15 @@ inline bool intersect_ray_first_hit(
// Direction of the ray.
const VectorType &dir,
// First intersection of the ray with the indexed triangle set.
igl::Hit &hit)
igl::Hit &hit,
// Epsilon for the ray-triangle intersection, it should be proportional to an average triangle edge length.
const double eps = 0.000001)
{
using Scalar = typename VectorType::Scalar;
auto ray_intersector = detail::RayIntersector<VertexType, IndexedFaceType, TreeType, VectorType> {
vertices, faces, tree,
origin, dir, VectorType(dir.cwiseInverse())
origin, dir, VectorType(dir.cwiseInverse()),
eps
};
return ! tree.empty() && detail::intersect_ray_recursive_first_hit(
ray_intersector, size_t(0), std::numeric_limits<Scalar>::infinity(), hit);
@ -652,11 +700,14 @@ inline bool intersect_ray_all_hits(
// Direction of the ray.
const VectorType &dir,
// All intersections of the ray with the indexed triangle set, sorted by parameter t.
std::vector<igl::Hit> &hits)
std::vector<igl::Hit> &hits,
// Epsilon for the ray-triangle intersection, it should be proportional to an average triangle edge length.
const double eps = 0.000001)
{
auto ray_intersector = detail::RayIntersectorHits<VertexType, IndexedFaceType, TreeType, VectorType> {
{ vertices, faces, {tree},
origin, dir, VectorType(dir.cwiseInverse()) }
origin, dir, VectorType(dir.cwiseInverse()),
eps }
};
if (! tree.empty()) {
ray_intersector.hits.reserve(8);

View File

@ -17,10 +17,18 @@ namespace sla {
class IndexedMesh::AABBImpl {
private:
AABBTreeIndirect::Tree3f m_tree;
double m_triangle_ray_epsilon;
public:
void init(const indexed_triangle_set &its)
void init(const indexed_triangle_set &its, bool calculate_epsilon)
{
m_triangle_ray_epsilon = 0.000001;
if (calculate_epsilon) {
// Calculate epsilon from average triangle edge length.
double l = its_average_edge_length(its);
if (l > 0)
m_triangle_ray_epsilon = 0.000001 * l * l;
}
m_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(
its.vertices, its.indices);
}
@ -31,7 +39,7 @@ public:
igl::Hit & hit)
{
AABBTreeIndirect::intersect_ray_first_hit(its.vertices, its.indices,
m_tree, s, dir, hit);
m_tree, s, dir, hit, m_triangle_ray_epsilon);
}
void intersect_ray(const indexed_triangle_set &its,
@ -40,7 +48,7 @@ public:
std::vector<igl::Hit> & hits)
{
AABBTreeIndirect::intersect_ray_all_hits(its.vertices, its.indices,
m_tree, s, dir, hits);
m_tree, s, dir, hits, m_triangle_ray_epsilon);
}
double squared_distance(const indexed_triangle_set & its,
@ -60,25 +68,25 @@ public:
}
};
template<class M> void IndexedMesh::init(const M &mesh)
template<class M> void IndexedMesh::init(const M &mesh, bool calculate_epsilon)
{
BoundingBoxf3 bb = bounding_box(mesh);
m_ground_level += bb.min(Z);
// Build the AABB accelaration tree
m_aabb->init(*m_tm);
m_aabb->init(*m_tm, calculate_epsilon);
}
IndexedMesh::IndexedMesh(const indexed_triangle_set& tmesh)
IndexedMesh::IndexedMesh(const indexed_triangle_set& tmesh, bool calculate_epsilon)
: m_aabb(new AABBImpl()), m_tm(&tmesh)
{
init(tmesh);
init(tmesh, calculate_epsilon);
}
IndexedMesh::IndexedMesh(const TriangleMesh &mesh)
IndexedMesh::IndexedMesh(const TriangleMesh &mesh, bool calculate_epsilon)
: m_aabb(new AABBImpl()), m_tm(&mesh.its)
{
init(mesh);
init(mesh, calculate_epsilon);
}
IndexedMesh::~IndexedMesh() {}

View File

@ -42,12 +42,14 @@ class IndexedMesh {
std::vector<DrainHole> m_holes;
#endif
template<class M> void init(const M &mesh);
template<class M> void init(const M &mesh, bool calculate_epsilon);
public:
explicit IndexedMesh(const indexed_triangle_set&);
explicit IndexedMesh(const TriangleMesh &mesh);
// calculate_epsilon ... calculate epsilon for triangle-ray intersection from an average triangle edge length.
// If set to false, a default epsilon is used, which works for "reasonable" meshes.
explicit IndexedMesh(const indexed_triangle_set &tmesh, bool calculate_epsilon = false);
explicit IndexedMesh(const TriangleMesh &mesh, bool calculate_epsilon = false);
IndexedMesh(const IndexedMesh& other);
IndexedMesh& operator=(const IndexedMesh&);

View File

@ -1275,6 +1275,21 @@ float its_volume(const indexed_triangle_set &its)
return volume;
}
float its_average_edge_length(const indexed_triangle_set &its)
{
if (its.indices.empty())
return 0.f;
double edge_length = 0.f;
for (size_t i = 0; i < its.indices.size(); ++ i) {
const its_triangle v = its_triangle_vertices(its, i);
edge_length += (v[1] - v[0]).cast<double>().norm() +
(v[2] - v[0]).cast<double>().norm() +
(v[1] - v[2]).cast<double>().norm();
}
return float(edge_length / (3 * its.indices.size()));
}
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its)
{
return its_split<>(its);

View File

@ -199,6 +199,7 @@ inline stl_normal its_unnormalized_normal(const indexed_triangle_set &its,
}
float its_volume(const indexed_triangle_set &its);
float its_average_edge_length(const indexed_triangle_set &its);
void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B);
void its_merge(indexed_triangle_set &A, const std::vector<Vec3f> &triangles);

View File

@ -112,7 +112,7 @@ public:
// The class references extern TriangleMesh, which must stay alive
// during MeshRaycaster existence.
MeshRaycaster(const TriangleMesh& mesh)
: m_emesh(mesh)
: m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
{
m_normals.reserve(mesh.stl.facet_start.size());
for (const stl_facet& facet : mesh.stl.facet_start)