fixed QEC decimation problem with ugly triangles

improved seam placer subdivision factor to get smaller models (less triangles)
changed aligned behavior - now should align according to the enforcers
little tweaking of parameters
This commit is contained in:
PavelMikus 2022-05-10 13:36:26 +02:00
parent 39ed3b9cea
commit 18c4fc69e3
3 changed files with 77 additions and 32 deletions

View File

@ -178,24 +178,22 @@ std::vector<FaceVisibilityInfo> raycast_visibility(const AABBTreeIndirect::Tree<
bool some_hit = AABBTreeIndirect::intersect_ray_all_hits(triangles.vertices,
triangles.indices, raycasting_tree,
ray_origin_d, final_ray_dir_d, hits);
if (some_hit && its_face_normal(triangles, hits[0].id).dot(final_ray_dir) <= 0) {
int in_negative = 0;
int in_positive = 0;
if (some_hit) {
int counter = 0;
// NOTE: iterating in reverse, from the last hit for one simple reason: We know the state of the ray at that point;
// It cannot be inside model, and it cannot be inside negative volume
for (int hit_index = int(hits.size()) - 1; hit_index >= 0; --hit_index) {
Vec3f face_normal = its_face_normal(triangles, hits[hit_index].id);
if (hits[hit_index].id >= int(negative_volumes_start_index)) { //negative volume hit
in_negative += sgn(face_normal.dot(final_ray_dir)); // if volume face aligns with ray dir, we are leaving negative space
counter -= sgn(face_normal.dot(final_ray_dir)); // if volume face aligns with ray dir, we are leaving negative space
// which in reverse hit analysis means, that we are entering negative space :) and vice versa
} else {
in_positive += sgn(face_normal.dot(final_ray_dir));
}
if (in_positive > 0 && in_negative <= 0) {
dest.visibility -= decrease;
break;
counter += sgn(face_normal.dot(final_ray_dir));
}
}
if (counter == 0) {
dest.visibility -= decrease;
}
}
}
}
@ -321,12 +319,10 @@ struct GlobalModelInfo {
for (size_t i = 0; i < divided_mesh.vertices.size(); ++i) {
float visibility = calculate_point_visibility(divided_mesh.vertices[i]);
Vec3f color = value_to_rgbf(0.0f, 1.0f,
visibility);
Vec3f color = value_to_rgbf(0.0f, 1.0f, visibility);
fprintf(fp, "v %f %f %f %f %f %f\n",
divided_mesh.vertices[i](0), divided_mesh.vertices[i](1), divided_mesh.vertices[i](2),
color(0), color(1), color(2)
);
color(0), color(1), color(2));
}
for (size_t i = 0; i < divided_mesh.indices.size(); ++i)
fprintf(fp, "f %d %d %d\n", divided_mesh.indices[i][0] + 1, divided_mesh.indices[i][1] + 1,
@ -557,15 +553,55 @@ void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po) {
<< "SeamPlacer: simplify occlusion meshes: start";
//simplify raycasting mesh
its_quadric_edge_collapse(triangle_set, SeamPlacer::raycasting_decimation_target_triangle_count, nullptr, nullptr,
nullptr);
triangle_set = its_subdivide(triangle_set, SeamPlacer::raycasting_subdivision_target_length);
{
its_quadric_edge_collapse(triangle_set, SeamPlacer::raycasting_decimation_target_triangle_count, nullptr,
nullptr,
nullptr);
float triangle_set_area = tbb::parallel_reduce(tbb::blocked_range<size_t>(0, triangle_set.indices.size()), 0,
[&triangle_set](
tbb::blocked_range<size_t> r, float sum) {
for (size_t t_idx = r.begin(); t_idx < r.end(); ++t_idx) {
const Vec3f &a = triangle_set.vertices[triangle_set.indices[t_idx].x()];
const Vec3f &b = triangle_set.vertices[triangle_set.indices[t_idx].y()];
const Vec3f &c = triangle_set.vertices[triangle_set.indices[t_idx].z()];
sum += 0.5f * (b - a).cross(c - a).norm();
}
return sum;
}, std::plus<float>());
float target_triangle_area = triangle_set_area / SeamPlacer::raycasting_subdivision_target_triangle_count;
float target_triangle_length = 2 * 1.316 * sqrtf(target_triangle_area); //assuming 30-30-120 triangle
float subdivision_length = std::max(SeamPlacer::raycasting_subdivision_target_length, target_triangle_length);
triangle_set = its_subdivide(triangle_set, subdivision_length);
BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: triangle set after subdivision: " << triangle_set.indices.size();
}
//simplify negative volumes
its_quadric_edge_collapse(negative_volumes_set, SeamPlacer::raycasting_decimation_target_triangle_count, nullptr,
nullptr,
nullptr);
negative_volumes_set = its_subdivide(negative_volumes_set, SeamPlacer::raycasting_subdivision_target_length);
{
its_quadric_edge_collapse(negative_volumes_set, SeamPlacer::raycasting_decimation_target_triangle_count,
nullptr,
nullptr,
nullptr);
float negative_volumes_set_area = tbb::parallel_reduce(
tbb::blocked_range<size_t>(0, negative_volumes_set.indices.size()), 0,
[&negative_volumes_set](
tbb::blocked_range<size_t> r, float sum) {
for (size_t t_idx = r.begin(); t_idx < r.end(); ++t_idx) {
const Vec3f &a = negative_volumes_set.vertices[negative_volumes_set.indices[t_idx].x()];
const Vec3f &b = negative_volumes_set.vertices[negative_volumes_set.indices[t_idx].y()];
const Vec3f &c = negative_volumes_set.vertices[negative_volumes_set.indices[t_idx].z()];
sum += 0.5f * (b - a).cross(c - a).norm();
}
return sum;
}, std::plus<float>());
float target_triangle_area = negative_volumes_set_area
/ SeamPlacer::raycasting_subdivision_target_triangle_count;
float target_triangle_length = 2 * 1.316 * sqrtf(target_triangle_area); //assuming 30-30-120 triangle
float subdivision_length = std::max(SeamPlacer::raycasting_subdivision_target_length, target_triangle_length);
negative_volumes_set = its_subdivide(negative_volumes_set, subdivision_length);
}
size_t negative_volumes_start_index = triangle_set.indices.size();
its_merge(triangle_set, negative_volumes_set);
@ -1077,15 +1113,12 @@ std::optional<std::pair<size_t, size_t>> SeamPlacer::find_next_seam_in_layer(
}
// Next compare nearest and nearby point. If they are similar pick nearest, Otherwise expect curvy lines on smooth surfaces like chimney of benchy model
// We also compare it to the last point, to detect sharp changes in the scoring - that points to change in the model geometry and string should be ended.
if (comparator.are_similar(nearest_point, best_nearby_point)
&& comparator.is_first_not_much_worse(nearest_point, next_layer_seam)
&& comparator.are_similar(last_point, nearest_point)) {
&& comparator.is_first_not_much_worse(nearest_point, next_layer_seam)) {
return {std::pair<size_t, size_t> {layer_idx, nearest_point_index}};
}
// If nearest point is not good enough, try it with the best nearby point.
if (comparator.is_first_not_much_worse(best_nearby_point, next_layer_seam)
&& comparator.are_similar(last_point, nearest_point)) {
if (comparator.is_first_not_much_worse(best_nearby_point, next_layer_seam)) {
return {std::pair<size_t, size_t> {layer_idx, best_nearby_point_index}};
}
@ -1204,11 +1237,13 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl::
}
}
Vec2f back_attractor = 2.0f * unscaled(po->bounding_box().max).cast<float>();
//sort them before alignment. Alignment is sensitive to initializaion, this gives it better chance to choose something nice
std::sort(seams.begin(), seams.end(),
[&comparator, &layers](const std::pair<size_t, size_t> &left, const std::pair<size_t, size_t> &right) {
[&comparator, &layers, &back_attractor](const std::pair<size_t, size_t> &left,
const std::pair<size_t, size_t> &right) {
return comparator.is_first_better(layers[left.first].points[left.second],
layers[right.first].points[right.second]);
layers[right.first].points[right.second], back_attractor);
}
);

