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") {
std::vector<Model> new_models;
for (auto &model : m_models) {
model.repair();
model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
size_t num_objects = model.objects.size();
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") {
for (auto &model : m_models)
model.repair();
// Models are repaired by default.
//for (auto &model : m_models)
// model.repair();
} else {
boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
return 1;

View File

@ -59,7 +59,7 @@ struct HashEdge {
// Ensure identical vertex ordering of equal edges.
// This method is numerically robust.
if (stl_vertex_lower(*a, *b)) {
if (vertex_lower(*a, *b)) {
} else {
// This edge is loaded backwards.
std::swap(a, b);
@ -110,6 +110,12 @@ struct HashEdge {
}
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 {
@ -440,7 +446,9 @@ void stl_check_facets_exact(stl_file *stl)
stl_facet &facet = stl->facet_start[i];
if (facet.vertex[0] == facet.vertex[1] || facet.vertex[1] == facet.vertex[2] || facet.vertex[0] == facet.vertex[2]) {
// 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.degenerate_facets += 1;
} else
@ -526,10 +534,8 @@ void stl_remove_unconnected_facets(stl_file *stl)
assert(false);
}
if (facet_number == -- stl->stats.number_of_facets)
// Removing the last face is easy, just forget the last face.
return;
if (facet_number < -- stl->stats.number_of_facets) {
// Removing a face, which was not the last one.
// 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];
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;
}
}
stl->facet_start.pop_back();
stl->neighbors_start.pop_back();
};
auto remove_degenerate = [stl, remove_facet](int facet)

View File

@ -128,6 +128,8 @@ struct indexed_triangle_set
void clear() { indices.clear(); vertices.clear(); }
std::vector<stl_triangle_vertex_indices> indices;
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);
@ -186,7 +188,7 @@ template<typename T>
inline void stl_transform(stl_file *stl, 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 (size_t i = 0; i < stl->stats.number_of_facets; ++i) {
for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
stl_facet &f = stl->facet_start[i];
for (size_t j = 0; j < 3; ++j)
f.vertex[j] = (t * f.vertex[j].template cast<T>()).template cast<float>().eval();
@ -199,7 +201,7 @@ inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Aff
template<typename T>
inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m)
{
for (size_t i = 0; i < stl->stats.number_of_facets; ++i) {
for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
stl_facet &f = stl->facet_start[i];
for (size_t j = 0; j < 3; ++j)
f.vertex[j] = (m * f.vertex[j].template cast<T>()).template cast<float>().eval();
@ -209,6 +211,34 @@ inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::Don
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 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);
@ -225,10 +255,6 @@ inline void stl_normalize_vector(stl_normal &normal) {
else
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_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

@ -147,7 +147,7 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first)
rewind(fp);
char normal_buf[3][32];
for (uint32_t i = first_facet; i < stl->stats.number_of_facets; ++i) {
for (uint32_t i = first_facet; i < stl->stats.number_of_facets; ++ i) {
stl_facet facet;
if (stl->stats.type == binary) {

View File

@ -108,7 +108,7 @@ void stl_scale_versor(stl_file *stl, const stl_vertex &versor)
static void calculate_normals(stl_file *stl)
{
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_normalize_vector(normal);
stl->facet_start[i].normal = normal;

View File

@ -1489,9 +1489,9 @@ namespace Slic3r {
}
// 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;
ModelVolume* volume = object.add_volume(TriangleMesh());
stl_file& stl = volume->mesh.stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)triangles_count;
stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
@ -1510,8 +1510,10 @@ namespace Slic3r {
}
stl_get_size(&stl);
volume->mesh.repair();
volume->center_geometry();
triangle_mesh.repair();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
volume->center_geometry_after_creation();
volume->calculate_convex_hull();
// apply volume's name and config data
@ -1879,11 +1881,14 @@ namespace Slic3r {
if (volume == nullptr)
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;
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())
{
add_error("Found invalid mesh");
@ -1916,7 +1921,7 @@ namespace Slic3r {
VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
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
volume_it->second.first_triangle_id = triangles_count;

View File

@ -522,7 +522,8 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_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.number_of_facets = int(m_volume_facets.size() / 3);
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));
}
stl_get_size(&stl);
m_volume->mesh.repair();
m_volume->center_geometry();
mesh.repair();
m_volume->set_mesh(std::move(mesh));
m_volume->center_geometry_after_creation();
m_volume->calculate_convex_hull();
m_volume_facets.clear();
m_volume = nullptr;
@ -923,10 +925,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
int num_vertices = 0;
for (ModelVolume *volume : object->volumes) {
vertices_offsets.push_back(num_vertices);
if (! volume->mesh.repaired)
if (! volume->mesh().repaired)
throw std::runtime_error("store_amf() requires repair()");
volume->mesh.require_shared_vertices();
const indexed_triangle_set &its = volume->mesh.its;
if (! volume->mesh().has_shared_vertices())
throw std::runtime_error("store_amf() requires shared vertices");
const indexed_triangle_set &its = volume->mesh().its;
const Transform3d& matrix = volume->get_matrix();
for (size_t i = 0; i < its.vertices.size(); ++i) {
stream << " <vertex>\n";
@ -955,10 +958,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
if (volume->is_modifier())
stream << " <metadata type=\"slic3r.modifier\">1</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";
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 << " </volume>\n";

View File

@ -160,12 +160,6 @@ Model Model::read_from_archive(const std::string &input_file, DynamicPrintConfig
return model;
}
void Model::repair()
{
for (ModelObject *o : this->objects)
o->repair();
}
ModelObject* Model::add_object()
{
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)
return false;
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())
zmin = zmin_this;
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);
this->volumes.push_back(v);
v->center_geometry();
v->center_geometry_after_creation();
this->invalidate_bounding_box();
return v;
}
@ -688,7 +682,7 @@ ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh)
{
ModelVolume* v = new ModelVolume(this, std::move(mesh));
this->volumes.push_back(v);
v->center_geometry();
v->center_geometry_after_creation();
this->invalidate_bounding_box();
return v;
}
@ -697,7 +691,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other)
{
ModelVolume* v = new ModelVolume(this, other);
this->volumes.push_back(v);
v->center_geometry();
v->center_geometry_after_creation();
this->invalidate_bounding_box();
return v;
}
@ -706,7 +700,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&me
{
ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
this->volumes.push_back(v);
v->center_geometry();
v->center_geometry_after_creation();
this->invalidate_bounding_box();
return v;
}
@ -827,7 +821,7 @@ TriangleMesh ModelObject::raw_mesh() const
for (const ModelVolume *v : this->volumes)
if (v->is_model_part())
{
TriangleMesh vol_mesh(v->mesh);
TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh);
}
@ -840,7 +834,7 @@ TriangleMesh ModelObject::full_raw_mesh() const
TriangleMesh mesh;
for (const ModelVolume *v : this->volumes)
{
TriangleMesh vol_mesh(v->mesh);
TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh);
}
@ -854,7 +848,7 @@ const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const
m_raw_mesh_bounding_box.reset();
for (const ModelVolume *v : this->volumes)
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;
}
@ -863,7 +857,7 @@ BoundingBoxf3 ModelObject::full_raw_mesh_bounding_box() const
{
BoundingBoxf3 bb;
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;
}
@ -881,7 +875,7 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const
for (const ModelVolume *v : this->volumes)
{
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;
@ -895,7 +889,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
for (ModelVolume *v : this->volumes)
{
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;
}
@ -909,10 +903,10 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
for (const ModelVolume *v : this->volumes)
if (v->is_model_part()) {
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()) {
// 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 (size_t j = 0; j < 3; ++ j) {
Vec3d p = trafo * facet.vertex[j].cast<double>();
@ -1038,6 +1032,7 @@ void ModelObject::mirror(Axis axis)
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)
{
for (ModelVolume *v : this->volumes)
@ -1061,14 +1056,14 @@ size_t ModelObject::facets_count() const
size_t num = 0;
for (const ModelVolume *v : this->volumes)
if (v->is_model_part())
num += v->mesh.stl.stats.number_of_facets;
num += v->mesh().stl.stats.number_of_facets;
return num;
}
bool ModelObject::needed_repair() const
{
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 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.
// 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
volume->mesh.require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer tms(&volume->mesh);
TriangleMeshSlicer tms(&mesh);
tms.cut(float(z), &upper_mesh, &lower_mesh);
// Reset volume transformation except for offset
@ -1232,7 +1228,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
}
ModelVolume* volume = this->volumes.front();
TriangleMeshPtrs meshptrs = volume->mesh.split();
TriangleMeshPtrs meshptrs = volume->mesh().split();
for (TriangleMesh *mesh : meshptrs) {
mesh->repair();
@ -1259,12 +1255,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
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,
// 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.
@ -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.;
// 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);
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.
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));
@ -1447,7 +1438,7 @@ std::string ModelObject::get_export_filename() const
stl_stats ModelObject::get_object_stl_stats() const
{
if (this->volumes.size() == 1)
return this->volumes[0]->mesh.stl.stats;
return this->volumes[0]->mesh().stl.stats;
stl_stats full_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())
continue;
const stl_stats& stats = volume->mesh.stl.stats;
const stl_stats& stats = volume->mesh().stl.stats;
// initialize full_stats (for repaired errors)
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
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;
}
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()))
{
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_mesh->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);
}
}
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
{
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 +
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
{
return m_convex_hull;
return *m_convex_hull.get();
}
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.
size_t ModelVolume::split(unsigned int max_extruders)
{
TriangleMeshPtrs meshptrs = this->mesh.split();
TriangleMeshPtrs meshptrs = this->mesh().split();
if (meshptrs.size() <= 1) {
delete meshptrs.front();
return 1;
@ -1614,7 +1605,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
mesh->repair();
if (idx == 0)
{
this->mesh = std::move(*mesh);
this->set_mesh(std::move(*mesh));
this->calculate_convex_hull();
// Assign a new unique ID, so that a new GLVolume will be generated.
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[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]->name = name + "_" + std::to_string(idx + 1);
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);
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelVolume::scale_geometry(const Vec3d& versor)
{
mesh.scale(versor);
m_convex_hull.scale(versor);
m_mesh->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);
this->m_convex_hull.transform(mesh_trafo, fix_left_handed);
TriangleMesh mesh = this->mesh();
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.
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);
this->m_convex_hull.transform(matrix, fix_left_handed);
TriangleMesh mesh = this->mesh();
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.
this->set_new_unique_id();
}

View File

@ -7,7 +7,9 @@
#include "Point.hpp"
#include "TriangleMesh.hpp"
#include "Slicing.hpp"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
@ -261,6 +263,7 @@ public:
void rotate(double angle, const Vec3d& 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);
size_t materials_count() const;
@ -268,7 +271,6 @@ public:
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
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,
// 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.
@ -340,7 +342,12 @@ class ModelVolume : public ModelBase
public:
std::string name;
// 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,
// overriding the global Slic3r settings and the ModelObject settings.
DynamicPrintConfig config;
@ -377,13 +384,16 @@ public:
void rotate(double angle, const Vec3d& 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);
// 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();
// Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
// 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();
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
int get_mesh_errors_count() const;
@ -430,17 +440,19 @@ protected:
explicit ModelVolume(const ModelVolume &rhs) = default;
void set_model_object(ModelObject *model_object) { object = model_object; }
void transform_mesh(const Transform3d& t, bool fix_left_handed);
void transform_mesh(const Matrix3d& m, bool fix_left_handed);
void transform_this_mesh(const Transform3d& t, bool fix_left_handed);
void transform_this_mesh(const Matrix3d& m, bool fix_left_handed);
private:
// Parent object owning this ModelVolume.
ModelObject* object;
// The triangular model.
std::shared_ptr<TriangleMesh> m_mesh;
// Is it an object to be printed, or a modifier volume?
ModelVolumeType m_type;
t_model_material_id m_material_id;
// The convex hull of this model's mesh.
TriangleMesh m_convex_hull;
std::shared_ptr<TriangleMesh> m_convex_hull;
Geometry::Transformation m_transformation;
// flag to optimize the checking if the volume is splittable
@ -449,24 +461,24 @@ private:
// 1 -> is splittable
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)
calculate_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.
ModelVolume(ModelObject *object, const ModelVolume &other) :
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());
}
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
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());
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_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.
ModelObject* add_object();
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()) {
// Compose mesh.
//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);
assert(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) {
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);
mesh.merge(vol_mesh);
}
@ -1815,10 +1815,11 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
// apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
// perform actual slicing
TriangleMeshSlicer mslicer;
const Print *print = this->print();
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.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled();
@ -1832,7 +1833,7 @@ std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z,
std::vector<ExPolygons> layers;
// Compose mesh.
//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);
if (mesh.repaired) {
//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;
const Print *print = this->print();
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.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
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);
for (const ModelVolume *volume : volumes)
if (volume->is_model_part())
as.add_mesh(&volume->mesh);
as.add_mesh(&volume->mesh());
as.prepare();
// 2) Generate layers using the algorithm of @platsch

