Sharing TriangleMesh objects between the front end (UI) and back end

(background processing)
This commit is contained in:
bubnikv 2019-06-11 17:08:47 +02:00
parent 5fc465b7e8
commit 0bb8ee149e
20 changed files with 254 additions and 193 deletions

View File

@ -241,7 +241,6 @@ int CLI::run(int argc, char **argv)
} else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") { } else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
std::vector<Model> new_models; std::vector<Model> new_models;
for (auto &model : m_models) { for (auto &model : m_models) {
model.repair();
model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0 model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
size_t num_objects = model.objects.size(); size_t num_objects = model.objects.size();
for (size_t i = 0; i < num_objects; ++ i) { for (size_t i = 0; i < num_objects; ++ i) {
@ -301,8 +300,9 @@ int CLI::run(int argc, char **argv)
} }
} }
} else if (opt_key == "repair") { } else if (opt_key == "repair") {
for (auto &model : m_models) // Models are repaired by default.
model.repair(); //for (auto &model : m_models)
// model.repair();
} else { } else {
boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl; boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
return 1; return 1;

View File

@ -59,7 +59,7 @@ struct HashEdge {
// Ensure identical vertex ordering of equal edges. // Ensure identical vertex ordering of equal edges.
// This method is numerically robust. // This method is numerically robust.
if (stl_vertex_lower(*a, *b)) { if (vertex_lower(*a, *b)) {
} else { } else {
// This edge is loaded backwards. // This edge is loaded backwards.
std::swap(a, b); std::swap(a, b);
@ -110,6 +110,12 @@ struct HashEdge {
} }
return true; return true;
} }
private:
inline bool vertex_lower(const stl_vertex &a, const stl_vertex &b) {
return (a(0) != b(0)) ? (a(0) < b(0)) :
((a(1) != b(1)) ? (a(1) < b(1)) : (a(2) < b(2)));
}
}; };
struct HashTableEdges { struct HashTableEdges {
@ -441,6 +447,8 @@ void stl_check_facets_exact(stl_file *stl)
if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) { if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) {
// Remove the degenerate facet. // Remove the degenerate facet.
facet = stl->facet_start[-- stl->stats.number_of_facets]; facet = stl->facet_start[-- stl->stats.number_of_facets];
stl->facet_start.pop_back();
stl->neighbors_start.pop_back();
stl->stats.facets_removed += 1; stl->stats.facets_removed += 1;
stl->stats.degenerate_facets += 1; stl->stats.degenerate_facets += 1;
} else } else
@ -526,10 +534,8 @@ void stl_remove_unconnected_facets(stl_file *stl)
assert(false); assert(false);
} }
if (facet_number == -- stl->stats.number_of_facets) if (facet_number < -- stl->stats.number_of_facets) {
// Removing the last face is easy, just forget the last face. // Removing a face, which was not the last one.
return;
// Copy the face and neighborship from the last face to facet_number. // Copy the face and neighborship from the last face to facet_number.
stl->facet_start[facet_number] = stl->facet_start[stl->stats.number_of_facets]; stl->facet_start[facet_number] = stl->facet_start[stl->stats.number_of_facets];
neighbors = stl->neighbors_start[stl->stats.number_of_facets]; neighbors = stl->neighbors_start[stl->stats.number_of_facets];
@ -543,6 +549,10 @@ void stl_remove_unconnected_facets(stl_file *stl)
} }
other_face_idx = facet_number; other_face_idx = facet_number;
} }
}
stl->facet_start.pop_back();
stl->neighbors_start.pop_back();
}; };
auto remove_degenerate = [stl, remove_facet](int facet) auto remove_degenerate = [stl, remove_facet](int facet)

View File

@ -128,6 +128,8 @@ struct indexed_triangle_set
void clear() { indices.clear(); vertices.clear(); } void clear() { indices.clear(); vertices.clear(); }
std::vector<stl_triangle_vertex_indices> indices; std::vector<stl_triangle_vertex_indices> indices;
std::vector<stl_vertex> vertices; std::vector<stl_vertex> vertices;
//FIXME add normals once we get rid of the stl_file from TriangleMesh completely.
//std::vector<stl_normal> normals
}; };
extern bool stl_open(stl_file *stl, const char *file); extern bool stl_open(stl_file *stl, const char *file);
@ -209,6 +211,34 @@ inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::Don
stl_get_size(stl); stl_get_size(stl);
} }
template<typename T>
extern void its_transform(indexed_triangle_set &its, T *trafo3x4)
{
for (stl_vertex &v_dst : its.vertices) {
stl_vertex &v_dst = face.vertex[i_vertex];
stl_vertex v_src = v_dst;
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]);
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
}
}
template<typename T>
inline void its_transform(indexed_triangle_set &its, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t)
{
const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0);
for (stl_vertex &v : its.vertices)
v = (t * v.template cast<T>()).template cast<float>().eval();
}
template<typename T>
inline void its_transform(indexed_triangle_set &its, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m)
{
for (stl_vertex &v : its.vertices)
v = (m * v.template cast<T>()).template cast<float>().eval();
}
extern void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its); extern void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its);
extern bool its_write_obj(const indexed_triangle_set &its, const char *file); extern bool its_write_obj(const indexed_triangle_set &its, const char *file);
extern bool its_write_off(const indexed_triangle_set &its, const char *file); extern bool its_write_off(const indexed_triangle_set &its, const char *file);
@ -225,10 +255,6 @@ inline void stl_normalize_vector(stl_normal &normal) {
else else
normal *= float(1.0 / length); normal *= float(1.0 / length);
} }
inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) {
return (a(0) != b(0)) ? (a(0) < b(0)) :
((a(1) != b(1)) ? (a(1) < b(1)) : (a(2) < b(2)));
}
extern void stl_calculate_volume(stl_file *stl); extern void stl_calculate_volume(stl_file *stl);
extern void stl_repair(stl_file *stl, bool fixall_flag, bool exact_flag, bool tolerance_flag, float tolerance, bool increment_flag, float increment, bool nearby_flag, int iterations, bool remove_unconnected_flag, bool fill_holes_flag, bool normal_directions_flag, bool normal_values_flag, bool reverse_all_flag, bool verbose_flag); extern void stl_repair(stl_file *stl, bool fixall_flag, bool exact_flag, bool tolerance_flag, float tolerance, bool increment_flag, float increment, bool nearby_flag, int iterations, bool remove_unconnected_flag, bool fill_holes_flag, bool normal_directions_flag, bool normal_values_flag, bool reverse_all_flag, bool verbose_flag);

View File

