diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index c774083f7..0f0836c86 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -582,10 +582,7 @@ namespace Slic3r { IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.first); if (obj_layer_heights_profile != m_layer_heights_profiles.end()) - { object.second->layer_height_profile = obj_layer_heights_profile->second; - object.second->layer_height_profile_valid = true; - } IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.first); if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) @@ -1931,7 +1928,7 @@ namespace Slic3r { for (const ModelObject* object : model.objects) { ++count; - std::vector layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector(); + const std::vector &layer_height_profile = object->layer_height_profile; if ((layer_height_profile.size() >= 4) && ((layer_height_profile.size() % 2) == 0)) { sprintf(buffer, "object_id=%d|", count); diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 6c6daaac0..cc1bf92c0 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -578,7 +578,6 @@ void AMFParserContext::endElement(const char * /* name */) break; p = end + 1; } - m_object->layer_height_profile_valid = true; } else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "sla_support_points") == 0) { // Parse object's layer height profile, a semicolon separated list of floats. @@ -893,7 +892,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << " " << object->config.serialize(key) << "\n"; if (!object->name.empty()) stream << " " << xml_escape(object->name) << "\n"; - std::vector layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector(); + const std::vector &layer_height_profile = object->layer_height_profile; if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) { // Store the layer height profile as a single semicolon separated list. stream << " "; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 157723ca6..29deafa80 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -570,7 +570,6 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs) this->sla_support_points = rhs.sla_support_points; this->layer_height_ranges = rhs.layer_height_ranges; this->layer_height_profile = rhs.layer_height_profile; - this->layer_height_profile_valid = rhs.layer_height_profile_valid; this->origin_translation = rhs.origin_translation; m_bounding_box = rhs.m_bounding_box; m_bounding_box_valid = rhs.m_bounding_box_valid; @@ -602,7 +601,6 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs) this->sla_support_points = std::move(rhs.sla_support_points); this->layer_height_ranges = std::move(rhs.layer_height_ranges); this->layer_height_profile = std::move(rhs.layer_height_profile); - this->layer_height_profile_valid = std::move(rhs.layer_height_profile_valid); this->origin_translation = std::move(rhs.origin_translation); m_bounding_box = std::move(rhs.m_bounding_box); m_bounding_box_valid = std::move(rhs.m_bounding_box_valid); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 70fe5aafd..5bed3e244 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -170,12 +170,8 @@ public: // Variation of a layer thickness for spans of Z coordinates. t_layer_height_ranges layer_height_ranges; // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. - // The pairs of are packed into a 1D array to simplify handling by the Perl XS. + // The pairs of are packed into a 1D array. std::vector layer_height_profile; - // layer_height_profile is initialized when the layer editing mode is entered. - // Only if the user really modified the layer height, layer_height_profile_valid is set - // and used subsequently by the PrintObject. - bool layer_height_profile_valid; // This vector holds position of selected support points for SLA. The data are // saved in mesh coordinates to allow using them for several instances. @@ -267,7 +263,7 @@ protected: void set_model(Model *model) { m_model = model; } private: - ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {} + ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {} ~ModelObject(); /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index 8ed595802..1f517375c 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -15,6 +15,7 @@ namespace arr { using namespace libnest2d; +// Only for debugging. Prints the model object vertices on stdout. std::string toString(const Model& model, bool holes = true) { std::stringstream ss; @@ -78,6 +79,7 @@ std::string toString(const Model& model, bool holes = true) { return ss.str(); } +// Debugging: Save model to svg file. void toSVG(SVG& svg, const Model& model) { for(auto objptr : model.objects) { if(!objptr) continue; @@ -121,6 +123,10 @@ Box boundingBox(const Box& pilebb, const Box& ibb ) { return Box(minc, maxc); } +// This is "the" object function which is evaluated many times for each vertex +// (decimated with the accuracy parameter) of each object. Therefore it is +// upmost crucial for this function to be as efficient as it possibly can be but +// at the same time, it has to provide reasonable results. std::tuple objfunc(const PointImpl& bincenter, const shapelike::Shapes& merged_pile, @@ -253,6 +259,8 @@ objfunc(const PointImpl& bincenter, return std::make_tuple(score, fullbb); } +// Fill in the placer algorithm configuration with values carefully chosen for +// Slic3r. template void fillConfig(PConf& pcfg) { @@ -274,13 +282,19 @@ void fillConfig(PConf& pcfg) { pcfg.parallel = true; } +// Type trait for an arranger class for different bin types (box, circle, +// polygon, etc...) template class AutoArranger {}; + +// A class encapsulating the libnest2d Nester class and extending it with other +// management and spatial index structures for acceleration. template class _ArrBase { protected: + // Useful type shortcuts... using Placer = TPacker; using Selector = FirstFitSelection; using Packer = Nester; @@ -289,15 +303,15 @@ protected: using Pile = sl::Shapes; Packer m_pck; - PConfig m_pconf; // Placement configuration + PConfig m_pconf; // Placement configuration double m_bin_area; - SpatIndex m_rtree; - SpatIndex m_smallsrtree; - double m_norm; - Pile m_merged_pile; - Box m_pilebb; - ItemGroup m_remaining; - ItemGroup m_items; + SpatIndex m_rtree; // spatial index for the normal (bigger) objects + SpatIndex m_smallsrtree; // spatial index for only the smaller items + double m_norm; // A coefficient to scale distances + Pile m_merged_pile; // The already merged pile (vector of items) + Box m_pilebb; // The bounding box of the merged pile. + ItemGroup m_remaining; // Remaining items (m_items at the beginning) + ItemGroup m_items; // The items to be packed public: _ArrBase(const TBin& bin, Distance dist, @@ -308,6 +322,8 @@ public: { fillConfig(m_pconf); + // Set up a callback that is called just before arranging starts + // This functionality is provided by the Nester class (m_pack). m_pconf.before_packing = [this](const Pile& merged_pile, // merged pile const ItemGroup& items, // packed items @@ -344,8 +360,8 @@ public: } }; -template<> -class AutoArranger: public _ArrBase { +// Arranger specialization for a Box shaped bin. +template<> class AutoArranger: public _ArrBase { public: AutoArranger(const Box& bin, Distance dist, @@ -354,6 +370,9 @@ public: _ArrBase(bin, dist, progressind, stopcond) { + // Here we set up the actual object function that calls the common + // object function for all bin shapes than does an additional inside + // check for the arranged pile. m_pconf.object_function = [this, bin] (const Item &item) { auto result = objfunc(bin.center(), @@ -387,8 +406,8 @@ inline lnCircle to_lnCircle(const Circle& circ) { return lnCircle({circ.center()(0), circ.center()(1)}, circ.radius()); } -template<> -class AutoArranger: public _ArrBase { +// Arranger specialization for circle shaped bin. +template<> class AutoArranger: public _ArrBase { public: AutoArranger(const lnCircle& bin, Distance dist, @@ -396,6 +415,7 @@ public: std::function stopcond): _ArrBase(bin, dist, progressind, stopcond) { + // As with the box, only the inside check is different. m_pconf.object_function = [this, &bin] (const Item &item) { auto result = objfunc(bin.center(), @@ -431,8 +451,9 @@ public: } }; -template<> -class AutoArranger: public _ArrBase { +// Arranger specialization for a generalized polygon. +// Warning: this is unfinished business. It may or may not work. +template<> class AutoArranger: public _ArrBase { public: AutoArranger(const PolygonImpl& bin, Distance dist, std::function progressind, @@ -461,8 +482,10 @@ public: } }; -template<> // Specialization with no bin -class AutoArranger: public _ArrBase { +// Specialization with no bin. In this case the arranger should just arrange +// all objects into a minimum sized pile but it is not limited by a bin. A +// consequence is that only one pile should be created. +template<> class AutoArranger: public _ArrBase { public: AutoArranger(Distance dist, std::function progressind, @@ -490,14 +513,15 @@ public: // A container which stores a pointer to the 3D object and its projected // 2D shape from top view. -using ShapeData2D = - std::vector>; +using ShapeData2D = std::vector>; ShapeData2D projectModelFromTop(const Slic3r::Model &model) { ShapeData2D ret; - auto s = std::accumulate(model.objects.begin(), model.objects.end(), size_t(0), - [](size_t s, ModelObject* o){ + // Count all the items on the bin (all the object's instances) + auto s = std::accumulate(model.objects.begin(), model.objects.end(), + size_t(0), [](size_t s, ModelObject* o) + { return s + o->instances.size(); }); @@ -517,7 +541,8 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { rmesh.rotate_x(float(finst->get_rotation()(X))); rmesh.rotate_y(float(finst->get_rotation()(Y))); - // TODO export the exact 2D projection + // TODO export the exact 2D projection. Cannot do it as libnest2d + // does not support concave shapes (yet). auto p = rmesh.convex_hull(); p.make_clockwise(); @@ -549,6 +574,8 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { return ret; } +// Apply the calculated translations and rotations (currently disabled) to the +// Model object instances. void applyResult( IndexedPackGroup::value_type& group, Coord batch_offset, @@ -576,6 +603,7 @@ void applyResult( } } +// Get the type of bed geometry from a simple vector of points. BedShapeHint bedShape(const Polyline &bed) { BedShapeHint ret; @@ -654,11 +682,15 @@ BedShapeHint bedShape(const Polyline &bed) { return ret; } -bool arrange(Model &model, - coord_t min_obj_distance, - const Polyline &bed, - BedShapeHint bedhint, - bool first_bin_only, +// The final client function to arrange the Model. A progress indicator and +// a stop predicate can be also be passed to control the process. +bool arrange(Model &model, // The model with the geometries + coord_t min_obj_distance, // Has to be in scaled (clipper) measure + const Polyline &bed, // The bed geometry. + BedShapeHint bedhint, // Hint about the bed geometry type. + bool first_bin_only, // What to do is not all items fit. + + // Controlling callbacks. std::function progressind, std::function stopcondition) { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 1981f338e..acaba9d6a 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -287,18 +287,8 @@ std::vector Print::object_extruders() const { std::vector extruders; extruders.reserve(m_regions.size() * 3); - - for (const PrintRegion *region : m_regions) { - // these checks reflect the same logic used in the GUI for enabling/disabling - // extruder selection fields - if (region->config().perimeters.value > 0 || m_config.brim_width.value > 0) - extruders.emplace_back(region->config().perimeter_extruder - 1); - if (region->config().fill_density.value > 0) - extruders.emplace_back(region->config().infill_extruder - 1); - if (region->config().top_solid_layers.value > 0 || region->config().bottom_solid_layers.value > 0) - extruders.emplace_back(region->config().solid_infill_extruder - 1); - } - + for (const PrintRegion *region : m_regions) + region->collect_object_printing_extruders(extruders); sort_remove_duplicates(extruders); return extruders; } @@ -366,37 +356,6 @@ double Print::max_allowed_layer_height() const return nozzle_diameter_max; } -static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders) -{ - if (opt.value > (int)num_extruders) - // assign the default extruder - opt.value = 1; -} - -static PrintObjectConfig object_config_from_model(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders) -{ - PrintObjectConfig config = default_object_config; - normalize_and_apply_config(config, object.config); - // Clamp invalid extruders to the default extruder (with index 1). - clamp_exturder_to_default(config.support_material_extruder, num_extruders); - clamp_exturder_to_default(config.support_material_interface_extruder, num_extruders); - return config; -} - -static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders) -{ - PrintRegionConfig config = default_region_config; - normalize_and_apply_config(config, volume.get_object()->config); - normalize_and_apply_config(config, volume.config); - if (! volume.material_id().empty()) - normalize_and_apply_config(config, volume.material()->config); - // Clamp invalid extruders to the default extruder (with index 1). - clamp_exturder_to_default(config.infill_extruder, num_extruders); - clamp_exturder_to_default(config.perimeter_extruder, num_extruders); - clamp_exturder_to_default(config.solid_infill_extruder, num_extruders); - return config; -} - // Caller is responsible for supplying models whose objects don't collide // and have explicit instance positions. void Print::add_model_object(ModelObject* model_object, int idx) @@ -433,7 +392,7 @@ void Print::add_model_object(ModelObject* model_object, int idx) if (! volume->is_model_part() && ! volume->is_modifier()) continue; // Get the config applied to this volume. - PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume, 99999); + PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, 99999); // Find an existing print region with the same config. size_t region_id = size_t(-1); for (size_t i = 0; i < m_regions.size(); ++ i) @@ -514,12 +473,12 @@ bool Print::apply_config(DynamicPrintConfig config) // If the new config for this volume differs from the other // volume configs currently associated to this region, it means // the region subdivision does not make sense anymore. - if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume, 99999))) { + if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999))) { rearrange_regions = true; goto exit_for_rearrange_regions; } } else { - this_region_config = region_config_from_model_volume(m_default_region_config, volume, 99999); + this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999); this_region_config_set = true; } for (const PrintRegionConfig &cfg : other_region_configs) { @@ -563,10 +522,6 @@ exit_for_rearrange_regions: invalidated = true; } - // Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads. - for (PrintObject *object : m_objects) - object->update_layer_height_profile(); - return invalidated; } @@ -888,8 +843,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co if (model_parts_differ || modifiers_differ || model_object.origin_translation != model_object_new.origin_translation || model_object.layer_height_ranges != model_object_new.layer_height_ranges || - model_object.layer_height_profile != model_object_new.layer_height_profile || - model_object.layer_height_profile_valid != model_object_new.layer_height_profile_valid) { + model_object.layer_height_profile != model_object_new.layer_height_profile) { // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects. auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); for (auto it = range.first; it != range.second; ++ it) { @@ -915,7 +869,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co if (object_config_changed) model_object.config = model_object_new.config; if (! object_diff.empty() || object_config_changed) { - PrintObjectConfig new_config = object_config_from_model(m_default_object_config, model_object, num_extruders); + PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders); auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); for (auto it = range.first; it != range.second; ++ it) { t_config_option_keys diff = it->print_object->config().diff(new_config); @@ -957,7 +911,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co old.emplace_back(&(*it)); } // Generate a list of trafos and XY offsets for instances of a ModelObject - PrintObjectConfig config = object_config_from_model(m_default_object_config, *model_object, num_extruders); + PrintObjectConfig config = PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders); std::vector new_print_instances = print_objects_from_model_object(*model_object); if (old.empty()) { // Simple case, just generate new instances. @@ -1048,11 +1002,11 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co // If the new config for this volume differs from the other // volume configs currently associated to this region, it means // the region subdivision does not make sense anymore. - if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume, num_extruders))) + if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders))) // Regions were split. Reset this print_object. goto print_object_end; } else { - this_region_config = region_config_from_model_volume(m_default_region_config, volume, num_extruders); + this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders); for (size_t i = 0; i < region_id; ++i) { const PrintRegion ®ion_other = *m_regions[i]; if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config)) @@ -1103,7 +1057,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co int region_id = -1; if (&print_object == &print_object0) { // Get the config applied to this volume. - PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume, num_extruders); + PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, num_extruders); // Find an existing print region with the same config. int idx_empty_slot = -1; for (int i = 0; i < (int)m_regions.size(); ++ i) { @@ -1139,13 +1093,6 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co } } - // Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads. - for (PrintObject *object : m_objects) - if (! object->layer_height_profile_valid) - // No need to call the next line as the step should already be invalidated above. - // update_apply_status(object->invalidate_step(posSlice)); - object->update_layer_height_profile(); - //FIXME there may be a race condition with the G-code export running at the background thread. this->update_object_placeholders(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index eaad767ea..d0591e764 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -45,6 +45,10 @@ public: // Average diameter of nozzles participating on extruding this region. coordf_t bridging_height_avg(const PrintConfig &print_config) const; + // Collect extruder indices used to print this region's object. + void collect_object_printing_extruders(std::vector &object_extruders) const; + static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, std::vector &object_extruders); + // Methods modifying the PrintRegion's state: public: Print* print() { return m_print; } @@ -80,14 +84,8 @@ public: std::vector> region_volumes; // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. - // The pairs of are packed into a 1D array to simplify handling by the Perl XS. - // layer_height_profile must not be set by the background thread. + // The pairs of are packed into a 1D array. std::vector layer_height_profile; - // There is a layer_height_profile at both PrintObject and ModelObject. The layer_height_profile at the ModelObject - // is used for interactive editing and for loading / storing into a project file (AMF file as of today). - // This flag indicates that the layer_height_profile at the UI has been updated, therefore the backend needs to get it. - // This flag is necessary as we cannot safely clear the layer_height_profile if the background calculation is running. - bool layer_height_profile_valid; // this is set to true when LayerRegion->slices is split in top/internal/bottom // so that next call to make_perimeters() performs a union() before computing loops @@ -129,23 +127,19 @@ public: SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z); void delete_support_layer(int idx); - // To be used over the layer_height_profile of both the PrintObject and ModelObject - // to initialize the height profile with the height ranges. - bool update_layer_height_profile(std::vector &layer_height_profile) const; - - // Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile. - // The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces. - bool update_layer_height_profile(); - - void reset_layer_height_profile(); - - void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action); + // Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters. + // Returns true, if the layer_height_profile was changed. + static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector &layer_height_profile); // Collect the slicing parameters, to be used by variable layer thickness algorithm, // by the interactive layer height editor and by the printing process itself. // The slicing parameters are dependent on various configuration values // (layer height, first layer height, raft settings, print nozzle diameter etc). - SlicingParameters slicing_parameters() const; + SlicingParameters slicing_parameters() const; + static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object); + + // returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions) + std::vector object_extruders() const; // Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t void slice(); @@ -172,6 +166,9 @@ protected: // Invalidate steps based on a set of parameters changed. bool invalidate_state_by_config_options(const std::vector &opt_keys); + static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders); + static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders); + private: void make_perimeters(); void prepare_infill(); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a7214afa3..9dd101123 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -38,8 +38,7 @@ namespace Slic3r { PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_instances) : PrintObjectBaseWithState(print, model_object), typed_slices(false), - size(Vec3crd::Zero()), - layer_height_profile_valid(false) + size(Vec3crd::Zero()) { // Compute the translation to be applied to our meshes so that we work with smaller coordinates { @@ -106,6 +105,8 @@ void PrintObject::slice() if (! this->set_started(posSlice)) return; m_print->set_status(10, "Processing triangulated mesh"); + this->update_layer_height_profile(*this->model_object(), this->slicing_parameters(), this->layer_height_profile); + m_print->throw_if_canceled(); this->_slice(); m_print->throw_if_canceled(); // Fix the model. @@ -455,7 +456,6 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorreset_layer_height_profile(); } else if ( opt_key == "clip_multipart_objects" @@ -542,7 +542,6 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorinvalidate_all_steps(); - this->reset_layer_height_profile(); invalidated = true; } } @@ -1329,55 +1328,107 @@ void PrintObject::bridge_over_infill() } } +static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders) +{ + if (opt.value > (int)num_extruders) + // assign the default extruder + opt.value = 1; +} + +PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders) +{ + PrintObjectConfig config = default_object_config; + normalize_and_apply_config(config, object.config); + // Clamp invalid extruders to the default extruder (with index 1). + clamp_exturder_to_default(config.support_material_extruder, num_extruders); + clamp_exturder_to_default(config.support_material_interface_extruder, num_extruders); + return config; +} + +PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders) +{ + PrintRegionConfig config = default_region_config; + normalize_and_apply_config(config, volume.get_object()->config); + normalize_and_apply_config(config, volume.config); + if (! volume.material_id().empty()) + normalize_and_apply_config(config, volume.material()->config); + // Clamp invalid extruders to the default extruder (with index 1). + clamp_exturder_to_default(config.infill_extruder, num_extruders); + clamp_exturder_to_default(config.perimeter_extruder, num_extruders); + clamp_exturder_to_default(config.solid_infill_extruder, num_extruders); + return config; +} + SlicingParameters PrintObject::slicing_parameters() const { return SlicingParameters::create_from_config( this->print()->config(), m_config, - unscale(this->size(2)), this->print()->object_extruders()); + unscale(this->size(2)), this->object_extruders()); } -bool PrintObject::update_layer_height_profile(std::vector &layer_height_profile) const +SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object) +{ + PrintConfig print_config; + PrintObjectConfig object_config; + PrintRegionConfig default_region_config; + print_config .apply(full_config, true); + object_config.apply(full_config, true); + default_region_config.apply(full_config, true); + size_t num_extruders = print_config.nozzle_diameter.size(); + object_config = object_config_from_model_object(object_config, model_object, num_extruders); + + std::vector object_extruders; + for (const ModelVolume *model_volume : model_object.volumes) + if (model_volume->is_model_part()) + PrintRegion::collect_object_printing_extruders( + print_config, + region_config_from_model_volume(default_region_config, *model_volume, num_extruders), + object_extruders); + sort_remove_duplicates(object_extruders); + + return SlicingParameters::create_from_config(print_config, object_config, model_object.bounding_box().max.z(), object_extruders); +} + +// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions) +std::vector PrintObject::object_extruders() const +{ + std::vector extruders; + extruders.reserve(this->region_volumes.size() * 3); + for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) + if (! this->region_volumes[idx_region].empty()) + m_print->get_region(idx_region)->collect_object_printing_extruders(extruders); + sort_remove_duplicates(extruders); + return extruders; +} + +bool PrintObject::update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector &layer_height_profile) { bool updated = false; - // If the layer height profile is not set, try to use the one stored at the ModelObject. if (layer_height_profile.empty()) { - layer_height_profile = this->model_object()->layer_height_profile; + layer_height_profile = model_object.layer_height_profile; updated = true; } // Verify the layer_height_profile. - SlicingParameters slicing_params = this->slicing_parameters(); if (! layer_height_profile.empty() && // Must not be of even length. ((layer_height_profile.size() & 1) != 0 || // Last entry must be at the top of the object. - std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) > 1e-3)) + std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_height()) > 1e-3)) layer_height_profile.clear(); if (layer_height_profile.empty()) { if (0) // if (this->layer_height_profile.empty()) - layer_height_profile = layer_height_profile_adaptive(slicing_params, this->model_object()->layer_height_ranges, this->model_object()->volumes); + layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_height_ranges, model_object.volumes); else - layer_height_profile = layer_height_profile_from_ranges(slicing_params, this->model_object()->layer_height_ranges); + layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_height_ranges); updated = true; } return updated; } -// This must be called from the main thread as it modifies the layer_height_profile. -bool PrintObject::update_layer_height_profile() -{ - // If the layer height profile has been marked as invalid for some reason (modified at the UI level - // or invalidated due to the slicing parameters), clear it now. - if (! this->layer_height_profile_valid) { - this->layer_height_profile.clear(); - this->layer_height_profile_valid = true; - } - return this->update_layer_height_profile(this->layer_height_profile); -} - // 1) Decides Z positions of the layers, // 2) Initializes layers and their regions // 3) Slices the object meshes @@ -2198,22 +2249,4 @@ void PrintObject::_generate_support_material() support_material.generate(*this); } -void PrintObject::reset_layer_height_profile() -{ - // Reset the layer_heigth_profile. - this->layer_height_profile.clear(); - this->layer_height_profile_valid = false; - // Reset the source layer_height_profile if it exists at the ModelObject. - this->model_object()->layer_height_profile.clear(); - this->model_object()->layer_height_profile_valid = false; -} - -void PrintObject::adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) -{ - update_layer_height_profile(m_model_object->layer_height_profile); - Slic3r::adjust_layer_height_profile(slicing_parameters(), m_model_object->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action)); - m_model_object->layer_height_profile_valid = true; - layer_height_profile_valid = false; -} - } // namespace Slic3r diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index 4ea777b4a..73b40487b 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -61,4 +61,20 @@ coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.value); } +void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, std::vector &object_extruders) +{ + // These checks reflect the same logic used in the GUI for enabling/disabling extruder selection fields. + if (region_config.perimeters.value > 0 || print_config.brim_width.value > 0) + object_extruders.emplace_back(region_config.perimeter_extruder - 1); + if (region_config.fill_density.value > 0) + object_extruders.emplace_back(region_config.infill_extruder - 1); + if (region_config.top_solid_layers.value > 0 || region_config.bottom_solid_layers.value > 0) + object_extruders.emplace_back(region_config.solid_infill_extruder - 1); +} + +void PrintRegion::collect_object_printing_extruders(std::vector &object_extruders) const +{ + collect_object_printing_extruders(print()->config(), this->config(), object_extruders); +} + } diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 6070864e1..395fedc9f 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -131,7 +131,7 @@ extern std::vector layer_height_profile_adaptive( const ModelVolumePtrs &volumes); -enum LayerHeightEditActionType { +enum LayerHeightEditActionType : unsigned int { LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0, LAYER_HEIGHT_EDIT_ACTION_DECREASE = 1, LAYER_HEIGHT_EDIT_ACTION_REDUCE = 2, diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 0ee89296a..c6265f275 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -386,58 +386,6 @@ void GLVolume::render() const ::glPopMatrix(); } -void GLVolume::render_using_layer_height() const -{ - if (!is_active) - return; - - GLint current_program_id; - glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id); - - if ((layer_height_texture_data.shader_id > 0) && (layer_height_texture_data.shader_id != current_program_id)) - glUseProgram(layer_height_texture_data.shader_id); - - GLint z_to_texture_row_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_to_texture_row") : -1; - GLint z_texture_row_to_normalized_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_texture_row_to_normalized") : -1; - GLint z_cursor_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor") : -1; - GLint z_cursor_band_width_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor_band_width") : -1; - GLint world_matrix_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "volume_world_matrix") : -1; - - if (z_to_texture_row_id >= 0) - glUniform1f(z_to_texture_row_id, (GLfloat)layer_height_texture_z_to_row_id()); - - if (z_texture_row_to_normalized_id >= 0) - glUniform1f(z_texture_row_to_normalized_id, (GLfloat)(1.0f / layer_height_texture_height())); - - if (z_cursor_id >= 0) - glUniform1f(z_cursor_id, (GLfloat)(layer_height_texture_data.print_object->model_object()->bounding_box().max(2) * layer_height_texture_data.z_cursor_relative)); - - if (z_cursor_band_width_id >= 0) - glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width); - - if (world_matrix_id >= 0) - ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast().data()); - - GLsizei w = (GLsizei)layer_height_texture_width(); - GLsizei h = (GLsizei)layer_height_texture_height(); - GLsizei half_w = w / 2; - GLsizei half_h = h / 2; - - ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glBindTexture(GL_TEXTURE_2D, layer_height_texture_data.texture_id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level0()); - glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1()); - - render(); - - glBindTexture(GL_TEXTURE_2D, 0); - - if ((current_program_id > 0) && (layer_height_texture_data.shader_id != current_program_id)) - glUseProgram(current_program_id); -} - void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const { if (!is_active) @@ -446,16 +394,6 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id) return; - if (layer_height_texture_data.can_use()) - { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); - render_using_layer_height(); - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - return; - } - GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first)); GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first)); if (n_triangles + n_quads == 0) @@ -558,44 +496,6 @@ void GLVolume::render_legacy() const ::glPopMatrix(); } -double GLVolume::layer_height_texture_z_to_row_id() const -{ - return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max(2)); -} - -void GLVolume::generate_layer_height_texture(const PrintObject *print_object, bool force) -{ - LayersTexture *tex = this->layer_height_texture.get(); - if (tex == nullptr) - // No layer_height_texture is assigned to this GLVolume, therefore the layer height texture cannot be filled. - return; - - // Always try to update the layer height profile. - bool update = print_object->update_layer_height_profile(const_cast(print_object->model_object())->layer_height_profile) || force; - // Update if the layer height profile was changed, or when the texture is not valid. - if (! update && ! tex->data.empty() && tex->cells > 0) - // Texture is valid, don't update. - return; - - if (tex->data.empty()) { - tex->width = 1024; - tex->height = 1024; - tex->levels = 2; - tex->data.assign(tex->width * tex->height * 5, 0); - } - - SlicingParameters slicing_params = print_object->slicing_parameters(); - bool level_of_detail_2nd_level = true; - tex->cells = Slic3r::generate_layer_height_texture( - slicing_params, - Slic3r::generate_object_layers(slicing_params, print_object->model_object()->layer_height_profile), - tex->data.data(), tex->height, tex->width, level_of_detail_2nd_level); -} - -// 512x512 bitmaps are supported everywhere, but that may not be sufficent for super large print volumes. -#define LAYER_HEIGHT_TEXTURE_WIDTH 1024 -#define LAYER_HEIGHT_TEXTURE_HEIGHT 1024 - std::vector GLVolumeCollection::load_object( const ModelObject *model_object, int obj_idx, @@ -603,19 +503,15 @@ std::vector GLVolumeCollection::load_object( const std::string &color_by, bool use_VBOs) { - // Object will share a single common layer height texture between all printable volumes. - std::shared_ptr layer_height_texture = std::make_shared(); std::vector volumes_idx; for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) for (int instance_idx : instance_idxs) - volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, layer_height_texture, obj_idx, volume_idx, instance_idx, color_by, use_VBOs)); + volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, use_VBOs)); return volumes_idx; } int GLVolumeCollection::load_object_volume( const ModelObject *model_object, - // Layer height texture is shared between all printable volumes of a single ModelObject. - std::shared_ptr &layer_height_texture, int obj_idx, int volume_idx, int instance_idx, @@ -666,7 +562,6 @@ int GLVolumeCollection::load_object_volume( v.set_convex_hull(&model_volume->get_convex_hull(), false); if (extruder_id != -1) v.extruder_id = extruder_id; - v.layer_height_texture = layer_height_texture; } v.is_modifier = ! model_volume->is_model_part(); v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); @@ -795,17 +690,19 @@ int GLVolumeCollection::load_wipe_tower_preview( #if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING typedef std::pair GLVolumeWithZ; typedef std::vector GLVolumesWithZList; -GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type) +static GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, std::function filter_func) { GLVolumesWithZList list; + list.reserve(volumes.size()); for (GLVolume* volume : volumes) { bool is_transparent = (volume->render_color[3] < 1.0f); - if (((type == GLVolumeCollection::Opaque) && !is_transparent) || - ((type == GLVolumeCollection::Transparent) && is_transparent) || - (type == GLVolumeCollection::All)) - list.push_back(std::make_pair(volume, 0.0)); + if ((((type == GLVolumeCollection::Opaque) && !is_transparent) || + ((type == GLVolumeCollection::Transparent) && is_transparent) || + (type == GLVolumeCollection::All)) && + (! filter_func || filter_func(*volume))) + list.emplace_back(std::make_pair(volume, 0.0)); } if ((type == GLVolumeCollection::Transparent) && (list.size() > 1)) @@ -826,7 +723,7 @@ GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollec return list; } -void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool disable_cullface) const +void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool disable_cullface, std::function filter_func) const #else void GLVolumeCollection::render_VBOs() const #endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING @@ -862,26 +759,17 @@ void GLVolumeCollection::render_VBOs() const ::glUniform2fv(z_range_id, 1, (const GLfloat*)z_range); #if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING - GLVolumesWithZList to_render = volumes_to_render(this->volumes, type); - for (GLVolumeWithZ& volume : to_render) - { - if (volume.first->layer_height_texture_data.can_use()) - volume.first->generate_layer_height_texture(volume.first->layer_height_texture_data.print_object, false); - else - volume.first->set_render_color(); - + GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, filter_func); + for (GLVolumeWithZ& volume : to_render) { + volume.first->set_render_color(); volume.first->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id); } #else for (GLVolume *volume : this->volumes) - { - if (volume->layer_height_texture_data.can_use()) - volume->generate_layer_height_texture(volume->layer_height_texture_data.print_object, false); - else + if (! filter_func || filter_func(*volume)) { volume->set_render_color(); - - volume->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id); - } + volume->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id); + } #endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING ::glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -917,7 +805,7 @@ void GLVolumeCollection::render_legacy() const glEnableClientState(GL_NORMAL_ARRAY); #if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING - GLVolumesWithZList to_render = volumes_to_render(this->volumes, type); + GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, std::function()); for (GLVolumeWithZ& volume : to_render) { volume.first->set_render_color(); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index b7b1af73d..1ea69a22f 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -9,6 +9,8 @@ #include "libslic3r/Model.hpp" #include "slic3r/GUI/GLCanvas3DManager.hpp" +#include + namespace Slic3r { class Print; @@ -199,50 +201,7 @@ private: } }; -class LayersTexture -{ -public: - LayersTexture() : width(0), height(0), levels(0), cells(0) {} - - // Texture data - std::vector data; - // Width of the texture, top level. - size_t width; - // Height of the texture, top level. - size_t height; - // For how many levels of detail is the data allocated? - size_t levels; - // Number of texture cells allocated for the height texture. - size_t cells; -}; - class GLVolume { - struct LayerHeightTextureData - { - // ID of the layer height texture - unsigned int texture_id; - // ID of the shader used to render with the layer height texture - unsigned int shader_id; - // The print object to update when generating the layer height texture - const PrintObject* print_object; - - float z_cursor_relative; - float edit_band_width; - - LayerHeightTextureData() { reset(); } - - void reset() - { - texture_id = 0; - shader_id = 0; - print_object = nullptr; - z_cursor_relative = 0.0f; - edit_band_width = 0.0f; - } - - bool can_use() const { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); } - }; - public: static const float SELECTED_COLOR[4]; static const float HOVER_COLOR[4]; @@ -406,7 +365,7 @@ public: int volume_idx() const { return this->composite_id.volume_id; } int instance_idx() const { return this->composite_id.instance_id; } - Transform3d world_matrix() const; + Transform3d world_matrix() const; const BoundingBoxf3& transformed_bounding_box() const; const BoundingBoxf3& transformed_convex_hull_bounding_box() const; @@ -416,49 +375,13 @@ public: void set_range(coordf_t low, coordf_t high); void render() const; - void render_using_layer_height() const; void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const; void render_legacy() const; void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); } void release_geometry() { this->indexed_vertex_array.release_geometry(); } - /************************************************ Layer height texture ****************************************************/ - std::shared_ptr layer_height_texture; - // Data to render this volume using the layer height texture - LayerHeightTextureData layer_height_texture_data; - - bool has_layer_height_texture() const - { return this->layer_height_texture.get() != nullptr; } - size_t layer_height_texture_width() const - { return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->width; } - size_t layer_height_texture_height() const - { return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->height; } - size_t layer_height_texture_cells() const - { return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->cells; } - void* layer_height_texture_data_ptr_level0() const { - return (layer_height_texture.get() == nullptr) ? 0 : - (void*)layer_height_texture->data.data(); - } - void* layer_height_texture_data_ptr_level1() const { - return (layer_height_texture.get() == nullptr) ? 0 : - (void*)(layer_height_texture->data.data() + layer_height_texture->width * layer_height_texture->height * 4); - } - double layer_height_texture_z_to_row_id() const; - void generate_layer_height_texture(const PrintObject *print_object, bool force); - - void set_layer_height_texture_data(unsigned int texture_id, unsigned int shader_id, const PrintObject* print_object, float z_cursor_relative, float edit_band_width) - { - layer_height_texture_data.texture_id = texture_id; - layer_height_texture_data.shader_id = shader_id; - layer_height_texture_data.print_object = print_object; - layer_height_texture_data.z_cursor_relative = z_cursor_relative; - layer_height_texture_data.edit_band_width = edit_band_width; - } - - void reset_layer_height_texture_data() { layer_height_texture_data.reset(); } - - void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } + void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } }; typedef std::vector GLVolumePtrs; @@ -498,7 +421,6 @@ public: int load_object_volume( const ModelObject *model_object, - std::shared_ptr &layer_height_texture, int obj_idx, int volume_idx, int instance_idx, @@ -521,7 +443,7 @@ public: // Render the volumes by OpenGL. #if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING - void render_VBOs(ERenderType type, bool disable_cullface) const; + void render_VBOs(ERenderType type, bool disable_cullface, std::function filter_func = std::function()) const; void render_legacy(ERenderType type, bool disable_cullface) const; #else void render_VBOs() const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 16f519455..24998cd60 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -819,12 +819,16 @@ GLCanvas3D::LayersEditing::LayersEditing() : m_use_legacy_opengl(false) , m_enabled(false) , m_z_texture_id(0) + , m_model_object(nullptr) + , m_object_max_z(0.f) + , m_slicing_parameters(new SlicingParameters) + , m_layer_height_profile_modified(false) , state(Unknown) , band_width(2.0f) , strength(0.005f) , last_object_id(-1) , last_z(0.0f) - , last_action(0) + , last_action(LAYER_HEIGHT_EDIT_ACTION_INCREASE) { } @@ -835,6 +839,7 @@ GLCanvas3D::LayersEditing::~LayersEditing() ::glDeleteTextures(1, &m_z_texture_id); m_z_texture_id = 0; } + delete m_slicing_parameters; } bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) @@ -854,9 +859,20 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, return true; } +void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id) +{ + m_model_object = (object_id >= 0) ? model.objects[object_id] : nullptr; + m_object_max_z = (m_model_object == nullptr) ? 0.f : m_model_object->bounding_box().max.z(); + if (m_model_object == nullptr || this->last_object_id != object_id) { + m_layer_height_profile.clear(); + m_layer_height_profile_modified = false; + } + this->last_object_id = object_id; +} + bool GLCanvas3D::LayersEditing::is_allowed() const { - return !m_use_legacy_opengl && m_shader.is_initialized(); + return !m_use_legacy_opengl && m_shader.is_initialized() && m_shader.get_shader()->shader_program_id > 0 && m_z_texture_id > 0; } void GLCanvas3D::LayersEditing::set_use_legacy_opengl(bool use_legacy_opengl) @@ -874,12 +890,7 @@ void GLCanvas3D::LayersEditing::set_enabled(bool enabled) m_enabled = is_allowed() && enabled; } -unsigned int GLCanvas3D::LayersEditing::get_z_texture_id() const -{ - return m_z_texture_id; -} - -void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const +void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const { if (!m_enabled) return; @@ -896,8 +907,8 @@ void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObje _render_tooltip_texture(canvas, bar_rect, reset_rect); _render_reset_texture(reset_rect); - _render_active_object_annotations(canvas, volume, print_object, bar_rect); - _render_profile(print_object, bar_rect); + _render_active_object_annotations(canvas, bar_rect); + _render_profile(bar_rect); // Revert the matrices. ::glPopMatrix(); @@ -905,12 +916,6 @@ void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObje ::glEnable(GL_DEPTH_TEST); } -int GLCanvas3D::LayersEditing::get_shader_program_id() const -{ - const GLShader* shader = m_shader.get_shader(); - return (shader != nullptr) ? shader->shader_program_id : -1; -} - float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) { const Point& mouse_pos = canvas.get_local_mouse_position(); @@ -1023,21 +1028,19 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top()); } -void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const +void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const { - float max_z = print_object.model_object()->bounding_box().max(2); - m_shader.start_using(); - m_shader.set_uniform("z_to_texture_row", (float)volume.layer_height_texture_z_to_row_id()); - m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)volume.layer_height_texture_height()); - m_shader.set_uniform("z_cursor", max_z * get_cursor_z_relative(canvas)); + m_shader.set_uniform("z_to_texture_row", float(m_layers_texture.cells - 1) / (float(m_layers_texture.width) * m_object_max_z)); + m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)m_layers_texture.height); + m_shader.set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas)); m_shader.set_uniform("z_cursor_band_width", band_width); // The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX); - GLsizei w = (GLsizei)volume.layer_height_texture_width(); - GLsizei h = (GLsizei)volume.layer_height_texture_height(); + GLsizei w = (GLsizei)m_layers_texture.width; + GLsizei h = (GLsizei)m_layers_texture.height; GLsizei half_w = w / 2; GLsizei half_h = h / 2; @@ -1045,8 +1048,8 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas ::glBindTexture(GL_TEXTURE_2D, m_z_texture_id); ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); ::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - ::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level0()); - ::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level1()); + ::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data()); + ::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4); // Render the color bar float l = bar_rect.get_left(); @@ -1057,25 +1060,24 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas ::glBegin(GL_QUADS); ::glVertex3f(l, b, 0.0f); ::glVertex3f(r, b, 0.0f); - ::glVertex3f(r, t, max_z); - ::glVertex3f(l, t, max_z); + ::glVertex3f(r, t, m_object_max_z); + ::glVertex3f(l, t, m_object_max_z); ::glEnd(); ::glBindTexture(GL_TEXTURE_2D, 0); m_shader.stop_using(); } -void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, const Rect& bar_rect) const +void GLCanvas3D::LayersEditing::_render_profile(const Rect& bar_rect) const { - // FIXME show some kind of legend. + //FIXME show some kind of legend. // Get a maximum layer height value. - // FIXME This is a duplicate code of Slicing.cpp. + //FIXME This is a duplicate code of Slicing.cpp. double layer_height_max = DBL_MAX; - const PrintConfig& print_config = print_object.print()->config(); - const std::vector& nozzle_diameters = dynamic_cast(print_config.option("nozzle_diameter"))->values; - const std::vector& layer_heights_min = dynamic_cast(print_config.option("min_layer_height"))->values; - const std::vector& layer_heights_max = dynamic_cast(print_config.option("max_layer_height"))->values; + const std::vector& nozzle_diameters = dynamic_cast(m_config->option("nozzle_diameter"))->values; + const std::vector& layer_heights_min = dynamic_cast(m_config->option("min_layer_height"))->values; + const std::vector& layer_heights_max = dynamic_cast(m_config->option("max_layer_height"))->values; for (unsigned int i = 0; i < (unsigned int)nozzle_diameters.size(); ++i) { double lh_min = (layer_heights_min[i] == 0.0) ? 0.07 : std::max(0.01, layer_heights_min[i]); @@ -1086,15 +1088,19 @@ void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, // Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region. layer_height_max *= 1.12; - double max_z = unscale(print_object.size(2)); - double layer_height = dynamic_cast(print_object.config().option("layer_height"))->value; + // Get global layer height. + double layer_height = dynamic_cast(m_config->option("layer_height"))->value; + // Override the global layer height with object's layer height if set. + const ConfigOption *opt_object_layer_height = m_model_object->config.option("layer_height"); + if (opt_object_layer_height != nullptr) + layer_height = dynamic_cast(opt_object_layer_height)->value; float l = bar_rect.get_left(); float w = bar_rect.get_right() - l; float b = bar_rect.get_bottom(); float t = bar_rect.get_top(); float h = t - b; float scale_x = w / (float)layer_height_max; - float scale_y = h / (float)max_z; + float scale_y = h / m_object_max_z; float x = l + (float)layer_height * scale_x; // Baseline @@ -1105,19 +1111,129 @@ void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, ::glEnd(); // Curve - const ModelObject* model_object = print_object.model_object(); - if (model_object->layer_height_profile_valid) - { - const std::vector& profile = model_object->layer_height_profile; + ::glColor3f(0.0f, 0.0f, 1.0f); + ::glBegin(GL_LINE_STRIP); + for (unsigned int i = 0; i < m_layer_height_profile.size(); i += 2) + ::glVertex2f(l + (float)m_layer_height_profile[i + 1] * scale_x, b + (float)m_layer_height_profile[i] * scale_y); + ::glEnd(); +} - ::glColor3f(0.0f, 0.0f, 1.0f); - ::glBegin(GL_LINE_STRIP); - for (unsigned int i = 0; i < profile.size(); i += 2) - { - ::glVertex2f(l + (float)profile[i + 1] * scale_x, b + (float)profile[i] * scale_y); +void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const GLVolumeCollection &volumes) const +{ + assert(this->is_allowed()); + assert(this->last_object_id != -1); + GLint shader_id = m_shader.get_shader()->shader_program_id; + assert(shader_id > 0); + + GLint current_program_id; + glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id); + if (shader_id > 0 && shader_id != current_program_id) + // The layer editing shader is not yet active. Activate it. + glUseProgram(shader_id); + else + // The layer editing shader was already active. + current_program_id = -1; + + GLint z_to_texture_row_id = glGetUniformLocation(shader_id, "z_to_texture_row"); + GLint z_texture_row_to_normalized_id = glGetUniformLocation(shader_id, "z_texture_row_to_normalized"); + GLint z_cursor_id = glGetUniformLocation(shader_id, "z_cursor"); + GLint z_cursor_band_width_id = glGetUniformLocation(shader_id, "z_cursor_band_width"); + GLint world_matrix_id = glGetUniformLocation(shader_id, "volume_world_matrix"); + + if (z_to_texture_row_id != -1 && z_texture_row_to_normalized_id != -1 && z_cursor_id != -1 && z_cursor_band_width_id != -1 && world_matrix_id != -1) + { + const_cast(this)->generate_layer_height_texture(); + + // Uniforms were resolved, go ahead using the layer editing shader. + glUniform1f(z_to_texture_row_id, GLfloat(m_layers_texture.cells - 1) / (GLfloat(m_layers_texture.width) * GLfloat(m_object_max_z))); + glUniform1f(z_texture_row_to_normalized_id, GLfloat(1.0f / m_layers_texture.height)); + glUniform1f(z_cursor_id, GLfloat(m_object_max_z) * GLfloat(this->get_cursor_z_relative(canvas))); + glUniform1f(z_cursor_band_width_id, GLfloat(this->band_width)); + // Initialize the layer height texture mapping. + GLsizei w = (GLsizei)m_layers_texture.width; + GLsizei h = (GLsizei)m_layers_texture.height; + GLsizei half_w = w / 2; + GLsizei half_h = h / 2; + ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindTexture(GL_TEXTURE_2D, m_z_texture_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data()); + glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4); + for (const GLVolume *glvolume : volumes.volumes) { + // Render the object using the layer editing shader and texture. + if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier) + continue; + ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast().data()); + glvolume->render(); } - ::glEnd(); + // Revert back to the previous shader. + glBindTexture(GL_TEXTURE_2D, 0); + if (current_program_id > 0) + glUseProgram(current_program_id); + } + else + { + // Something went wrong. Just render the object. + assert(false); + for (const GLVolume *glvolume : volumes.volumes) { + // Render the object using the layer editing shader and texture. + if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier) + continue; + ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast().data()); + glvolume->render(); + } + } +} + +void GLCanvas3D::LayersEditing::adjust_layer_height_profile() +{ + *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object); + PrintObject::update_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile); + Slic3r::adjust_layer_height_profile(*m_slicing_parameters, m_layer_height_profile, this->last_z, this->strength, this->band_width, this->last_action); + m_layer_height_profile_modified = true; + m_layers_texture.valid = false; +} + +void GLCanvas3D::LayersEditing::generate_layer_height_texture() +{ + // Always try to update the layer height profile. + bool update = ! m_layers_texture.valid; + *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object); + if (PrintObject::update_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile)) { + // Initialized to the default value. + m_layer_height_profile_modified = false; + update = true; } + // Update if the layer height profile was changed, or when the texture is not valid. + if (! update && ! m_layers_texture.data.empty() && m_layers_texture.cells > 0) + // Texture is valid, don't update. + return; + + if (m_layers_texture.data.empty()) { + m_layers_texture.width = 1024; + m_layers_texture.height = 1024; + m_layers_texture.levels = 2; + m_layers_texture.data.assign(m_layers_texture.width * m_layers_texture.height * 5, 0); + } + + bool level_of_detail_2nd_level = true; + m_layers_texture.cells = Slic3r::generate_layer_height_texture( + *m_slicing_parameters, + Slic3r::generate_object_layers(*m_slicing_parameters, m_layer_height_profile), + m_layers_texture.data.data(), m_layers_texture.height, m_layers_texture.width, level_of_detail_2nd_level); + m_layers_texture.valid = true; +} + +void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas) +{ + if (last_object_id >= 0) { + if (m_layer_height_profile_modified) { + const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } + } + m_layer_height_profile_modified = false; } const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); @@ -3689,7 +3805,6 @@ wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); -wxDEFINE_EVENT(EVT_GLCANVAS_MODEL_UPDATE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent); @@ -3916,9 +4031,10 @@ int GLCanvas3D::check_volumes_outside_state() const return (int)state; } -void GLCanvas3D::set_config(DynamicPrintConfig* config) +void GLCanvas3D::set_config(const DynamicPrintConfig* config) { m_config = config; + m_layers_editing.set_config(config); } void GLCanvas3D::set_process(BackgroundSlicingProcess *process) @@ -4262,7 +4378,8 @@ void GLCanvas3D::render() _resize_toolbars(); _render_toolbar(); _render_view_toolbar(); - _render_layer_editing_overlay(); + if (m_layers_editing.last_object_id >= 0) + m_layers_editing.render_overlay(*this); #if ENABLE_IMGUI wxGetApp().imgui()->render(); @@ -4502,8 +4619,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_volumes.volumes = std::move(glvolumes_new); for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++ obj_idx) { const ModelObject &model_object = *m_model->objects[obj_idx]; - // Object will share a single common layer height texture between all printable volumes. - std::shared_ptr layer_height_texture; for (int volume_idx = 0; volume_idx < (int)model_object.volumes.size(); ++ volume_idx) { const ModelVolume &model_volume = *model_object.volumes[volume_idx]; for (int instance_idx = 0; instance_idx < (int)model_object.instances.size(); ++ instance_idx) { @@ -4513,33 +4628,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); if (it->new_geometry()) { // New volume. - if (model_volume.is_model_part() && ! layer_height_texture) { - // New object part needs to have the layer height texture assigned, which is shared with the other volumes of the same part. - // Search for the layer height texture in the other volumes. - for (int iv = volume_idx; iv < (int)model_object.volumes.size(); ++ iv) { - const ModelVolume &mv = *model_object.volumes[iv]; - if (mv.is_model_part()) - for (int ii = instance_idx; ii < (int)model_object.instances.size(); ++ ii) { - const ModelInstance &mi = *model_object.instances[ii]; - ModelVolumeState key(mv.id(), mi.id()); - auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); - assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); - if (! it->new_geometry()) { - // Found an old printable GLVolume (existing before this function was called). - assert(m_volumes.volumes[it->volume_idx]->geometry_id == key.geometry_id); - // Reuse the layer height texture. - const GLVolume *volume = m_volumes.volumes[it->volume_idx]; - assert(volume->layer_height_texture); - layer_height_texture = volume->layer_height_texture; - goto iv_end; - } - } - } - iv_end: - if (! layer_height_texture) - layer_height_texture = std::make_shared(); - } - m_volumes.load_object_volume(&model_object, layer_height_texture, obj_idx, volume_idx, instance_idx, m_color_by, m_use_VBOs && m_initialized); + m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_use_VBOs && m_initialized); m_volumes.volumes.back()->geometry_id = key.geometry_id; } else { // Recycling an old GLVolume. @@ -4547,11 +4636,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re assert(existing_volume.geometry_id == key.geometry_id); // Update the Object/Volume/Instance indices into the current Model. existing_volume.composite_id = it->composite_id; - if (model_volume.is_model_part() && ! layer_height_texture) { - assert(existing_volume.layer_height_texture); - // cache its layer height texture - layer_height_texture = existing_volume.layer_height_texture; - } } } } @@ -4945,10 +5029,8 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) void GLCanvas3D::on_timer(wxTimerEvent& evt) { - if (m_layers_editing.state != LayersEditing::Editing) - return; - - _perform_layer_editing_action(); + if (m_layers_editing.state == LayersEditing::Editing) + _perform_layer_editing_action(); } void GLCanvas3D::on_mouse(wxMouseEvent& evt) @@ -4967,7 +5049,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) int selected_object_idx = m_selection.get_object_idx(); int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; - m_layers_editing.last_object_id = layer_editing_object_idx; + m_layers_editing.select_object(*m_model, layer_editing_object_idx); bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position, *this); int view_toolbar_contains_mouse = (m_view_toolbar != nullptr) ? m_view_toolbar->contains_mouse(m_mouse.position, *this) : -1; @@ -5032,10 +5114,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { if (evt.LeftDown()) { - // A volume is selected and the mouse is inside the reset button. - // The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself, - // therefore it is safe to call it while the background processing is running. - const_cast(this->fff_print()->get_object(layer_editing_object_idx))->reset_layer_height_profile(); + // A volume is selected and the mouse is inside the reset button. Reset the ModelObject's layer height profile. + m_model->objects[layer_editing_object_idx]->layer_height_profile.clear(); // Index 2 means no editing, just wait for mouse up event. m_layers_editing.state = LayersEditing::Completed; @@ -5296,9 +5376,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { m_layers_editing.state = LayersEditing::Unknown; _stop_timer(); - - if (layer_editing_object_idx != -1) - post_event(SimpleEvent(EVT_GLCANVAS_MODEL_UPDATE)); + m_layers_editing.accept_changes(*this); } else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging) { @@ -6074,28 +6152,6 @@ float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) co return (float)std::min((double)cnv_size.get_width() / max_x, (double)cnv_size.get_height() / max_y); } -void GLCanvas3D::_mark_volumes_for_layer_height() const -{ - const Print *print = (m_process == nullptr) ? nullptr : m_process->fff_print(); - if (print == nullptr) - return; - - for (GLVolume* vol : m_volumes.volumes) - { - int object_id = vol->object_idx(); - int shader_id = m_layers_editing.get_shader_program_id(); - - if (is_layers_editing_enabled() && (shader_id != -1) && vol->selected && - vol->has_layer_height_texture() && (object_id < (int)print->objects().size())) - { - vol->set_layer_height_texture_data(m_layers_editing.get_z_texture_id(), shader_id, - print->get_object(object_id), _get_layers_editing_cursor_z_relative(), m_layers_editing.band_width); - } - else - vol->reset_layer_height_texture_data(); - } -} - void GLCanvas3D::_refresh_if_shown_on_screen() { if (_is_shown_on_screen()) @@ -6241,7 +6297,8 @@ void GLCanvas3D::_render_objects() const { if (m_picking_enabled) { - _mark_volumes_for_layer_height(); + // Update the layer editing selection to the first object selected, update the current object maximum Z. + const_cast(m_layers_editing).select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1); if (m_config != nullptr) { @@ -6262,8 +6319,18 @@ void GLCanvas3D::_render_objects() const m_shader.start_using(); #if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING - // do not cull backfaces to show broken geometry, if any - m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled); + if (m_picking_enabled && m_layers_editing.is_enabled() && m_layers_editing.last_object_id != -1) { + int object_id = m_layers_editing.last_object_id; + m_volumes.render_VBOs(GLVolumeCollection::Opaque, false, [object_id](const GLVolume &volume) { + // Which volume to paint without the layer height profile shader? + return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); + }); + // Let LayersEditing handle rendering of the active object using the layer height profile shader. + m_layers_editing.render_volumes(*this, this->m_volumes); + } else { + // do not cull backfaces to show broken geometry, if any + m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled); + } m_volumes.render_VBOs(GLVolumeCollection::Transparent, false); #else m_volumes.render_VBOs(); @@ -6344,39 +6411,6 @@ void GLCanvas3D::_render_legend_texture() const m_legend_texture.render(*this); } -void GLCanvas3D::_render_layer_editing_overlay() const -{ - const Print *print = this->fff_print(); - if ((print == nullptr) || print->objects().empty()) - return; - - GLVolume* volume = nullptr; - - for (GLVolume* vol : m_volumes.volumes) - { - if ((vol != nullptr) && vol->selected && vol->has_layer_height_texture()) - { - volume = vol; - break; - } - } - - if (volume == nullptr) - return; - - // If the active object was not allocated at the Print, go away.This should only be a momentary case between an object addition / deletion - // and an update by Platter::async_apply_config. - int object_idx = volume->object_idx(); - if ((int)print->objects().size() <= object_idx) - return; - - const PrintObject* print_object = print->get_object(object_idx); - if (print_object == nullptr) - return; - - m_layers_editing.render(*this, *print_object, *volume); -} - void GLCanvas3D::_render_volumes(bool fake_colors) const { static const GLfloat INV_255 = 1.0f / 255.0f; @@ -6776,55 +6810,24 @@ void GLCanvas3D::_update_gizmos_data() } } -float GLCanvas3D::_get_layers_editing_cursor_z_relative() const -{ - return m_layers_editing.get_cursor_z_relative(*this); -} - void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) { int object_idx_selected = m_layers_editing.last_object_id; if (object_idx_selected == -1) return; - const Print *print = this->fff_print(); - if (print == nullptr) - return; - - const PrintObject* selected_obj = print->get_object(object_idx_selected); - if (selected_obj == nullptr) - return; - // A volume is selected. Test, whether hovering over a layer thickness bar. if (evt != nullptr) { const Rect& rect = LayersEditing::get_bar_rect_screen(*this); float b = rect.get_bottom(); - m_layers_editing.last_z = unscale(selected_obj->size(2)) * (b - evt->GetY() - 1.0f) / (b - rect.get_top()); - m_layers_editing.last_action = evt->ShiftDown() ? (evt->RightIsDown() ? 3 : 2) : (evt->RightIsDown() ? 0 : 1); + m_layers_editing.last_z = m_layers_editing.object_max_z() * (b - evt->GetY() - 1.0f) / (b - rect.get_top()); + m_layers_editing.last_action = + evt->ShiftDown() ? (evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_SMOOTH : LAYER_HEIGHT_EDIT_ACTION_REDUCE) : + (evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_INCREASE : LAYER_HEIGHT_EDIT_ACTION_DECREASE); } - // Mark the volume as modified, so Print will pick its layer height profile ? Where to mark it ? - // Start a timer to refresh the print ? schedule_background_process() ? - // The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself, - // therefore it is safe to call it while the background processing is running. - const_cast(selected_obj)->adjust_layer_height_profile(m_layers_editing.last_z, m_layers_editing.strength, m_layers_editing.band_width, m_layers_editing.last_action); - - // searches the id of the first volume of the selected object - int volume_idx = 0; - for (int i = 0; i < object_idx_selected; ++i) - { - const PrintObject* obj = print->get_object(i); - if (obj != nullptr) - { - for (int j = 0; j < (int)obj->region_volumes.size(); ++j) - { - volume_idx += (int)obj->region_volumes[j].size(); - } - } - } - - m_volumes.volumes[volume_idx]->generate_layer_height_texture(selected_obj, 1); + m_layers_editing.adjust_layer_height_profile(); _refresh_if_shown_on_screen(); // Automatic action on mouse down with the same coordinate. diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 625c73169..6c6ea9af8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -29,6 +29,8 @@ class GLShader; class ExPolygon; class BackgroundSlicingProcess; class GCodePreviewData; +struct SlicingParameters; +enum LayerHeightEditActionType : unsigned int; namespace GUI { @@ -101,7 +103,6 @@ wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); -wxDECLARE_EVENT(EVT_GLCANVAS_MODEL_UPDATE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent); @@ -293,12 +294,42 @@ class GLCanvas3D }; private: - bool m_use_legacy_opengl; - bool m_enabled; - Shader m_shader; - unsigned int m_z_texture_id; - mutable GLTexture m_tooltip_texture; - mutable GLTexture m_reset_texture; + bool m_use_legacy_opengl; + bool m_enabled; + Shader m_shader; + unsigned int m_z_texture_id; + mutable GLTexture m_tooltip_texture; + mutable GLTexture m_reset_texture; + // Not owned by LayersEditing. + const DynamicPrintConfig *m_config; + // ModelObject for the currently selected object (Model::objects[last_object_id]). + const ModelObject *m_model_object; + // Maximum z of the currently selected object (Model::objects[last_object_id]). + float m_object_max_z; + // Owned by LayersEditing. + SlicingParameters *m_slicing_parameters; + std::vector m_layer_height_profile; + bool m_layer_height_profile_modified; + + class LayersTexture + { + public: + LayersTexture() : width(0), height(0), levels(0), cells(0), valid(false) {} + + // Texture data + std::vector data; + // Width of the texture, top level. + size_t width; + // Height of the texture, top level. + size_t height; + // For how many levels of detail is the data allocated? + size_t levels; + // Number of texture cells allocated for the height texture. + size_t cells; + // Does it need to be refreshed? + bool valid; + }; + LayersTexture m_layers_texture; public: EState state; @@ -306,12 +337,14 @@ class GLCanvas3D float strength; int last_object_id; float last_z; - unsigned int last_action; + LayerHeightEditActionType last_action; LayersEditing(); ~LayersEditing(); bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); + void set_config(const DynamicPrintConfig* config) { m_config = config; } + void select_object(const Model &model, int object_id); bool is_allowed() const; void set_use_legacy_opengl(bool use_legacy_opengl); @@ -319,11 +352,12 @@ class GLCanvas3D bool is_enabled() const; void set_enabled(bool enabled); - unsigned int get_z_texture_id() const; + void render_overlay(const GLCanvas3D& canvas) const; + void render_volumes(const GLCanvas3D& canvas, const GLVolumeCollection& volumes) const; - void render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const; - - int get_shader_program_id() const; + void generate_layer_height_texture(); + void adjust_layer_height_profile(); + void accept_changes(GLCanvas3D& canvas); static float get_cursor_z_relative(const GLCanvas3D& canvas); static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); @@ -333,12 +367,14 @@ class GLCanvas3D static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); static Rect get_reset_rect_viewport(const GLCanvas3D& canvas); + float object_max_z() const { return m_object_max_z; } + private: bool _is_initialized() const; void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const; void _render_reset_texture(const Rect& reset_rect) const; - void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const; - void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const; + void _render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const; + void _render_profile(const Rect& bar_rect) const; }; struct Mouse @@ -821,7 +857,7 @@ private: mutable GLVolumeCollection m_volumes; Selection m_selection; - DynamicPrintConfig* m_config; + const DynamicPrintConfig* m_config; Model* m_model; BackgroundSlicingProcess *m_process; @@ -881,7 +917,7 @@ public: void reset_volumes(); int check_volumes_outside_state() const; - void set_config(DynamicPrintConfig* config); + void set_config(const DynamicPrintConfig* config); void set_process(BackgroundSlicingProcess* process); void set_model(Model* model); @@ -1023,7 +1059,6 @@ private: void _zoom_to_bounding_box(const BoundingBoxf3& bbox); float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const; - void _mark_volumes_for_layer_height() const; void _refresh_if_shown_on_screen(); void _camera_tranform() const; @@ -1038,7 +1073,6 @@ private: #endif // ENABLE_RENDER_SELECTION_CENTER void _render_warning_texture() const; void _render_legend_texture() const; - void _render_layer_editing_overlay() const; void _render_volumes(bool fake_colors) const; void _render_current_gizmo() const; void _render_gizmos_overlay() const; @@ -1055,7 +1089,6 @@ private: void _update_volumes_hover_state() const; void _update_gizmos_data(); - float _get_layers_editing_cursor_z_relative() const; void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); // Convert the screen space coordinate to an object space coordinate. diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index ad432c273..53ca52cb6 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -27,7 +27,7 @@ namespace Slic3r { namespace GUI { - View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process) +View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process) : m_canvas_widget(nullptr) , m_canvas(nullptr) #if !ENABLE_IMGUI diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 2a6003986..15bb2c07b 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -159,6 +159,7 @@ void MainFrame::create_preset_tabs() add_created_tab(new TabSLAPrint(m_tabpanel)); add_created_tab(new TabSLAMaterial(m_tabpanel)); add_created_tab(new TabPrinter(m_tabpanel)); + GUI::wxGetApp().load_current_presets(); } void MainFrame::add_created_tab(Tab* panel) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ee4b62bf1..59ceafd16 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1098,7 +1098,10 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host", "printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", - "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology" + "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology", + // The following three layer height config values are passed here for View3D::m_canvas to receive + // layer height updates for the layer height. + "min_layer_height", "max_layer_height", "layer_height", "first_layer_height" })) , sidebar(new Sidebar(q)) , delayed_scene_refresh(false) @@ -1167,7 +1170,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this); view3D_canvas->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); view3D_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this); - view3D_canvas->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); }); view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); }); view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); }); view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); @@ -2072,7 +2074,7 @@ void Plater::priv::reload_from_disk() } } - // XXX: Restore more: layer_height_ranges, layer_height_profile, layer_height_profile_valid (?) + // XXX: Restore more: layer_height_ranges, layer_height_profile (?) } remove(obj_orig_idx); @@ -2103,7 +2105,7 @@ void Plater::priv::fix_through_netfabb(const int obj_idx) o->volumes[i]->config.apply(model_object->volumes[i]->config); } } - // FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid, + // FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile } remove(obj_idx); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 02366c721..0b1f1e571 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -245,9 +245,6 @@ void Tab::create_preset_tab() // Initialize the DynamicPrintConfig by default keys/values. build(); rebuild_page_tree(); -// update(); - // Load the currently selected preset into the GUI, update the preset selection box. - load_current_preset(); } void Tab::load_initial_data()