View File

@ -112,6 +112,8 @@ struct PrintObjectSeamData
class SeamPlacer {
public:
static constexpr size_t raycasting_decimation_target_triangle_count = 10000;
// for subdivision, both following criteria are considered, and the one with less resulting triangles is used
static constexpr size_t raycasting_subdivision_target_triangle_count = 15000;
static constexpr float raycasting_subdivision_target_length = 2.0f;
//square of number of rays per triangle
static constexpr size_t sqr_rays_per_triangle = 7;
@ -120,7 +122,7 @@ public:
static constexpr float polygon_local_angles_arm_distance = 0.5f;
// increases angle importance at the cost of deacreasing visibility info importance. must be > 0
static constexpr float additional_angle_importance = 0.4f;
static constexpr float additional_angle_importance = 0.3f;
// If enforcer or blocker is closer to the seam candidate than this limit, the seam candidate is set to Blocker or Enforcer
static constexpr float enforcer_blocker_distance_tolerance = 0.35f;
@ -129,7 +131,7 @@ public:
// When searching for seam clusters for alignment:
// following value describes, how much worse score can point have and still be picked into seam cluster instead of original seam point on the same layer
static constexpr float seam_align_score_tolerance = 0.3f;
static constexpr float seam_align_score_tolerance = 0.2f;
// seam_align_tolerable_dist - if next layer closes point is too far away, break string
static constexpr float seam_align_tolerable_dist = 1.0f;
// if the seam of the current layer is too far away, and the closest seam candidate is not very good, layer is skipped.

View File

@ -595,8 +595,7 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
const EdgeInfos & e_infos,
const indexed_triangle_set &its)
{
static const float thr_pos = 1.0f - std::numeric_limits<float>::epsilon();
static const float thr_neg = -thr_pos;
static const float triangle_beauty_threshold = 1.0f - std::numeric_limits<float>::epsilon();
static const float dot_thr = 0.2f; // Value from simplify mesh cca 80 DEG
// for each vertex triangles
@ -615,7 +614,16 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
d2.normalize();
float dot = d1.dot(d2);
if (dot > thr_pos || dot < thr_neg) return true;
if (std::abs(dot) > triangle_beauty_threshold) { // OK, the new triangle is suspiciously ugly, but it can still be better than the original
const Vec3f &v_orig = its.vertices[t[(e_info.edge) % 3]];
Vec3f d1_orig = vf - v_orig;
d1_orig.normalize();
Vec3f d2_orig = vs - v_orig;
d2_orig.normalize();
if (std::abs(d1_orig.dot(d2_orig)) < std::abs(dot)) { // original was not that ugly, so return flipped
return true;
}
}
// IMPROVE: propagate new normal
Vec3f n = d1.cross(d2);
n.normalize();