@ -108,7 +108,7 @@ void stl_scale_versor(stl_file *stl, const stl_vertex &versor)
static void calculate_normals(stl_file *stl) static void calculate_normals(stl_file *stl)
{ {
stl_normal normal; stl_normal normal;
for (uint32_t i = 0; i < stl->stats.number_of_facets; i++) { for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
stl_calculate_normal(normal, &stl->facet_start[i]); stl_calculate_normal(normal, &stl->facet_start[i]);
stl_normalize_vector(normal); stl_normalize_vector(normal);
stl->facet_start[i].normal = normal; stl->facet_start[i].normal = normal;

View File

@ -1489,9 +1489,9 @@ namespace Slic3r {
} }
// splits volume out of imported geometry // splits volume out of imported geometry
TriangleMesh triangle_mesh;
stl_file &stl = triangle_mesh.stl;
unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1; unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1;
ModelVolume* volume = object.add_volume(TriangleMesh());
stl_file& stl = volume->mesh.stl;
stl.stats.type = inmemory; stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)triangles_count; stl.stats.number_of_facets = (uint32_t)triangles_count;
stl.stats.original_num_facets = (int)stl.stats.number_of_facets; stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
@ -1510,8 +1510,10 @@ namespace Slic3r {
} }
stl_get_size(&stl); stl_get_size(&stl);
volume->mesh.repair(); triangle_mesh.repair();
volume->center_geometry();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
volume->center_geometry_after_creation();
volume->calculate_convex_hull(); volume->calculate_convex_hull();
// apply volume's name and config data // apply volume's name and config data
@ -1879,11 +1881,14 @@ namespace Slic3r {
if (volume == nullptr) if (volume == nullptr)
continue; continue;
if (!volume->mesh().repaired)
throw std::runtime_error("store_3mf() requires repair()");
if (!volume->mesh().has_shared_vertices())
throw std::runtime_error("store_3mf() requires shared vertices");
volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first; volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first;
volume->mesh.require_shared_vertices(); const indexed_triangle_set &its = volume->mesh().its;
const indexed_triangle_set &its = volume->mesh.its;
if (its.vertices.empty()) if (its.vertices.empty())
{ {
add_error("Found invalid mesh"); add_error("Found invalid mesh");
@ -1916,7 +1921,7 @@ namespace Slic3r {
VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume); VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
assert(volume_it != volumes_offsets.end()); assert(volume_it != volumes_offsets.end());
const indexed_triangle_set &its = volume->mesh.its; const indexed_triangle_set &its = volume->mesh().its;
// updates triangle offsets // updates triangle offsets
volume_it->second.first_triangle_id = triangles_count; volume_it->second.first_triangle_id = triangles_count;

View File

@ -522,7 +522,8 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VOLUME: case NODE_TYPE_VOLUME:
{ {
assert(m_object && m_volume); assert(m_object && m_volume);
stl_file &stl = m_volume->mesh.stl; TriangleMesh mesh;
stl_file &stl = mesh.stl;
stl.stats.type = inmemory; stl.stats.type = inmemory;
stl.stats.number_of_facets = int(m_volume_facets.size() / 3); stl.stats.number_of_facets = int(m_volume_facets.size() / 3);
stl.stats.original_num_facets = stl.stats.number_of_facets; stl.stats.original_num_facets = stl.stats.number_of_facets;
@ -533,8 +534,9 @@ void AMFParserContext::endElement(const char * /* name */)
memcpy(facet.vertex[v].data(), &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float)); memcpy(facet.vertex[v].data(), &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float));
} }
stl_get_size(&stl); stl_get_size(&stl);
m_volume->mesh.repair(); mesh.repair();
m_volume->center_geometry(); m_volume->set_mesh(std::move(mesh));
m_volume->center_geometry_after_creation();
m_volume->calculate_convex_hull(); m_volume->calculate_convex_hull();
m_volume_facets.clear(); m_volume_facets.clear();
m_volume = nullptr; m_volume = nullptr;
@ -923,10 +925,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
int num_vertices = 0; int num_vertices = 0;
for (ModelVolume *volume : object->volumes) { for (ModelVolume *volume : object->volumes) {
vertices_offsets.push_back(num_vertices); vertices_offsets.push_back(num_vertices);
if (! volume->mesh.repaired) if (! volume->mesh().repaired)
throw std::runtime_error("store_amf() requires repair()"); throw std::runtime_error("store_amf() requires repair()");
volume->mesh.require_shared_vertices(); if (! volume->mesh().has_shared_vertices())
const indexed_triangle_set &its = volume->mesh.its; throw std::runtime_error("store_amf() requires shared vertices");
const indexed_triangle_set &its = volume->mesh().its;
const Transform3d& matrix = volume->get_matrix(); const Transform3d& matrix = volume->get_matrix();
for (size_t i = 0; i < its.vertices.size(); ++i) { for (size_t i = 0; i < its.vertices.size(); ++i) {
stream << " <vertex>\n"; stream << " <vertex>\n";
@ -955,10 +958,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
if (volume->is_modifier()) if (volume->is_modifier())
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n"; stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n"; stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
for (size_t i = 0; i < (int)volume->mesh.its.indices.size(); ++i) { const indexed_triangle_set &its = volume->mesh().its;
for (size_t i = 0; i < (int)its.indices.size(); ++i) {
stream << " <triangle>\n"; stream << " <triangle>\n";
for (int j = 0; j < 3; ++j) for (int j = 0; j < 3; ++j)
stream << " <v" << j + 1 << ">" << volume->mesh.its.indices[i][j] + vertices_offset << "</v" << j + 1 << ">\n"; stream << " <v" << j + 1 << ">" << its.indices[i][j] + vertices_offset << "</v" << j + 1 << ">\n";
stream << " </triangle>\n"; stream << " </triangle>\n";
} }
stream << " </volume>\n"; stream << " </volume>\n";

View File

@ -160,12 +160,6 @@ Model Model::read_from_archive(const std::string &input_file, DynamicPrintConfig
return model; return model;
} }
void Model::repair()
{
for (ModelObject *o : this->objects)
o->repair();
}
ModelObject* Model::add_object() ModelObject* Model::add_object()
{ {
this->objects.emplace_back(new ModelObject(this)); this->objects.emplace_back(new ModelObject(this));
@ -472,7 +466,7 @@ bool Model::looks_like_multipart_object() const
if (obj->volumes.size() > 1 || obj->config.keys().size() > 1) if (obj->volumes.size() > 1 || obj->config.keys().size() > 1)
return false; return false;
for (const ModelVolume *vol : obj->volumes) { for (const ModelVolume *vol : obj->volumes) {
double zmin_this = vol->mesh.bounding_box().min(2); double zmin_this = vol->mesh().bounding_box().min(2);
if (zmin == std::numeric_limits<double>::max()) if (zmin == std::numeric_limits<double>::max())
zmin = zmin_this; zmin = zmin_this;
else if (std::abs(zmin - zmin_this) > EPSILON) else if (std::abs(zmin - zmin_this) > EPSILON)
@ -679,7 +673,7 @@ ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
{ {
ModelVolume* v = new ModelVolume(this, mesh); ModelVolume* v = new ModelVolume(this, mesh);
this->volumes.push_back(v); this->volumes.push_back(v);
v->center_geometry(); v->center_geometry_after_creation();
this->invalidate_bounding_box(); this->invalidate_bounding_box();
return v; return v;
} }
@ -688,7 +682,7 @@ ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh)
{ {
ModelVolume* v = new ModelVolume(this, std::move(mesh)); ModelVolume* v = new ModelVolume(this, std::move(mesh));
this->volumes.push_back(v); this->volumes.push_back(v);
v->center_geometry(); v->center_geometry_after_creation();
this->invalidate_bounding_box(); this->invalidate_bounding_box();
return v; return v;
} }
@ -697,7 +691,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other)
{ {
ModelVolume* v = new ModelVolume(this, other); ModelVolume* v = new ModelVolume(this, other);
this->volumes.push_back(v); this->volumes.push_back(v);
v->center_geometry(); v->center_geometry_after_creation();
this->invalidate_bounding_box(); this->invalidate_bounding_box();
return v; return v;
} }
@ -706,7 +700,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&me
{ {
ModelVolume* v = new ModelVolume(this, other, std::move(mesh)); ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
this->volumes.push_back(v); this->volumes.push_back(v);
v->center_geometry(); v->center_geometry_after_creation();
this->invalidate_bounding_box(); this->invalidate_bounding_box();
return v; return v;
} }
@ -827,7 +821,7 @@ TriangleMesh ModelObject::raw_mesh() const
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) if (v->is_model_part())
{ {
TriangleMesh vol_mesh(v->mesh); TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix()); vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh); mesh.merge(vol_mesh);
} }
@ -840,7 +834,7 @@ TriangleMesh ModelObject::full_raw_mesh() const
TriangleMesh mesh; TriangleMesh mesh;
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
{ {
TriangleMesh vol_mesh(v->mesh); TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix()); vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh); mesh.merge(vol_mesh);
} }
@ -854,7 +848,7 @@ const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const
m_raw_mesh_bounding_box.reset(); m_raw_mesh_bounding_box.reset();
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) if (v->is_model_part())
m_raw_mesh_bounding_box.merge(v->mesh.transformed_bounding_box(v->get_matrix())); m_raw_mesh_bounding_box.merge(v->mesh().transformed_bounding_box(v->get_matrix()));
} }
return m_raw_mesh_bounding_box; return m_raw_mesh_bounding_box;
} }
@ -863,7 +857,7 @@ BoundingBoxf3 ModelObject::full_raw_mesh_bounding_box() const
{ {
BoundingBoxf3 bb; BoundingBoxf3 bb;
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
bb.merge(v->mesh.transformed_bounding_box(v->get_matrix())); bb.merge(v->mesh().transformed_bounding_box(v->get_matrix()));
return bb; return bb;
} }
@ -881,7 +875,7 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
{ {
if (v->is_model_part()) if (v->is_model_part())
m_raw_bounding_box.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix())); m_raw_bounding_box.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
} }
} }
return m_raw_bounding_box; return m_raw_bounding_box;
@ -895,7 +889,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
for (ModelVolume *v : this->volumes) for (ModelVolume *v : this->volumes)
{ {
if (v->is_model_part()) if (v->is_model_part())
bb.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix())); bb.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
} }
return bb; return bb;
} }
@ -909,10 +903,10 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) { if (v->is_model_part()) {
Transform3d trafo = trafo_instance * v->get_matrix(); Transform3d trafo = trafo_instance * v->get_matrix();
const indexed_triangle_set &its = v->mesh.its; const indexed_triangle_set &its = v->mesh().its;
if (its.vertices.empty()) { if (its.vertices.empty()) {
// Using the STL faces. // Using the STL faces.
const stl_file& stl = v->mesh.stl; const stl_file& stl = v->mesh().stl;
for (const stl_facet &facet : stl.facet_start) for (const stl_facet &facet : stl.facet_start)
for (size_t j = 0; j < 3; ++ j) { for (size_t j = 0; j < 3; ++ j) {
Vec3d p = trafo * facet.vertex[j].cast<double>(); Vec3d p = trafo * facet.vertex[j].cast<double>();
@ -1038,6 +1032,7 @@ void ModelObject::mirror(Axis axis)
this->invalidate_bounding_box(); this->invalidate_bounding_box();
} }
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelObject::scale_mesh(const Vec3d &versor) void ModelObject::scale_mesh(const Vec3d &versor)
{ {
for (ModelVolume *v : this->volumes) for (ModelVolume *v : this->volumes)
@ -1061,14 +1056,14 @@ size_t ModelObject::facets_count() const
size_t num = 0; size_t num = 0;
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) if (v->is_model_part())
num += v->mesh.stl.stats.number_of_facets; num += v->mesh().stl.stats.number_of_facets;
return num; return num;
} }
bool ModelObject::needed_repair() const bool ModelObject::needed_repair() const
{ {
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (v->is_model_part() && v->mesh.needed_repair()) if (v->is_model_part() && v->mesh().needed_repair())
return true; return true;
return false; return false;
} }
@ -1134,11 +1129,12 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
// Transform the mesh by the combined transformation matrix. // Transform the mesh by the combined transformation matrix.
// Flip the triangles in case the composite transformation is left handed. // Flip the triangles in case the composite transformation is left handed.
volume->mesh.transform(instance_matrix * volume_matrix, true); TriangleMesh mesh(volume->mesh());
mesh.transform(instance_matrix * volume_matrix, true);
volume->reset_mesh();
// Perform cut // Perform cut
volume->mesh.require_shared_vertices(); // TriangleMeshSlicer needs this TriangleMeshSlicer tms(&mesh);
TriangleMeshSlicer tms(&volume->mesh);
tms.cut(float(z), &upper_mesh, &lower_mesh); tms.cut(float(z), &upper_mesh, &lower_mesh);
// Reset volume transformation except for offset // Reset volume transformation except for offset
@ -1232,7 +1228,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
} }
ModelVolume* volume = this->volumes.front(); ModelVolume* volume = this->volumes.front();
TriangleMeshPtrs meshptrs = volume->mesh.split(); TriangleMeshPtrs meshptrs = volume->mesh().split();
for (TriangleMesh *mesh : meshptrs) { for (TriangleMesh *mesh : meshptrs) {
mesh->repair(); mesh->repair();
@ -1259,12 +1255,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
return; return;
} }
void ModelObject::repair()
{
for (ModelVolume *v : this->volumes)
v->mesh.repair();
}
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure. // then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
// This situation is solved by baking in the instance transformation into the mesh vertices. // This situation is solved by baking in the instance transformation into the mesh vertices.
@ -1305,7 +1295,8 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx)
double volume_new_scaling_factor = volume_uniform_scaling ? volume_trafo.get_scaling_factor().x() : 1.; double volume_new_scaling_factor = volume_uniform_scaling ? volume_trafo.get_scaling_factor().x() : 1.;
// Transform the mesh. // Transform the mesh.
Matrix3d volume_trafo_3x3 = volume_trafo.get_matrix(true, false, volume_uniform_scaling, !volume_has_mirrorring).matrix().block<3, 3>(0, 0); Matrix3d volume_trafo_3x3 = volume_trafo.get_matrix(true, false, volume_uniform_scaling, !volume_has_mirrorring).matrix().block<3, 3>(0, 0);
model_volume->transform_mesh(mesh_trafo_3x3 * volume_trafo_3x3, left_handed != volume_left_handed); // Following method creates a new shared_ptr<TriangleMesh>
model_volume->transform_this_mesh(mesh_trafo_3x3 * volume_trafo_3x3, left_handed != volume_left_handed);
// Reset the rotation, scaling and mirroring. // Reset the rotation, scaling and mirroring.
model_volume->set_rotation(Vec3d(0., 0., 0.)); model_volume->set_rotation(Vec3d(0., 0., 0.));
model_volume->set_scaling_factor(Vec3d(volume_new_scaling_factor, volume_new_scaling_factor, volume_new_scaling_factor)); model_volume->set_scaling_factor(Vec3d(volume_new_scaling_factor, volume_new_scaling_factor, volume_new_scaling_factor));
@ -1447,7 +1438,7 @@ std::string ModelObject::get_export_filename() const
stl_stats ModelObject::get_object_stl_stats() const stl_stats ModelObject::get_object_stl_stats() const
{ {
if (this->volumes.size() == 1) if (this->volumes.size() == 1)
return this->volumes[0]->mesh.stl.stats; return this->volumes[0]->mesh().stl.stats;
stl_stats full_stats; stl_stats full_stats;
memset(&full_stats, 0, sizeof(stl_stats)); memset(&full_stats, 0, sizeof(stl_stats));
@ -1458,7 +1449,7 @@ stl_stats ModelObject::get_object_stl_stats() const
if (volume->id() == this->volumes[0]->id()) if (volume->id() == this->volumes[0]->id())
continue; continue;
const stl_stats& stats = volume->mesh.stl.stats; const stl_stats& stats = volume->mesh().stl.stats;
// initialize full_stats (for repaired errors) // initialize full_stats (for repaired errors)
full_stats.degenerate_facets += stats.degenerate_facets; full_stats.degenerate_facets += stats.degenerate_facets;
@ -1526,30 +1517,30 @@ bool ModelVolume::is_splittable() const
{ {
// the call mesh.is_splittable() is expensive, so cache the value to calculate it only once // the call mesh.is_splittable() is expensive, so cache the value to calculate it only once
if (m_is_splittable == -1) if (m_is_splittable == -1)
m_is_splittable = (int)mesh.is_splittable(); m_is_splittable = (int)this->mesh().is_splittable();
return m_is_splittable == 1; return m_is_splittable == 1;
} }
void ModelVolume::center_geometry() void ModelVolume::center_geometry_after_creation()
{ {
Vec3d shift = mesh.bounding_box().center(); Vec3d shift = this->mesh().bounding_box().center();
if (!shift.isApprox(Vec3d::Zero())) if (!shift.isApprox(Vec3d::Zero()))
{ {
mesh.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); m_mesh->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
m_convex_hull.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); m_convex_hull->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
translate(shift); translate(shift);
} }
} }
void ModelVolume::calculate_convex_hull() void ModelVolume::calculate_convex_hull()
{ {
m_convex_hull = mesh.convex_hull_3d(); m_convex_hull = std::make_shared<TriangleMesh>(this->mesh().convex_hull_3d());
} }
int ModelVolume::get_mesh_errors_count() const int ModelVolume::get_mesh_errors_count() const
{ {
const stl_stats& stats = this->mesh.stl.stats; const stl_stats& stats = this->mesh().stl.stats;
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges; stats.facets_added + stats.facets_reversed + stats.backwards_edges;
@ -1557,7 +1548,7 @@ int ModelVolume::get_mesh_errors_count() const
const TriangleMesh& ModelVolume::get_convex_hull() const const TriangleMesh& ModelVolume::get_convex_hull() const
{ {
return m_convex_hull; return *m_convex_hull.get();
} }
ModelVolumeType ModelVolume::type_from_string(const std::string &s) ModelVolumeType ModelVolume::type_from_string(const std::string &s)
@ -1597,7 +1588,7 @@ std::string ModelVolume::type_to_string(const ModelVolumeType t)
// This is useful to assign different materials to different volumes of an object. // This is useful to assign different materials to different volumes of an object.
size_t ModelVolume::split(unsigned int max_extruders) size_t ModelVolume::split(unsigned int max_extruders)
{ {
TriangleMeshPtrs meshptrs = this->mesh.split(); TriangleMeshPtrs meshptrs = this->mesh().split();
if (meshptrs.size() <= 1) { if (meshptrs.size() <= 1) {
delete meshptrs.front(); delete meshptrs.front();
return 1; return 1;
@ -1614,7 +1605,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
mesh->repair(); mesh->repair();
if (idx == 0) if (idx == 0)
{ {
this->mesh = std::move(*mesh); this->set_mesh(std::move(*mesh));
this->calculate_convex_hull(); this->calculate_convex_hull();
// Assign a new unique ID, so that a new GLVolume will be generated. // Assign a new unique ID, so that a new GLVolume will be generated.
this->set_new_unique_id(); this->set_new_unique_id();
@ -1623,7 +1614,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh))); this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh)));
this->object->volumes[ivolume]->set_offset(Vec3d::Zero()); this->object->volumes[ivolume]->set_offset(Vec3d::Zero());
this->object->volumes[ivolume]->center_geometry(); this->object->volumes[ivolume]->center_geometry_after_creation();
this->object->volumes[ivolume]->translate(offset); this->object->volumes[ivolume]->translate(offset);
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1); this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders)); this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders));
@ -1689,24 +1680,33 @@ void ModelVolume::mirror(Axis axis)
set_mirror(mirror); set_mirror(mirror);
} }
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelVolume::scale_geometry(const Vec3d& versor) void ModelVolume::scale_geometry(const Vec3d& versor)
{ {
mesh.scale(versor); m_mesh->scale(versor);
m_convex_hull.scale(versor); m_convex_hull->scale(versor);
} }
void ModelVolume::transform_mesh(const Transform3d &mesh_trafo, bool fix_left_handed) void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_left_handed)
{ {
this->mesh.transform(mesh_trafo, fix_left_handed); TriangleMesh mesh = this->mesh();
this->m_convex_hull.transform(mesh_trafo, fix_left_handed); mesh.transform(mesh_trafo, fix_left_handed);
this->set_mesh(std::move(mesh));
TriangleMesh convex_hull = this->get_convex_hull();
convex_hull.transform(mesh_trafo, fix_left_handed);
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded. // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
this->set_new_unique_id(); this->set_new_unique_id();
} }
void ModelVolume::transform_mesh(const Matrix3d &matrix, bool fix_left_handed) void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_handed)
{ {
this->mesh.transform(matrix, fix_left_handed); TriangleMesh mesh = this->mesh();
this->m_convex_hull.transform(matrix, fix_left_handed); mesh.transform(matrix, fix_left_handed);
this->set_mesh(std::move(mesh));
TriangleMesh convex_hull = this->get_convex_hull();
convex_hull.transform(matrix, fix_left_handed);
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded. // Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
this->set_new_unique_id(); this->set_new_unique_id();
} }

