diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index f71e33636..c10ae247f 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -57,6 +57,14 @@ using namespace Slic3r; int CLI::run(int argc, char **argv) { + +#ifdef __WXGTK__ + // On Linux, wxGTK has no support for Wayland, and the app crashes on + // startup if gtk3 is used. This env var has to be set explicitly to + // instruct the window manager to fall back to X server mode. + ::setenv("GDK_BACKEND", "x11", /* replace */ true); +#endif + // Switch boost::filesystem to utf8. try { boost::nowide::nowide_filesystem(); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 9dc396443..9fda79a13 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -591,6 +591,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type) vertex.fan_speed = m_fan_speed; vertex.extruder_id = m_extruder_id; vertex.cp_color_id = m_cp_color.current; + vertex.time = static_cast(m_result.moves.size()); m_result.moves.emplace_back(vertex); } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 221214836..4f6cf7430 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -82,6 +82,7 @@ namespace Slic3r { float height{ 0.0f }; // mm float mm3_per_mm{ 0.0f }; float fan_speed{ 0.0f }; // percentage + float time{ 0.0f }; // s float volumetric_rate() const { return feedrate * mm3_per_mm; } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 619d80c43..98f595d91 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1040,6 +1040,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b for (ModelVolume *volume : volumes) { const auto volume_matrix = volume->get_matrix(); + volume->m_supported_facets.clear(); + if (! volume->is_model_part()) { // Modifiers are not cut, but we still need to add the instance transformation // to the modifier volume transformation to preserve their shape properly. @@ -1739,6 +1741,41 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const return ret; } + +std::vector FacetsAnnotation::get_facets(FacetSupportType type) const +{ + std::vector out; + for (auto& [facet_idx, this_type] : m_data) + if (this_type == type) + out.push_back(facet_idx); + return out; +} + + + +void FacetsAnnotation::set_facet(int idx, FacetSupportType type) +{ + bool changed = true; + + if (type == FacetSupportType::NONE) + changed = m_data.erase(idx) != 0; + else + m_data[idx] = type; + + if (changed) + update_timestamp(); +} + + + +void FacetsAnnotation::clear() +{ + m_data.clear(); + update_timestamp(); +} + + + // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. bool model_object_list_equal(const Model &model_old, const Model &model_new) @@ -1802,6 +1839,16 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO return false; } +bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new) { + assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART)); + assert(mo.volumes.size() == mo_new.volumes.size()); + for (size_t i=0; im_supported_facets.is_same_as(mo.volumes[i]->m_supported_facets)) + return true; + } + return false; +}; + extern bool model_has_multi_part_objects(const Model &model) { for (const ModelObject *model_object : model.objects) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index a0c5f4e8a..cd2c4957d 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace cereal { class BinaryInputArchive; @@ -214,8 +215,8 @@ public: when user expects that. */ Vec3d origin_translation; - Model* get_model() { return m_model; }; - const Model* get_model() const { return m_model; }; + Model* get_model() { return m_model; } + const Model* get_model() const { return m_model; } ModelVolume* add_volume(const TriangleMesh &mesh); ModelVolume* add_volume(TriangleMesh &&mesh); @@ -391,6 +392,34 @@ enum class ModelVolumeType : int { SUPPORT_BLOCKER, }; +enum class FacetSupportType : int8_t { + NONE = 0, + ENFORCER = 1, + BLOCKER = 2 +}; + +class FacetsAnnotation { +public: + using ClockType = std::chrono::steady_clock; + + std::vector get_facets(FacetSupportType type) const; + void set_facet(int idx, FacetSupportType type); + void clear(); + + ClockType::time_point get_timestamp() const { return timestamp; } + bool is_same_as(const FacetsAnnotation& other) const { + return timestamp == other.get_timestamp(); + } + +private: + std::map m_data; + + ClockType::time_point timestamp; + void update_timestamp() { + timestamp = ClockType::now(); + } +}; + // An object STL, or a modifier volume, over which a different set of parameters shall be applied. // ModelVolume instances are owned by a ModelObject. class ModelVolume final : public ObjectBase @@ -421,8 +450,11 @@ public: // overriding the global Slic3r settings and the ModelObject settings. ModelConfig config; + // List of mesh facets to be supported/unsupported. + FacetsAnnotation m_supported_facets; + // A parent object owning this modifier volume. - ModelObject* get_object() const { return this->object; }; + ModelObject* get_object() const { return this->object; } ModelVolumeType type() const { return m_type; } void set_type(const ModelVolumeType t) { m_type = t; } bool is_model_part() const { return m_type == ModelVolumeType::MODEL_PART; } @@ -548,7 +580,9 @@ private: // Copying an existing volume, therefore this volume will get a copy of the ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other) : ObjectBase(other), - name(other.name), source(other.source), 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) + name(other.name), source(other.source), 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), + m_supported_facets(other.m_supported_facets) { assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); assert(this->id() == other.id() && this->config.id() == other.config.id()); @@ -565,6 +599,8 @@ private: if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); assert(this->config.id().valid()); assert(this->config.id() != other.config.id()); assert(this->id() != this->config.id()); + + m_supported_facets.clear(); } ModelVolume& operator=(ModelVolume &rhs) = delete; @@ -820,8 +856,7 @@ public: std::string propose_export_file_name_and_path(const std::string &new_extension) const; private: - - explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }; + explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); } void assign_new_unique_ids_recursive(); void update_links_bottom_up_recursive(); @@ -848,6 +883,10 @@ extern bool model_object_list_extended(const Model &model_old, const Model &mode // than the old ModelObject. extern bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolumeType type); +// Test whether the now ModelObject has newer custom supports data than the old one. +// The function assumes that volumes list is synchronized. +extern bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new); + // If the model has multi-part objects, then it is currently not supported by the SLA mode. // Either the model cannot be loaded, or a SLA printer has to be activated. extern bool model_has_multi_part_objects(const Model &model); diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 7a6b31395..e511a6316 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -114,6 +114,7 @@ public: Point& operator+=(const Point& rhs) { (*this)(0) += rhs(0); (*this)(1) += rhs(1); return *this; } Point& operator-=(const Point& rhs) { (*this)(0) -= rhs(0); (*this)(1) -= rhs(1); return *this; } Point& operator*=(const double &rhs) { (*this)(0) = coord_t((*this)(0) * rhs); (*this)(1) = coord_t((*this)(1) * rhs); return *this; } + Point operator*(const double &rhs) { return Point((*this)(0) * rhs, (*this)(1) * rhs); } void rotate(double angle); void rotate(double angle, const Point ¢er); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a35311fdf..865f5c3b8 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -404,6 +404,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, // Copy the ModelVolume data. mv_dst.name = mv_src.name; static_cast(mv_dst.config) = static_cast(mv_src.config); + mv_dst.m_supported_facets = mv_src.m_supported_facets; //FIXME what to do with the materials? // mv_dst.m_material_id = mv_src.m_material_id; ++ i_src; @@ -854,7 +855,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } // Copy content of the ModelObject including its ID, do not change the parent. model_object.assign_copy(model_object_new); - } else if (support_blockers_differ || support_enforcers_differ) { + } else if (support_blockers_differ || support_enforcers_differ || model_custom_supports_data_changed(model_object, model_object_new)) { // First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list. this->call_cancel_callback(); update_apply_status(false); @@ -862,8 +863,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); for (auto it = range.first; it != range.second; ++ it) update_apply_status(it->print_object->invalidate_step(posSupportMaterial)); - // Copy just the support volumes. - model_volume_list_update_supports(model_object, model_object_new); + if (support_enforcers_differ || support_blockers_differ) { + // Copy just the support volumes. + model_volume_list_update_supports(model_object, model_object_new); + } } if (! model_parts_differ && ! modifiers_differ) { // Synchronize Object's config. @@ -881,7 +884,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ } } } - // Synchronize (just copy) the remaining data of ModelVolumes (name, config). + // Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data). //FIXME What to do with m_material_id? model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART); model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index db4d14e54..e57c4360b 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -195,6 +195,11 @@ public: std::vector slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); } std::vector slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); } + // Helpers to project custom supports on slices + void project_and_append_custom_supports(FacetSupportType type, std::vector& expolys) const; + void project_and_append_custom_enforcers(std::vector& enforcers) const { project_and_append_custom_supports(FacetSupportType::ENFORCER, enforcers); } + void project_and_append_custom_blockers(std::vector& blockers) const { project_and_append_custom_supports(FacetSupportType::BLOCKER, blockers); } + private: // to be called from Print only. friend class Print; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5573f4ac3..34b17c7bf 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2645,4 +2645,168 @@ void PrintObject::_generate_support_material() support_material.generate(*this); } + +void PrintObject::project_and_append_custom_supports( + FacetSupportType type, std::vector& expolys) const +{ + for (const ModelVolume* mv : this->model_object()->volumes) { + const std::vector custom_facets = mv->m_supported_facets.get_facets(type); + if (custom_facets.empty()) + continue; + + const TriangleMesh& mesh = mv->mesh(); + const Transform3f& tr1 = mv->get_matrix().cast(); + const Transform3f& tr2 = this->trafo().cast(); + const Transform3f tr = tr2 * tr1; + + + // The projection will be at most a pentagon. Let's minimize heap + // reallocations by saving in in the following struct. + // Points are used so that scaling can be done in parallel + // and they can be moved from to create an ExPolygon later. + struct LightPolygon { + LightPolygon() { pts.reserve(5); } + Points pts; + + void add(const Vec2f& pt) { + pts.emplace_back(scale_(pt.x()), scale_(pt.y())); + assert(pts.size() <= 5); + } + }; + + // Structure to collect projected polygons. One element for each triangle. + // Saves vector of polygons and layer_id of the first one. + struct TriangleProjections { + size_t first_layer_id; + std::vector polygons; + }; + + // Vector to collect resulting projections from each triangle. + std::vector projections_of_triangles(custom_facets.size()); + + // Iterate over all triangles. + tbb::parallel_for( + tbb::blocked_range(0, custom_facets.size()), + [&](const tbb::blocked_range& range) { + for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + + std::array facet; + + // Transform the triangle into worlds coords. + for (int i=0; i<3; ++i) + facet[i] = tr * mesh.its.vertices[mesh.its.indices[custom_facets[idx]](i)]; + + // Ignore triangles with upward-pointing normal. + if ((facet[1]-facet[0]).cross(facet[2]-facet[0]).z() > 0.) + continue; + + // Sort the three vertices according to z-coordinate. + std::sort(facet.begin(), facet.end(), + [](const Vec3f& pt1, const Vec3f&pt2) { + return pt1.z() < pt2.z(); + }); + + std::array trianglef; + for (int i=0; i<3; ++i) { + trianglef[i] = Vec2f(facet[i].x(), facet[i].y()); + trianglef[i] += Vec2f(unscale(this->center_offset().x()), + unscale(this->center_offset().y())); + } + + // Find lowest slice not below the triangle. + auto it = std::lower_bound(layers().begin(), layers().end(), facet[0].z()+EPSILON, + [](const Layer* l1, float z) { + return l1->slice_z < z; + }); + + // Count how many projections will be generated for this triangle + // and allocate respective amount in projections_of_triangles. + projections_of_triangles[idx].first_layer_id = it-layers().begin(); + size_t last_layer_id = projections_of_triangles[idx].first_layer_id; + // The cast in the condition below is important. The comparison must + // be an exact opposite of the one lower in the code where + // the polygons are appended. And that one is on floats. + while (last_layer_id + 1 < layers().size() + && float(layers()[last_layer_id]->slice_z) <= facet[2].z()) + ++last_layer_id; + projections_of_triangles[idx].polygons.resize( + last_layer_id - projections_of_triangles[idx].first_layer_id + 1); + + // Calculate how to move points on triangle sides per unit z increment. + Vec2f ta(trianglef[1] - trianglef[0]); + Vec2f tb(trianglef[2] - trianglef[0]); + ta *= 1./(facet[1].z() - facet[0].z()); + tb *= 1./(facet[2].z() - facet[0].z()); + + // Projection on current slice will be build directly in place. + LightPolygon* proj = &projections_of_triangles[idx].polygons[0]; + proj->add(trianglef[0]); + + bool passed_first = false; + bool stop = false; + + // Project a sub-polygon on all slices intersecting the triangle. + while (it != layers().end()) { + const float z = (*it)->slice_z; + + // Projections of triangle sides intersections with slices. + // a moves along one side, b tracks the other. + Vec2f a; + Vec2f b; + + // If the middle vertex was already passed, append the vertex + // and use ta for tracking the remaining side. + if (z > facet[1].z() && ! passed_first) { + proj->add(trianglef[1]); + ta = trianglef[2]-trianglef[1]; + ta *= 1./(facet[2].z() - facet[1].z()); + passed_first = true; + } + + // This slice is above the triangle already. + if (z > facet[2].z() || it+1 == layers().end()) { + proj->add(trianglef[2]); + stop = true; + } + else { + // Move a, b along the side it currently tracks to get + // projected intersection with current slice. + a = passed_first ? (trianglef[1]+ta*(z-facet[1].z())) + : (trianglef[0]+ta*(z-facet[0].z())); + b = trianglef[0]+tb*(z-facet[0].z()); + proj->add(a); + proj->add(b); + } + + if (stop) + break; + + // Advance to the next layer. + ++it; + ++proj; + assert(proj <= &projections_of_triangles[idx].polygons.back() ); + + // a, b are first two points of the polygon for the next layer. + proj->add(b); + proj->add(a); + } + } + }); // end of parallel_for + + // Make sure that the output vector can be used. + expolys.resize(layers().size()); + + // Now append the collected polygons to respective layers. + for (auto& trg : projections_of_triangles) { + int layer_id = trg.first_layer_id; + + for (const LightPolygon& poly : trg.polygons) { + expolys[layer_id].emplace_back(std::move(poly.pts)); + ++layer_id; + } + } + + } // loop over ModelVolumes +} + } // namespace Slic3r diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 1d98bb0e6..6cde050cf 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -971,6 +971,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ std::vector enforcers = object.slice_support_enforcers(); std::vector blockers = object.slice_support_blockers(); + // Append custom supports. + object.project_and_append_custom_enforcers(enforcers); + object.project_and_append_custom_blockers(blockers); + // Output layers, sorted by top Z. MyLayersPtr contact_out; @@ -1097,10 +1101,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ if (! enforcers.empty()) { // Apply the "support enforcers". //FIXME add the "enforcers" to the sparse support regions only. - const ExPolygons &enforcer = enforcers[layer_id - 1]; + const ExPolygons &enforcer = enforcers[layer_id]; if (! enforcer.empty()) { // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes. - Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(enforcer)), + Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(std::move(enforcer))), offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (! new_contacts.empty()) { if (diff_polygons.empty()) @@ -1111,19 +1115,26 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } } } - // Apply the "support blockers". - if (! diff_polygons.empty() && ! blockers.empty() && ! blockers[layer_id].empty()) { - // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes. - diff_polygons = diff(diff_polygons, to_polygons(blockers[layer_id])); - } + if (diff_polygons.empty()) continue; + // Apply the "support blockers". + if (! blockers.empty() && ! blockers[layer_id].empty()) { + // Expand the blocker a bit. Custom blockers produce strips + // spanning just the projection between the two slices. + // Subtracting them as they are may leave unwanted narrow + // residues of diff_polygons that would then be supported. + diff_polygons = diff(diff_polygons, + offset(union_(to_polygons(std::move(blockers[layer_id]))), + 1000.*SCALED_EPSILON)); + } + #ifdef SLIC3R_DEBUG { ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", iRun, layer_id, - std::find_if(layer.regions.begin(), layer.regions.end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions.begin()), + std::find_if(layer.regions.begin(), layer.regions.end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions.begin()), get_extents(diff_polygons)); Slic3r::ExPolygons expolys = union_ex(diff_polygons, false); svg.draw(expolys); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 255afc631..3f821ddce 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,6 +59,7 @@ // Enable G-Code viewer #define ENABLE_GCODE_VIEWER (1 && ENABLE_2_3_0_ALPHA1) #define ENABLE_GCODE_VIEWER_DEBUG_OUTPUT (0 && ENABLE_GCODE_VIEWER) +#define ENABLE_GCODE_VIEWER_GL_OPTIMIZATION (1 && ENABLE_GCODE_VIEWER) #define ENABLE_GCODE_VIEWER_STATISTICS (0 && ENABLE_GCODE_VIEWER) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 742941b84..2cc9de38c 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -259,7 +259,8 @@ Point Bed3D::point_projection(const Point& point) const return m_polygon.point_projection(point); } -void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes) const +void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, + bool show_axes, bool show_texture) const { m_scale_factor = scale_factor; @@ -270,9 +271,9 @@ void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool sho switch (m_type) { - case System: { render_system(canvas, bottom); break; } + case System: { render_system(canvas, bottom, show_texture); break; } default: - case Custom: { render_custom(canvas, bottom); break; } + case Custom: { render_custom(canvas, bottom, show_texture); break; } } glsafe(::glDisable(GL_DEPTH_TEST)); @@ -384,12 +385,13 @@ void Bed3D::render_axes() const m_axes.render(); } -void Bed3D::render_system(GLCanvas3D& canvas, bool bottom) const +void Bed3D::render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) const { if (!bottom) render_model(); - render_texture(bottom, canvas); + if (show_texture) + render_texture(bottom, canvas); } void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const @@ -564,7 +566,7 @@ void Bed3D::render_model() const } } -void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const +void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) const { if (m_texture_filename.empty() && m_model_filename.empty()) { @@ -575,7 +577,8 @@ void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const if (!bottom) render_model(); - render_texture(bottom, canvas); + if (show_texture) + render_texture(bottom, canvas); } void Bed3D::render_default(bool bottom) const diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 3a5f95978..abdfca1fe 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -103,11 +103,15 @@ public: // Return true if the bed shape changed, so the calee will update the UI. bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model); - const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; } + const BoundingBoxf3& get_bounding_box(bool extended) const { + return extended ? m_extended_bounding_box : m_bounding_box; + } + bool contains(const Point& point) const; Point point_projection(const Point& point) const; - void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes) const; + void render(GLCanvas3D& canvas, bool bottom, float scale_factor, + bool show_axes, bool show_texture) const; private: void calc_bounding_boxes() const; @@ -115,10 +119,10 @@ private: void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); std::tuple detect_type(const Pointfs& shape) const; void render_axes() const; - void render_system(GLCanvas3D& canvas, bool bottom) const; + void render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) const; void render_texture(bool bottom, GLCanvas3D& canvas) const; void render_model() const; - void render_custom(GLCanvas3D& canvas, bool bottom) const; + void render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) const; void render_default(bool bottom) const; void reset(); }; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 71053819d..64c027a7b 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -92,9 +92,8 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con void GCodeViewer::IBuffer::add_path(const GCodeProcessor::MoveVertex& move) { - unsigned int id = static_cast(data.size()); - double z = static_cast(move.position[2]); - paths.push_back({ move.type, move.extrusion_role, id, id, z, z, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); + Path::Endpoint endpoint = { static_cast(data.size()), static_cast(move.position[2]) }; + paths.push_back({ move.type, move.extrusion_role, endpoint, endpoint, move.delta_extruder, move.height, move.width, move.feedrate, move.fan_speed, move.volumetric_rate(), move.extruder_id, move.cp_color_id }); } std::array GCodeViewer::Extrusions::Range::get_color_at(float value) const @@ -215,6 +214,11 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: } } +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + // update buffers' render paths + refresh_render_paths(); +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + #if ENABLE_GCODE_VIEWER_STATISTICS m_statistics.refresh_time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -346,6 +350,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { #if ENABLE_GCODE_VIEWER_STATISTICS auto start_time = std::chrono::high_resolution_clock::now(); + m_statistics.results_size = SLIC3R_STDVEC_MEMSIZE(gcode_result.moves, GCodeProcessor::MoveVertex); #endif // ENABLE_GCODE_VIEWER_STATISTICS // vertex data @@ -363,7 +368,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } #if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.vertices_size = vertices_data.size() * sizeof(float); + m_statistics.vertices_size = SLIC3R_STDVEC_MEMSIZE(vertices_data, float); + m_statistics.vertices_gpu_size = vertices_data.size() * sizeof(float); #endif // ENABLE_GCODE_VIEWER_STATISTICS // vertex data -> send to gpu @@ -408,13 +414,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) buffer.data.push_back(static_cast(i - 1)); } - buffer.paths.back().last = static_cast(buffer.data.size()); + buffer.paths.back().last.id = static_cast(buffer.data.size()); buffer.data.push_back(static_cast(i)); break; } default: { - continue; + break; } } } @@ -424,7 +430,8 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { buffer.data_size = buffer.data.size(); #if ENABLE_GCODE_VIEWER_STATISTICS - m_statistics.indices_size += buffer.data_size * sizeof(unsigned int); + m_statistics.indices_size += SLIC3R_STDVEC_MEMSIZE(buffer.data, unsigned int); + m_statistics.indices_gpu_size += buffer.data_size * sizeof(unsigned int); #endif // ENABLE_GCODE_VIEWER_STATISTICS if (buffer.data_size > 0) { @@ -526,7 +533,8 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) } } -void GCodeViewer::render_toolpaths() const +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION +void GCodeViewer::refresh_render_paths() const { auto extrusion_color = [this](const Path& path) { std::array color; @@ -551,6 +559,65 @@ void GCodeViewer::render_toolpaths() const Travel_Colors[0] /* Move */); }; + + for (IBuffer& buffer : m_buffers) { + buffer.render_paths = std::vector(); + for (const Path& path : buffer.paths) + { + if (!is_in_z_range(path)) + continue; + + if (path.type == GCodeProcessor::EMoveType::Extrude && !is_visible(path)) + continue; + + std::array color = { 0.0f, 0.0f, 0.0f }; + switch (path.type) + { + case GCodeProcessor::EMoveType::Extrude: { color = extrusion_color(path); break; } + case GCodeProcessor::EMoveType::Travel: { color = (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path); break; } + } + + auto it = std::find_if(buffer.render_paths.begin(), buffer.render_paths.end(), [color](const RenderPath& path) { return path.color == color; }); + if (it == buffer.render_paths.end()) + { + it = buffer.render_paths.insert(buffer.render_paths.end(), RenderPath()); + it->color = color; + } + + it->sizes.push_back(path.last.id - path.first.id + 1); + it->offsets.push_back(static_cast(path.first.id * sizeof(unsigned int))); + } + } +} +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + +void GCodeViewer::render_toolpaths() const +{ +#if !ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + auto extrusion_color = [this](const Path& path) { + std::array color; + switch (m_view_type) + { + case EViewType::FeatureType: { color = Extrusion_Role_Colors[static_cast(path.role)]; break; } + case EViewType::Height: { color = m_extrusions.ranges.height.get_color_at(path.height); break; } + case EViewType::Width: { color = m_extrusions.ranges.width.get_color_at(path.width); break; } + case EViewType::Feedrate: { color = m_extrusions.ranges.feedrate.get_color_at(path.feedrate); break; } + case EViewType::FanSpeed: { color = m_extrusions.ranges.fan_speed.get_color_at(path.fan_speed); break; } + case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } + case EViewType::Tool: { color = m_tool_colors[path.extruder_id]; break; } + case EViewType::ColorPrint: { color = m_tool_colors[path.cp_color_id]; break; } + default: { color = { 1.0f, 1.0f, 1.0f }; break; } + } + return color; + }; + + auto travel_color = [this](const Path& path) { + return (path.delta_extruder < 0.0f) ? Travel_Colors[2] /* Retract */ : + ((path.delta_extruder > 0.0f) ? Travel_Colors[1] /* Extrude */ : + Travel_Colors[0] /* Move */); + }; +#endif // !ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + auto set_color = [](GLint current_program_id, const std::array& color) { if (current_program_id > 0) { GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; @@ -601,7 +668,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -619,7 +686,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -637,7 +704,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -655,7 +722,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -673,7 +740,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -691,7 +758,7 @@ void GCodeViewer::render_toolpaths() const continue; glsafe(::glEnable(GL_PROGRAM_POINT_SIZE)); - glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_POINTS, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); glsafe(::glDisable(GL_PROGRAM_POINT_SIZE)); #if ENABLE_GCODE_VIEWER_STATISTICS @@ -702,32 +769,56 @@ void GCodeViewer::render_toolpaths() const } case GCodeProcessor::EMoveType::Extrude: { +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + for (const RenderPath& path : buffer.render_paths) + { + set_color(current_program_id, path.color); + glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_line_strip_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + + } +#else for (const Path& path : buffer.paths) { if (!is_visible(path) || !is_in_z_range(path)) continue; set_color(current_program_id, extrusion_color(path)); - glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_line_strip_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } case GCodeProcessor::EMoveType::Travel: { +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + for (const RenderPath& path : buffer.render_paths) + { + set_color(current_program_id, path.color); + glsafe(::glMultiDrawElements(GL_LINE_STRIP, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_INT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size())); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_multi_line_strip_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + + } +#else for (const Path& path : buffer.paths) { if (!is_in_z_range(path)) continue; set_color(current_program_id, (m_view_type == EViewType::Feedrate || m_view_type == EViewType::Tool || m_view_type == EViewType::ColorPrint) ? extrusion_color(path) : travel_color(path)); - glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last - path.first + 1), GL_UNSIGNED_INT, (const void*)(path.first * sizeof(GLuint)))); + glsafe(::glDrawElements(GL_LINE_STRIP, GLsizei(path.last.id - path.first.id + 1), GL_UNSIGNED_INT, (const void*)(path.first.id * sizeof(GLuint)))); #if ENABLE_GCODE_VIEWER_STATISTICS ++m_statistics.gl_line_strip_calls_count; #endif // ENABLE_GCODE_VIEWER_STATISTICS } +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION break; } } @@ -840,6 +931,10 @@ void GCodeViewer::render_legend() const if (role < erCount) { m_extrusions.role_visibility_flags = is_visible(role) ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + // update buffers' render paths + refresh_render_paths(); +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION wxGetApp().plater()->get_current_canvas3D()->set_as_dirty(); wxGetApp().plater()->update_preview_bottom_toolbar(); } @@ -986,7 +1081,7 @@ void GCodeViewer::render_statistics() const static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); static const float offset = 250.0f; - if (!m_legend_enabled || m_roles.empty()) + if (m_roles.empty()) return; ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -1020,20 +1115,52 @@ void GCodeViewer::render_statistics() const ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.gl_line_strip_calls_count)); +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Multi GL_POINTS calls:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.gl_multi_points_calls_count)); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Multi GL_LINE_STRIP calls:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.gl_multi_line_strip_calls_count)); +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("Vertices:")); + imgui.text(std::string("Results:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.results_size) + " bytes"); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Vertices CPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.vertices_size) + " bytes"); ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); - imgui.text(std::string("Indices:")); + imgui.text(std::string("Vertices GPU:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.vertices_gpu_size) + " bytes"); + + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Indices CPU:")); ImGui::PopStyleColor(); ImGui::SameLine(offset); imgui.text(std::to_string(m_statistics.indices_size) + " bytes"); + ImGui::PushStyleColor(ImGuiCol_Text, ORANGE); + imgui.text(std::string("Indices GPU:")); + ImGui::PopStyleColor(); + ImGui::SameLine(offset); + imgui.text(std::to_string(m_statistics.indices_gpu_size) + " bytes"); + imgui.end(); } #endif // ENABLE_GCODE_VIEWER_STATISTICS diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index a8816f7a1..ec3bfc9a6 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -35,12 +35,16 @@ class GCodeViewer // Used to identify different toolpath sub-types inside a IBuffer struct Path { + struct Endpoint + { + unsigned int id{ 0u }; + double z{ 0.0 }; + }; + GCodeProcessor::EMoveType type{ GCodeProcessor::EMoveType::Noop }; ExtrusionRole role{ erNone }; - unsigned int first{ 0 }; - unsigned int last{ 0 }; - double first_z{ 0.0f }; - double last_z{ 0.0f }; + Endpoint first; + Endpoint last; float delta_extruder{ 0.0f }; float height{ 0.0f }; float width{ 0.0f }; @@ -57,6 +61,16 @@ class GCodeViewer } }; +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + // Used to batch the indices needed to render paths + struct RenderPath + { + std::array color; + std::vector sizes; + std::vector offsets; // use size_t because we need the pointer's size (used in the call glMultiDrawElements()) + }; +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + // buffer containing indices data and shader for a specific toolpath type struct IBuffer { @@ -65,6 +79,9 @@ class GCodeViewer std::vector data; size_t data_size{ 0 }; std::vector paths; +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + std::vector render_paths; +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION bool visible{ false }; void reset(); @@ -139,8 +156,15 @@ class GCodeViewer long long refresh_time{ 0 }; long long gl_points_calls_count{ 0 }; long long gl_line_strip_calls_count{ 0 }; +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + long long gl_multi_points_calls_count{ 0 }; + long long gl_multi_line_strip_calls_count{ 0 }; +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + long long results_size{ 0 }; long long vertices_size{ 0 }; + long long vertices_gpu_size{ 0 }; long long indices_size{ 0 }; + long long indices_gpu_size{ 0 }; void reset_all() { reset_times(); @@ -156,11 +180,18 @@ class GCodeViewer void reset_opengl() { gl_points_calls_count = 0; gl_line_strip_calls_count = 0; +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + gl_multi_points_calls_count = 0; + gl_multi_line_strip_calls_count = 0; +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION } void reset_sizes() { + results_size = 0; vertices_size = 0; + vertices_gpu_size = 0; indices_size = 0; + indices_gpu_size = 0; } }; #endif // ENABLE_GCODE_VIEWER_STATISTICS @@ -240,6 +271,9 @@ private: bool init_shaders(); void load_toolpaths(const GCodeProcessor::Result& gcode_result); void load_shells(const Print& print, bool initialized); +#if ENABLE_GCODE_VIEWER_GL_OPTIMIZATION + void refresh_render_paths() const; +#endif // ENABLE_GCODE_VIEWER_GL_OPTIMIZATION void render_toolpaths() const; void render_shells() const; void render_legend() const; @@ -255,7 +289,7 @@ private: return z > m_layers_z_range[0] - EPSILON && z < m_layers_z_range[1] + EPSILON; }; - return in_z_range(path.first_z) || in_z_range(path.last_z); + return in_z_range(path.first.z) || in_z_range(path.last.z); } }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 080ba2045..f8c0889fd 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5459,14 +5459,19 @@ void GLCanvas3D::_render_background() const glsafe(::glPopMatrix()); } -void GLCanvas3D::_render_bed(float theta, bool show_axes) const +void GLCanvas3D::_render_bed(bool bottom, bool show_axes) const { float scale_factor = 1.0; #if ENABLE_RETINA_GL scale_factor = m_retina_helper->get_scale_factor(); #endif // ENABLE_RETINA_GL + + bool show_texture = ! bottom || + (m_gizmos.get_current_type() != GLGizmosManager::FdmSupports + && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports); + #if ENABLE_NON_STATIC_CANVAS_MANAGER - wxGetApp().plater()->get_bed().render(const_cast(*this), theta, scale_factor, show_axes); + wxGetApp().plater()->get_bed().render(const_cast(*this), bottom, scale_factor, show_axes, show_texture); #else m_bed.render(const_cast(*this), theta, scale_factor, show_axes); #endif // ENABLE_NON_STATIC_CANVAS_MANAGER diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index c640108fd..a5066d923 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -795,7 +795,7 @@ private: void _picking_pass() const; void _rectangular_selection_picking_pass() const; void _render_background() const; - void _render_bed(float theta, bool show_axes) const; + void _render_bed(bool bottom, bool show_axes) const; void _render_objects() const; #if ENABLE_GCODE_VIEWER void _render_gcode() const; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 28fd45b6c..72f40bb6b 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -314,7 +314,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view m_label_show = new wxStaticText(this, wxID_ANY, _(L("Show"))); m_combochecklist_features = new wxComboCtrl(); - m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), wxCB_READONLY); + m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); std::string feature_items = GUI::into_u8( #if ENABLE_GCODE_VIEWER _L("Unknown") + "|1|" + @@ -337,7 +337,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view #if ENABLE_GCODE_VIEWER m_combochecklist_options = new wxComboCtrl(); - m_combochecklist_options->Create(this, wxID_ANY, _(L("Others")), wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), wxCB_READONLY); + m_combochecklist_options->Create(this, wxID_ANY, _(L("Options")), wxDefaultPosition, wxDefaultSize, wxCB_READONLY); std::string options_items = GUI::into_u8( _(L("Travel")) + "|0|" + _(L("Retractions")) + "|0|" + @@ -349,7 +349,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view _(L("Shells")) + "|0|" + _(L("Legend")) + "|1" ); - Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_(L("Others"))), options_items); + Slic3r::GUI::create_combochecklist(m_combochecklist_options, GUI::into_u8(_(L("Options"))), options_items); #else m_checkbox_travel = new wxCheckBox(this, wxID_ANY, _(L("Travel"))); m_checkbox_retractions = new wxCheckBox(this, wxID_ANY, _(L("Retractions"))); @@ -605,15 +605,11 @@ void Preview::show_hide_ui_elements(const std::string& what) bool enable = (what == "full"); m_label_show->Enable(enable); m_combochecklist_features->Enable(enable); -#if ENABLE_GCODE_VIEWER - m_combochecklist_options->Enable(enable); -#else m_checkbox_travel->Enable(enable); m_checkbox_retractions->Enable(enable); m_checkbox_unretractions->Enable(enable); m_checkbox_shells->Enable(enable); m_checkbox_legend->Enable(enable); -#endif // ENABLE_GCODE_VIEWER enable = (what != "none"); m_label_view_type->Enable(enable); @@ -622,15 +618,11 @@ void Preview::show_hide_ui_elements(const std::string& what) bool visible = (what != "none"); m_label_show->Show(visible); m_combochecklist_features->Show(visible); -#if ENABLE_GCODE_VIEWER - m_combochecklist_options->Show(visible); -#else m_checkbox_travel->Show(visible); m_checkbox_retractions->Show(visible); m_checkbox_unretractions->Show(visible); m_checkbox_shells->Show(visible); m_checkbox_legend->Show(visible); -#endif // ENABLE_GCODE_VIEWER m_label_view_type->Show(visible); m_choice_view_type->Show(visible); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index def8cb0f8..8094d10ad 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -8,6 +8,7 @@ #include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/PresetBundle.hpp" #include "slic3r/GUI/Camera.hpp" +#include "libslic3r/Model.hpp" @@ -45,6 +46,7 @@ bool GLGizmoFdmSupports::on_init() m_desc["block"] = _L("Block supports"); m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": "; m_desc["remove"] = _L("Remove selection"); + m_desc["remove_all"] = _L("Remove all"); return true; } @@ -191,7 +193,16 @@ void GLGizmoFdmSupports::update_mesh() // This mesh does not account for the possible Z up SLA offset. const TriangleMesh* mesh = &mv->mesh(); - m_selected_facets[volume_id].assign(mesh->its.indices.size(), SelType::NONE); + m_selected_facets[volume_id].assign(mesh->its.indices.size(), FacetSupportType::NONE); + + // Load current state from ModelVolume. + for (FacetSupportType type : {FacetSupportType::ENFORCER, FacetSupportType::BLOCKER}) { + const std::vector& list = mv->m_supported_facets.get_facets(type); + for (int i : list) + m_selected_facets[volume_id][i] = type; + } + update_vertex_buffers(mv, volume_id, true, true); + m_neighbors[volume_id].resize(3 * mesh->its.indices.size()); // Prepare vector of vertex_index - facet_index pairs to quickly find adjacent facets @@ -247,16 +258,16 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous || action == SLAGizmoEventType::RightDown || (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) { - SelType new_state = SelType::NONE; + FacetSupportType new_state = FacetSupportType::NONE; if (! shift_down) { if (action == SLAGizmoEventType::Dragging) new_state = m_button_down == Button::Left - ? SelType::ENFORCER - : SelType::BLOCKER; + ? FacetSupportType::ENFORCER + : FacetSupportType::BLOCKER; else new_state = action == SLAGizmoEventType::LeftDown - ? SelType::ENFORCER - : SelType::BLOCKER; + ? FacetSupportType::ENFORCER + : FacetSupportType::BLOCKER; } const Camera& camera = wxGetApp().plater()->get_camera(); @@ -383,9 +394,9 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // Now just select all facets that passed. for (size_t next_facet : facets_to_select) { - SelType& facet = m_selected_facets[mesh_id][next_facet]; + FacetSupportType& facet = m_selected_facets[mesh_id][next_facet]; - if (facet != new_state && facet != SelType::NONE) { + if (facet != new_state && facet != FacetSupportType::NONE) { // this triangle is currently in the other VBA. // Both VBAs need to be refreshed. update_both = true; @@ -395,8 +406,8 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } update_vertex_buffers(mv, mesh_id, - new_state == SelType::ENFORCER || update_both, - new_state == SelType::BLOCKER || update_both + new_state == FacetSupportType::ENFORCER || update_both, + new_state == FacetSupportType::BLOCKER || update_both ); } @@ -420,6 +431,18 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp) && m_button_down != Button::None) { m_button_down = Button::None; + + // Synchronize gizmo with ModelVolume data. + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + ++idx; + if (! mv->is_model_part()) + continue; + for (int i=0; im_supported_facets.set_facet(i, m_selected_facets[idx][i]); + } + return true; } @@ -434,16 +457,16 @@ void GLGizmoFdmSupports::update_vertex_buffers(const ModelVolume* mv, { const TriangleMesh* mesh = &mv->mesh(); - for (SelType type : {SelType::ENFORCER, SelType::BLOCKER}) { - if ((type == SelType::ENFORCER && ! update_enforcers) - || (type == SelType::BLOCKER && ! update_blockers)) + for (FacetSupportType type : {FacetSupportType::ENFORCER, FacetSupportType::BLOCKER}) { + if ((type == FacetSupportType::ENFORCER && ! update_enforcers) + || (type == FacetSupportType::BLOCKER && ! update_blockers)) continue; - GLIndexedVertexArray& iva = m_ivas[mesh_id][type==SelType::ENFORCER ? 0 : 1]; + GLIndexedVertexArray& iva = m_ivas[mesh_id][type==FacetSupportType::ENFORCER ? 0 : 1]; iva.release_geometry(); size_t triangle_cnt=0; for (size_t facet_idx=0; facet_idxcalc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f); + const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f); const float minimal_slider_width = m_imgui->scaled(4.f); float caption_max = 0.f; @@ -484,6 +508,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left); window_width = std::max(window_width, total_text_max); + window_width = std::max(window_width, button_width); auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) { static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f); @@ -499,6 +524,20 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l m_imgui->text(""); + if (m_imgui->button(m_desc.at("remove_all"))) { + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + ++idx; + if (mv->is_model_part()) { + m_selected_facets[idx].assign(m_selected_facets[idx].size(), FacetSupportType::NONE); + mv->m_supported_facets.clear(); + update_vertex_buffers(mv, idx, true, true); + m_parent.set_as_dirty(); + } + } + } + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; m_imgui->text(m_desc.at("cursor_size")); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index d80721fbf..994218f69 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -9,6 +9,9 @@ namespace Slic3r { + +enum class FacetSupportType : int8_t; + namespace GUI { enum class SLAGizmoEventType : unsigned char; @@ -26,15 +29,9 @@ private: static constexpr float CursorRadiusMax = 8.f; static constexpr float CursorRadiusStep = 0.2f; - enum class SelType : int8_t { - NONE, - ENFORCER, - BLOCKER - }; - // For each model-part volume, store a list of statuses of // individual facets (one of the enum values above). - std::vector> m_selected_facets; + std::vector> m_selected_facets; // Store two vertex buffer arrays (for enforcers/blockers) // for each model-part volume.