View File

@ -82,10 +82,13 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
// #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;
}
// admesh fails when repairing empty meshes
if (this->stl.stats.number_of_facets == 0)
@ -97,6 +100,7 @@ void TriangleMesh::repair()
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact";
#endif /* SLIC3R_TRACE_REPAIR */
assert(stl_validate(&this->stl));
stl_check_facets_exact(&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);
@ -179,6 +183,12 @@ void TriangleMesh::repair()
this->repaired = true;
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()
@ -238,8 +248,7 @@ bool TriangleMesh::needed_repair() const
void TriangleMesh::WriteOBJFile(const char* output_file)
{
stl_generate_shared_vertices(&stl, its);
its_write_obj(its, output_file);
its_write_obj(this->its, output_file);
}
void TriangleMesh::scale(float factor)
@ -294,6 +303,7 @@ void TriangleMesh::rotate(float angle, const Vec3d& axis)
Transform3d m = Transform3d::Identity();
m.rotate(Eigen::AngleAxisd(angle, axis_norm));
stl_transform(&stl, m);
its_transform(its, m);
}
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)
{
stl_transform(&stl, t);
this->its.clear();
its_transform(its, t);
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.
this->repair();
this->repair(false);
stl_reverse_all_facets(&stl);
this->its.clear();
this->require_shared_vertices();
}
}
void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
{
stl_transform(&stl, m);
this->its.clear();
its_transform(its, m);
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.
this->repair();
this->repair(false);
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.
Polygon TriangleMesh::convex_hull()
{
this->require_shared_vertices();
Points pp;
pp.reserve(this->its.vertices.size());
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
{
// 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:
orgQhull::Qhull qhull;
qhull.disableOutputStream(); // we want qhull to be quiet
std::vector<realT> src_vertices;
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 (...)
{
@ -566,7 +585,6 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
TriangleMesh output_mesh(dst_vertices, facets);
output_mesh.repair();
output_mesh.require_shared_vertices();
return output_mesh;
}

View File

@ -33,7 +33,7 @@ public:
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_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); }
void repair();
void repair(bool update_shared_vertices = true);
float volume();
void check_topology();
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_sla_shift_z(0.0)
, m_transformed_convex_hull_bounding_box_dirty(true)
, m_convex_hull(nullptr)
, m_convex_hull_owned(false)
// geometry_id == 0 -> invalid
, geometry_id(std::pair<size_t, size_t>(0, 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);
}
GLVolume::~GLVolume()
{
if (m_convex_hull_owned)
delete m_convex_hull;
}
void GLVolume::set_render_color(float r, float g, float b, float a)
{
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;
}
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 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
{
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) :
bounding_box.transformed(trafo);
}
@ -587,7 +573,7 @@ int GLVolumeCollection::load_object_volume(
const ModelVolume *model_volume = model_object->volumes[volume_idx];
const int extruder_id = model_volume->extruder_id();
const ModelInstance *instance = model_object->instances[instance_idx];
const TriangleMesh& mesh = model_volume->mesh;
const TriangleMesh& mesh = model_volume->mesh();
float color[4];
memcpy(color, GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
/* if (model_volume->is_support_blocker()) {
@ -613,7 +599,7 @@ int GLVolumeCollection::load_object_volume(
if (model_volume->is_model_part())
{
// 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)
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.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.
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.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree);
v.set_instance_transformation(model_instance.get_transformation());

View File

@ -10,6 +10,7 @@
#include "slic3r/GUI/GLCanvas3DManager.hpp"
#include <functional>
#include <memory>
#ifndef NDEBUG
#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(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
~GLVolume();
private:
Geometry::Transformation m_instance_transformation;
@ -255,10 +255,8 @@ private:
mutable BoundingBoxf3 m_transformed_bounding_box;
// Whether or not is needed to recalculate the transformed bounding box.
mutable bool m_transformed_bounding_box_dirty;
// Pointer to convex hull of the original mesh, if any.
// This object may or may not own the convex hull instance based on m_convex_hull_owned
const TriangleMesh* m_convex_hull;
bool m_convex_hull_owned;
// Convex hull of the volume, if any.
std::shared_ptr<const TriangleMesh> m_convex_hull;
// Bounding box of this volume, in unscaled coordinates.
mutable BoundingBoxf3 m_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; }
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 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_rotation(Vec3d(0.0, 0.0, (double)instance.rotation));
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

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 ?
(*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 = {
{ 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.
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
// 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));
// Set the modifier position.
auto offset = (type_name == "Slab") ?

View File

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

View File

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

View File

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