View File

@ -7,7 +7,9 @@
#include "Point.hpp" #include "Point.hpp"
#include "TriangleMesh.hpp" #include "TriangleMesh.hpp"
#include "Slicing.hpp" #include "Slicing.hpp"
#include <map> #include <map>
#include <memory>
#include <string> #include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -261,6 +263,7 @@ public:
void rotate(double angle, const Vec3d& axis); void rotate(double angle, const Vec3d& axis);
void mirror(Axis axis); void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
void scale_mesh(const Vec3d& versor); void scale_mesh(const Vec3d& versor);
size_t materials_count() const; size_t materials_count() const;
@ -268,7 +271,6 @@ public:
bool needed_repair() const; bool needed_repair() const;
ModelObjectPtrs cut(size_t instance, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); // Note: z is in world coordinates ModelObjectPtrs cut(size_t instance, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); // Note: z is in world coordinates
void split(ModelObjectPtrs* new_objects); void split(ModelObjectPtrs* new_objects);
void repair();
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees, // Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure. // then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
// This situation is solved by baking in the instance transformation into the mesh vertices. // This situation is solved by baking in the instance transformation into the mesh vertices.
@ -340,7 +342,12 @@ class ModelVolume : public ModelBase
public: public:
std::string name; std::string name;
// The triangular model. // The triangular model.
TriangleMesh mesh; const TriangleMesh& mesh() const { return *m_mesh.get(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<TriangleMesh>(std::move(mesh)); }
void set_mesh(std::shared_ptr<TriangleMesh> &mesh) { m_mesh = mesh; }
void set_mesh(std::unique_ptr<TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
void reset_mesh() { m_mesh = std::make_shared<TriangleMesh>(); }
// Configuration parameters specific to an object model geometry or a modifier volume, // Configuration parameters specific to an object model geometry or a modifier volume,
// overriding the global Slic3r settings and the ModelObject settings. // overriding the global Slic3r settings and the ModelObject settings.
DynamicPrintConfig config; DynamicPrintConfig config;
@ -377,13 +384,16 @@ public:
void rotate(double angle, const Vec3d& axis); void rotate(double angle, const Vec3d& axis);
void mirror(Axis axis); void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
void scale_geometry(const Vec3d& versor); void scale_geometry(const Vec3d& versor);
// translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box // Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
void center_geometry(); // Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
void center_geometry_after_creation();
void calculate_convex_hull(); void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const; const TriangleMesh& get_convex_hull() const;
std::shared_ptr<const TriangleMesh> get_convex_hull_shared_ptr() const { return m_convex_hull; }
// Get count of errors in the mesh // Get count of errors in the mesh
int get_mesh_errors_count() const; int get_mesh_errors_count() const;
@ -430,17 +440,19 @@ protected:
explicit ModelVolume(const ModelVolume &rhs) = default; explicit ModelVolume(const ModelVolume &rhs) = default;
void set_model_object(ModelObject *model_object) { object = model_object; } void set_model_object(ModelObject *model_object) { object = model_object; }
void transform_mesh(const Transform3d& t, bool fix_left_handed); void transform_this_mesh(const Transform3d& t, bool fix_left_handed);
void transform_mesh(const Matrix3d& m, bool fix_left_handed); void transform_this_mesh(const Matrix3d& m, bool fix_left_handed);
private: private:
// Parent object owning this ModelVolume. // Parent object owning this ModelVolume.
ModelObject* object; ModelObject* object;
// The triangular model.
std::shared_ptr<TriangleMesh> m_mesh;
// Is it an object to be printed, or a modifier volume? // Is it an object to be printed, or a modifier volume?
ModelVolumeType m_type; ModelVolumeType m_type;
t_model_material_id m_material_id; t_model_material_id m_material_id;
// The convex hull of this model's mesh. // The convex hull of this model's mesh.
TriangleMesh m_convex_hull; std::shared_ptr<TriangleMesh> m_convex_hull;
Geometry::Transformation m_transformation; Geometry::Transformation m_transformation;
// flag to optimize the checking if the volume is splittable // flag to optimize the checking if the volume is splittable
@ -449,24 +461,24 @@ private:
// 1 -> is splittable // 1 -> is splittable
mutable int m_is_splittable{ -1 }; mutable int m_is_splittable{ -1 };
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(ModelVolumeType::MODEL_PART), object(object) ModelVolume(ModelObject *object, const TriangleMesh &mesh) : m_mesh(new TriangleMesh(mesh)), m_type(ModelVolumeType::MODEL_PART), object(object)
{ {
if (mesh.stl.stats.number_of_facets > 1) if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull(); calculate_convex_hull();
} }
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(ModelVolumeType::MODEL_PART), object(object) {} m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {}
// Copying an existing volume, therefore this volume will get a copy of the ID assigned. // Copying an existing volume, therefore this volume will get a copy of the ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other) : ModelVolume(ModelObject *object, const ModelVolume &other) :
ModelBase(other), // copy the ID ModelBase(other), // copy the ID
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{ {
this->set_material_id(other.material_id()); this->set_material_id(other.material_id());
} }
// Providing a new mesh, therefore this volume will get a new unique ID assigned. // Providing a new mesh, therefore this volume will get a new unique ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{ {
this->set_material_id(other.material_id()); this->set_material_id(other.material_id());
if (mesh.stl.stats.number_of_facets > 1) if (mesh.stl.stats.number_of_facets > 1)
@ -597,10 +609,6 @@ public:
static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true); static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true);
static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true); static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true);
/// Repair the ModelObjects of the current Model.
/// This function calls repair function on each TriangleMesh of each model object volume
void repair();
// Add a new ModelObject to this Model, generate a new ID for this ModelObject. // Add a new ModelObject to this Model, generate a new ID for this ModelObject.
ModelObject* add_object(); ModelObject* add_object();
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh); ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);

