Fix normal direction when exporting STL (#6406)

The export function does not depend on Model/ModelObject::mesh() family of functions,
changing them might break the already too brittle code.
This commit is contained in:
Lukas Matena 2021-04-26 19:56:28 +02:00
parent d1cfdcb49e
commit 978b359492
6 changed files with 42 additions and 26 deletions

View file

@ -833,18 +833,6 @@ indexed_triangle_set ModelObject::raw_indexed_triangle_set() const
return out; return out;
} }
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
TriangleMesh ModelObject::full_raw_mesh() const
{
TriangleMesh mesh;
for (const ModelVolume *v : this->volumes)
{
TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh);
}
return mesh;
}
const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const
{ {

View file

@ -289,8 +289,6 @@ public:
TriangleMesh raw_mesh() const; TriangleMesh raw_mesh() const;
// The same as above, but producing a lightweight indexed_triangle_set. // The same as above, but producing a lightweight indexed_triangle_set.
indexed_triangle_set raw_indexed_triangle_set() const; indexed_triangle_set raw_indexed_triangle_set() const;
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
TriangleMesh full_raw_mesh() const;
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied. // A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
// This bounding box is only used for the actual slicing. // This bounding box is only used for the actual slicing.
const BoundingBoxf3& raw_bounding_box() const; const BoundingBoxf3& raw_bounding_box() const;

View file

@ -2207,6 +2207,7 @@ std::vector<ExPolygons> PrintObject::slice_volumes(
TriangleMesh vol_mesh(model_volume.mesh()); TriangleMesh vol_mesh(model_volume.mesh());
vol_mesh.transform(model_volume.get_matrix(), true); vol_mesh.transform(model_volume.get_matrix(), true);
mesh.merge(vol_mesh); mesh.merge(vol_mesh);
mesh.repair(false);
} }
if (mesh.stl.stats.number_of_facets > 0) { if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo, true); mesh.transform(m_trafo, true);

View file

@ -357,10 +357,14 @@ void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
its_transform(its, t); its_transform(its, t);
if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) { if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) {
// Left handed transformation is being applied. It is a good idea to flip the faces and their normals. // Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
this->repair(false); // As for the assert: the repair function would fix the normals, reversing would
stl_reverse_all_facets(&stl); // break them again. The caller should provide a mesh that does not need repair.
this->its.clear(); // The repair call is left here so things don't break more than they were.
this->require_shared_vertices(); assert(this->repaired);
this->repair(false);
stl_reverse_all_facets(&stl);
this->its.clear();
this->require_shared_vertices();
} }
} }
@ -369,11 +373,12 @@ void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
stl_transform(&stl, m); stl_transform(&stl, m);
its_transform(its, m); its_transform(its, m);
if (fix_left_handed && m.determinant() < 0.) { if (fix_left_handed && m.determinant() < 0.) {
// Left handed transformation is being applied. It is a good idea to flip the faces and their normals. // See comments in function above.
assert(this->repaired);
this->repair(false); this->repair(false);
stl_reverse_all_facets(&stl); stl_reverse_all_facets(&stl);
this->its.clear(); this->its.clear();
this->require_shared_vertices(); this->require_shared_vertices();
} }
} }

View file

@ -1,7 +1,6 @@
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
#include "GLCanvas3D.hpp" #include "GLCanvas3D.hpp"
#include "admesh/stl.h"
#include "libslic3r/ClipperUtils.hpp" #include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/PrintConfig.hpp" #include "libslic3r/PrintConfig.hpp"
#include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/GCode/ThumbnailData.hpp"

View file

@ -5085,6 +5085,30 @@ void Plater::export_stl(bool extended, bool selection_only)
if (selection_only && (obj_idx == -1 || selection.is_wipe_tower())) if (selection_only && (obj_idx == -1 || selection.is_wipe_tower()))
return; return;
// Following lambda generates a combined mesh for export with normals pointing outwards.
auto mesh_to_export = [](const ModelObject* mo, bool instances) -> TriangleMesh {
TriangleMesh mesh;
for (const ModelVolume *v : mo->volumes)
if (v->is_model_part()) {
TriangleMesh vol_mesh(v->mesh());
vol_mesh.repair();
vol_mesh.transform(v->get_matrix(), true);
mesh.merge(vol_mesh);
}
mesh.repair();
if (instances) {
TriangleMesh vols_mesh(mesh);
mesh = TriangleMesh();
for (const ModelInstance *i : mo->instances) {
TriangleMesh m = vols_mesh;
m.transform(i->get_matrix(), true);
mesh.merge(m);
}
}
mesh.repair();
return mesh;
};
TriangleMesh mesh; TriangleMesh mesh;
if (p->printer_technology == ptFFF) { if (p->printer_technology == ptFFF) {
if (selection_only) { if (selection_only) {
@ -5092,20 +5116,21 @@ void Plater::export_stl(bool extended, bool selection_only)
if (selection.get_mode() == Selection::Instance) if (selection.get_mode() == Selection::Instance)
{ {
if (selection.is_single_full_object()) if (selection.is_single_full_object())
mesh = model_object->mesh(); mesh = mesh_to_export(model_object, true);
else else
mesh = model_object->full_raw_mesh(); mesh = mesh_to_export(model_object, false);
} }
else else
{ {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
mesh = model_object->volumes[volume->volume_idx()]->mesh(); mesh = model_object->volumes[volume->volume_idx()]->mesh();
mesh.transform(volume->get_volume_transformation().get_matrix()); mesh.transform(volume->get_volume_transformation().get_matrix(), true);
mesh.translate(-model_object->origin_translation.cast<float>()); mesh.translate(-model_object->origin_translation.cast<float>());
} }
} }
else { else {
mesh = p->model.mesh(); for (const ModelObject *o : p->model.objects)
mesh.merge(mesh_to_export(o, true));
} }
} }
else else