View File

@ -1797,7 +1797,7 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
if (! volumes.empty()) { if (! volumes.empty()) {
// Compose mesh. // Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volumes.front()->mesh); TriangleMesh mesh(volumes.front()->mesh());
mesh.transform(volumes.front()->get_matrix(), true); mesh.transform(volumes.front()->get_matrix(), true);
assert(mesh.repaired); assert(mesh.repaired);
if (volumes.size() == 1 && mesh.repaired) { if (volumes.size() == 1 && mesh.repaired) {
@ -1806,7 +1806,7 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
} }
for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) { for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) {
const ModelVolume &model_volume = *volumes[idx_volume]; const ModelVolume &model_volume = *volumes[idx_volume];
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);
} }
@ -1815,10 +1815,11 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
// apply XY shift // apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0); mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
// perform actual slicing // perform actual slicing
TriangleMeshSlicer mslicer;
const Print *print = this->print(); const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mesh.require_shared_vertices(); // TriangleMeshSlicer needs this // TriangleMeshSlicer needs shared vertices, also this calls the repair() function.
mesh.require_shared_vertices();
TriangleMeshSlicer mslicer;
mslicer.init(&mesh, callback); mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled(); m_print->throw_if_canceled();
@ -1832,7 +1833,7 @@ std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z,
std::vector<ExPolygons> layers; std::vector<ExPolygons> layers;
// Compose mesh. // Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volume.mesh); TriangleMesh mesh(volume.mesh());
mesh.transform(volume.get_matrix(), true); mesh.transform(volume.get_matrix(), true);
if (mesh.repaired) { if (mesh.repaired) {
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it. //FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
@ -1846,7 +1847,8 @@ std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z,
TriangleMeshSlicer mslicer; TriangleMeshSlicer mslicer;
const Print *print = this->print(); const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
mesh.require_shared_vertices(); // TriangleMeshSlicer needs this // TriangleMeshSlicer needs the shared vertices.
mesh.require_shared_vertices();
mslicer.init(&mesh, callback); mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled(); m_print->throw_if_canceled();

View File

@ -227,7 +227,7 @@ std::vector<coordf_t> layer_height_profile_adaptive(
as.set_slicing_parameters(slicing_params); as.set_slicing_parameters(slicing_params);
for (const ModelVolume *volume : volumes) for (const ModelVolume *volume : volumes)
if (volume->is_model_part()) if (volume->is_model_part())
as.add_mesh(&volume->mesh); as.add_mesh(&volume->mesh());
as.prepare(); as.prepare();
// 2) Generate layers using the algorithm of @platsch // 2) Generate layers using the algorithm of @platsch

View File

@ -82,10 +82,13 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
// #define SLIC3R_TRACE_REPAIR // #define SLIC3R_TRACE_REPAIR
void TriangleMesh::repair() void TriangleMesh::repair(bool update_shared_vertices)
{ {
if (this->repaired) if (this->repaired) {
if (update_shared_vertices)
this->require_shared_vertices();
return; return;
}
// admesh fails when repairing empty meshes // admesh fails when repairing empty meshes
if (this->stl.stats.number_of_facets == 0) if (this->stl.stats.number_of_facets == 0)
@ -97,6 +100,7 @@ void TriangleMesh::repair()
#ifdef SLIC3R_TRACE_REPAIR #ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact"; BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact";
#endif /* SLIC3R_TRACE_REPAIR */ #endif /* SLIC3R_TRACE_REPAIR */
assert(stl_validate(&this->stl));
stl_check_facets_exact(&stl); stl_check_facets_exact(&stl);
assert(stl_validate(&this->stl)); assert(stl_validate(&this->stl));
stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge); stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
@ -179,6 +183,12 @@ void TriangleMesh::repair()
this->repaired = true; this->repaired = true;
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished"; BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
// This call should be quite cheap, a lot of code requires the indexed_triangle_set data structure,
// and it is risky to generate such a structure once the meshes are shared. Do it now.
this->its.clear();
if (update_shared_vertices)
this->require_shared_vertices();
} }
float TriangleMesh::volume() float TriangleMesh::volume()
@ -238,8 +248,7 @@ bool TriangleMesh::needed_repair() const
void TriangleMesh::WriteOBJFile(const char* output_file) void TriangleMesh::WriteOBJFile(const char* output_file)
{ {
stl_generate_shared_vertices(&stl, its); its_write_obj(this->its, output_file);
its_write_obj(its, output_file);
} }
void TriangleMesh::scale(float factor) void TriangleMesh::scale(float factor)
@ -294,6 +303,7 @@ void TriangleMesh::rotate(float angle, const Vec3d& axis)
Transform3d m = Transform3d::Identity(); Transform3d m = Transform3d::Identity();
m.rotate(Eigen::AngleAxisd(angle, axis_norm)); m.rotate(Eigen::AngleAxisd(angle, axis_norm));
stl_transform(&stl, m); stl_transform(&stl, m);
its_transform(its, m);
} }
void TriangleMesh::mirror(const Axis &axis) void TriangleMesh::mirror(const Axis &axis)
@ -311,22 +321,26 @@ void TriangleMesh::mirror(const Axis &axis)
void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed) void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
{ {
stl_transform(&stl, t); stl_transform(&stl, t);
this->its.clear(); 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(); this->repair(false);
stl_reverse_all_facets(&stl); stl_reverse_all_facets(&stl);
this->its.clear();
this->require_shared_vertices();
} }
} }
void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed) void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
{ {
stl_transform(&stl, m); stl_transform(&stl, m);
this->its.clear(); 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. // Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
this->repair(); this->repair(false);
stl_reverse_all_facets(&stl); stl_reverse_all_facets(&stl);
this->its.clear();
this->require_shared_vertices();
} }
} }
@ -482,7 +496,6 @@ ExPolygons TriangleMesh::horizontal_projection() const
// 2D convex hull of a 3D mesh projected into the Z=0 plane. // 2D convex hull of a 3D mesh projected into the Z=0 plane.
Polygon TriangleMesh::convex_hull() Polygon TriangleMesh::convex_hull()
{ {
this->require_shared_vertices();
Points pp; Points pp;
pp.reserve(this->its.vertices.size()); pp.reserve(this->its.vertices.size());
for (size_t i = 0; i < this->its.vertices.size(); ++ i) { for (size_t i = 0; i < this->its.vertices.size(); ++ i) {
@ -519,26 +532,32 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c
TriangleMesh TriangleMesh::convex_hull_3d() const TriangleMesh TriangleMesh::convex_hull_3d() const
{ {
// Helper struct for qhull:
struct PointForQHull{
PointForQHull(float x_p, float y_p, float z_p) : x((realT)x_p), y((realT)y_p), z((realT)z_p) {}
realT x, y, z;
};
std::vector<PointForQHull> src_vertices;
// We will now fill the vector with input points for computation:
for (const stl_facet &facet : stl.facet_start)
for (int i = 0; i < 3; ++ i) {
const stl_vertex& v = facet.vertex[i];
src_vertices.emplace_back(v(0), v(1), v(2));
}
// The qhull call: // The qhull call:
orgQhull::Qhull qhull; orgQhull::Qhull qhull;
qhull.disableOutputStream(); // we want qhull to be quiet qhull.disableOutputStream(); // we want qhull to be quiet
std::vector<realT> src_vertices;
try try
{ {
qhull.runQhull("", 3, (int)src_vertices.size(), (const realT*)(src_vertices.data()), "Qt"); if (this->has_shared_vertices()) {
#if REALfloat
qhull.runQhull("", 3, (int)this->its.vertices.size() / 3, (const realT*)(this->its.vertices.front().data()), "Qt");
#else
src_vertices.reserve(this->its.vertices() * 3);
// We will now fill the vector with input points for computation:
for (const stl_vertex &v : ths->its.vertices.size())
for (int i = 0; i < 3; ++ i)
src_vertices.emplace_back(v(i));
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
#endif
} else {
src_vertices.reserve(this->stl.facet_start.size() * 9);
// We will now fill the vector with input points for computation:
for (const stl_facet &f : this->stl.facet_start)
for (int i = 0; i < 3; ++ i)
for (int j = 0; j < 3; ++ j)
src_vertices.emplace_back(f.vertex[i](j));
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
}
} }
catch (...) catch (...)
{ {
@ -566,7 +585,6 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
TriangleMesh output_mesh(dst_vertices, facets); TriangleMesh output_mesh(dst_vertices, facets);
output_mesh.repair(); output_mesh.repair();
output_mesh.require_shared_vertices();
return output_mesh; return output_mesh;
} }

View File

@ -33,7 +33,7 @@ public:
bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); } bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); }
bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); } bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); }
bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); } bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); }
void repair(); void repair(bool update_shared_vertices = true);
float volume(); float volume();
void check_topology(); void check_topology();
bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; } bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; }

View File

@ -241,8 +241,6 @@ GLVolume::GLVolume(float r, float g, float b, float a)
: m_transformed_bounding_box_dirty(true) : m_transformed_bounding_box_dirty(true)
, m_sla_shift_z(0.0) , m_sla_shift_z(0.0)
, m_transformed_convex_hull_bounding_box_dirty(true) , m_transformed_convex_hull_bounding_box_dirty(true)
, m_convex_hull(nullptr)
, m_convex_hull_owned(false)
// geometry_id == 0 -> invalid // geometry_id == 0 -> invalid
, geometry_id(std::pair<size_t, size_t>(0, 0)) , geometry_id(std::pair<size_t, size_t>(0, 0))
, extruder_id(0) , extruder_id(0)
@ -268,12 +266,6 @@ GLVolume::GLVolume(float r, float g, float b, float a)
set_render_color(r, g, b, a); set_render_color(r, g, b, a);
} }
GLVolume::~GLVolume()
{
if (m_convex_hull_owned)
delete m_convex_hull;
}
void GLVolume::set_render_color(float r, float g, float b, float a) void GLVolume::set_render_color(float r, float g, float b, float a)
{ {
render_color[0] = r; render_color[0] = r;
@ -335,12 +327,6 @@ void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume)
color[3] = model_volume->is_model_part() ? 1.f : 0.5f; color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
} }
void GLVolume::set_convex_hull(const TriangleMesh *convex_hull, bool owned)
{
m_convex_hull = convex_hull;
m_convex_hull_owned = owned;
}
Transform3d GLVolume::world_matrix() const Transform3d GLVolume::world_matrix() const
{ {
Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix();
@ -377,7 +363,7 @@ const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const
{ {
return (m_convex_hull != nullptr && m_convex_hull->stl.stats.number_of_facets > 0) ? return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ?
m_convex_hull->transformed_bounding_box(trafo) : m_convex_hull->transformed_bounding_box(trafo) :
bounding_box.transformed(trafo); bounding_box.transformed(trafo);
} }
@ -587,7 +573,7 @@ int GLVolumeCollection::load_object_volume(
const ModelVolume *model_volume = model_object->volumes[volume_idx]; const ModelVolume *model_volume = model_object->volumes[volume_idx];
const int extruder_id = model_volume->extruder_id(); const int extruder_id = model_volume->extruder_id();
const ModelInstance *instance = model_object->instances[instance_idx]; const ModelInstance *instance = model_object->instances[instance_idx];
const TriangleMesh& mesh = model_volume->mesh; const TriangleMesh& mesh = model_volume->mesh();
float color[4]; float color[4];
memcpy(color, GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); memcpy(color, GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
/* if (model_volume->is_support_blocker()) { /* if (model_volume->is_support_blocker()) {
@ -613,7 +599,7 @@ int GLVolumeCollection::load_object_volume(
if (model_volume->is_model_part()) if (model_volume->is_model_part())
{ {
// GLVolume will reference a convex hull from model_volume! // GLVolume will reference a convex hull from model_volume!
v.set_convex_hull(&model_volume->get_convex_hull(), false); v.set_convex_hull(model_volume->get_convex_hull_shared_ptr());
if (extruder_id != -1) if (extruder_id != -1)
v.extruder_id = extruder_id; v.extruder_id = extruder_id;
} }
@ -656,7 +642,10 @@ void GLVolumeCollection::load_object_auxiliary(
v.composite_id = GLVolume::CompositeID(obj_idx, - int(milestone), (int)instance_idx.first); v.composite_id = GLVolume::CompositeID(obj_idx, - int(milestone), (int)instance_idx.first);
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id); v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true); if (&instance_idx == &instances.back())
v.set_convex_hull(std::move(convex_hull));
else
v.set_convex_hull(convex_hull);
v.is_modifier = false; v.is_modifier = false;
v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree); v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree);
v.set_instance_transformation(model_instance.get_transformation()); v.set_instance_transformation(model_instance.get_transformation());

View File

@ -10,6 +10,7 @@
#include "slic3r/GUI/GLCanvas3DManager.hpp" #include "slic3r/GUI/GLCanvas3DManager.hpp"
#include <functional> #include <functional>
#include <memory>
#ifndef NDEBUG #ifndef NDEBUG
#define HAS_GLSAFE #define HAS_GLSAFE
@ -243,7 +244,6 @@ public:
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f); GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
~GLVolume();
private: private:
Geometry::Transformation m_instance_transformation; Geometry::Transformation m_instance_transformation;
@ -255,10 +255,8 @@ private:
mutable BoundingBoxf3 m_transformed_bounding_box; mutable BoundingBoxf3 m_transformed_bounding_box;
// Whether or not is needed to recalculate the transformed bounding box. // Whether or not is needed to recalculate the transformed bounding box.
mutable bool m_transformed_bounding_box_dirty; mutable bool m_transformed_bounding_box_dirty;
// Pointer to convex hull of the original mesh, if any. // Convex hull of the volume, if any.
// This object may or may not own the convex hull instance based on m_convex_hull_owned std::shared_ptr<const TriangleMesh> m_convex_hull;
const TriangleMesh* m_convex_hull;
bool m_convex_hull_owned;
// Bounding box of this volume, in unscaled coordinates. // Bounding box of this volume, in unscaled coordinates.
mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box; mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box;
// Whether or not is needed to recalculate the transformed convex hull bounding box. // Whether or not is needed to recalculate the transformed convex hull bounding box.
@ -395,7 +393,9 @@ public:
double get_sla_shift_z() const { return m_sla_shift_z; } double get_sla_shift_z() const { return m_sla_shift_z; }
void set_sla_shift_z(double z) { m_sla_shift_z = z; } void set_sla_shift_z(double z) { m_sla_shift_z = z; }
void set_convex_hull(const TriangleMesh *convex_hull, bool owned); void set_convex_hull(std::shared_ptr<const TriangleMesh> &convex_hull) { m_convex_hull = convex_hull; }
void set_convex_hull(const TriangleMesh &convex_hull) { m_convex_hull = std::make_shared<const TriangleMesh>(convex_hull); }
void set_convex_hull(TriangleMesh &&convex_hull) { m_convex_hull = std::make_shared<const TriangleMesh>(std::move(convex_hull)); }
int object_idx() const { return this->composite_id.object_id; } int object_idx() const { return this->composite_id.object_id; }
int volume_idx() const { return this->composite_id.volume_id; } int volume_idx() const { return this->composite_id.volume_id; }

View File

@ -5498,7 +5498,7 @@ void GLCanvas3D::_load_sla_shells()
v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0)); v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0));
v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation)); v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation));
v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.); v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.);
v.set_convex_hull(new TriangleMesh(std::move(mesh.convex_hull_3d())), true); v.set_convex_hull(mesh.convex_hull_3d());
}; };
// adds objects' volumes // adds objects' volumes

View File

@ -261,7 +261,7 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /
const stl_stats& stats = vol_idx == -1 ? const stl_stats& stats = vol_idx == -1 ?
(*m_objects)[obj_idx]->get_object_stl_stats() : (*m_objects)[obj_idx]->get_object_stl_stats() :
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh.stl.stats; (*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stl.stats;
std::map<std::string, int> error_msg = { std::map<std::string, int> error_msg = {
{ L("degenerate facets"), stats.degenerate_facets }, { L("degenerate facets"), stats.degenerate_facets },
@ -1592,7 +1592,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
// First (any) GLVolume of the selected instance. They all share the same instance matrix. // First (any) GLVolume of the selected instance. They all share the same instance matrix.
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
// Transform the new modifier to be aligned with the print bed. // Transform the new modifier to be aligned with the print bed.
const BoundingBoxf3 mesh_bb = new_volume->mesh.bounding_box(); const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box();
new_volume->set_transformation(volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb)); new_volume->set_transformation(volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
// Set the modifier position. // Set the modifier position.
auto offset = (type_name == "Slab") ? auto offset = (type_name == "Slab") ?

View File

@ -388,8 +388,7 @@ void GLGizmoSlaSupports::update_mesh()
wxBusyCursor wait; wxBusyCursor wait;
// this way we can use that mesh directly. // this way we can use that mesh directly.
// This mesh does not account for the possible Z up SLA offset. // This mesh does not account for the possible Z up SLA offset.
m_mesh = &m_model_object->volumes.front()->mesh; m_mesh = &m_model_object->volumes.front()->mesh();
const_cast<TriangleMesh*>(m_mesh)->require_shared_vertices(); // TriangleMeshSlicer needs this
m_its = &m_mesh->its; m_its = &m_mesh->its;
m_current_mesh_model_id = m_model_object->id(); m_current_mesh_model_id = m_model_object->id();
m_editing_mode = false; m_editing_mode = false;

View File

@ -3565,7 +3565,7 @@ void Plater::export_stl(bool extended, bool selection_only)
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());
mesh.translate(-model_object->origin_translation.cast<float>()); mesh.translate(-model_object->origin_translation.cast<float>());
} }

View File

@ -253,7 +253,7 @@ ModelMaterial::attributes()
Ref<DynamicPrintConfig> config() Ref<DynamicPrintConfig> config()
%code%{ RETVAL = &THIS->config; %}; %code%{ RETVAL = &THIS->config; %};
Ref<TriangleMesh> mesh() Ref<TriangleMesh> mesh()
%code%{ RETVAL = &THIS->mesh; %}; %code%{ RETVAL = &THIS->mesh(); %};
bool modifier() bool modifier()
%code%{ RETVAL = THIS->is_modifier(); %}; %code%{ RETVAL = THIS->is_modifier(); %};