Merge branch 'master' of https://github.com/prusa3d/Slic3r into objects_centering

This commit is contained in:
Enrico Turri 2019-01-21 11:21:21 +01:00
commit 4577a49d5a
18 changed files with 456 additions and 595 deletions

View file

@ -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<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
const std::vector<double> &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);

View file

@ -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 << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
if (!object->name.empty())
stream << " <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
const std::vector<double> &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 << " <metadata type=\"slic3r.layer_height_profile\">";

View file

@ -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);

View file

@ -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 <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
// The pairs of <z, layer_height> are packed into a 1D array.
std::vector<coordf_t> 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" */

View file

@ -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<double /*score*/, Box /*farthest point from bin center*/>
objfunc(const PointImpl& bincenter,
const shapelike::Shapes<PolygonImpl>& 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<class PConf>
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 TBin>
class AutoArranger {};
// A class encapsulating the libnest2d Nester class and extending it with other
// management and spatial index structures for acceleration.
template<class TBin>
class _ArrBase {
protected:
// Useful type shortcuts...
using Placer = TPacker<TBin>;
using Selector = FirstFitSelection;
using Packer = Nester<Placer, Selector>;
@ -291,13 +305,13 @@ protected:
Packer m_pck;
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<Box>: public _ArrBase<Box> {
// Arranger specialization for a Box shaped bin.
template<> class AutoArranger<Box>: public _ArrBase<Box> {
public:
AutoArranger(const Box& bin, Distance dist,
@ -354,6 +370,9 @@ public:
_ArrBase<Box>(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<lnCircle>: public _ArrBase<lnCircle> {
// Arranger specialization for circle shaped bin.
template<> class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
public:
AutoArranger(const lnCircle& bin, Distance dist,
@ -396,6 +415,7 @@ public:
std::function<bool(void)> stopcond):
_ArrBase<lnCircle>(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<PolygonImpl>: public _ArrBase<PolygonImpl> {
// Arranger specialization for a generalized polygon.
// Warning: this is unfinished business. It may or may not work.
template<> class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
public:
AutoArranger(const PolygonImpl& bin, Distance dist,
std::function<void(unsigned)> progressind,
@ -461,8 +482,10 @@ public:
}
};
template<> // Specialization with no bin
class AutoArranger<bool>: public _ArrBase<Box> {
// 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<bool>: public _ArrBase<Box> {
public:
AutoArranger(Distance dist, std::function<void(unsigned)> 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<std::pair<Slic3r::ModelInstance*, Item>>;
using ShapeData2D = std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
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<void (unsigned)> progressind,
std::function<bool ()> stopcondition)
{

View file

@ -287,18 +287,8 @@ std::vector<unsigned int> Print::object_extruders() const
{
std::vector<unsigned int> 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<PrintInstances> 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 &region_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();

View file

@ -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<unsigned int> &object_extruders) const;
static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, std::vector<unsigned int> &object_extruders);
// Methods modifying the PrintRegion's state:
public:
Print* print() { return m_print; }
@ -80,14 +84,8 @@ public:
std::vector<std::vector<int>> region_volumes;
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
// The pairs of <z, layer_height> 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 <z, layer_height> are packed into a 1D array.
std::vector<coordf_t> 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<coordf_t> &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<coordf_t> &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;
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<unsigned int> 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<t_config_option_key> &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();

View file

@ -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::vector<t_config_
|| opt_key == "first_layer_height"
|| opt_key == "raft_layers") {
steps.emplace_back(posSlice);
this->reset_layer_height_profile();
}
else if (
opt_key == "clip_multipart_objects"
@ -542,7 +542,6 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
} else {
// for legacy, if we can't handle this option let's invalidate all steps
this->invalidate_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<double>(this->size(2)), this->print()->object_extruders());
unscale<double>(this->size(2)), this->object_extruders());
}
bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &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<unsigned int> 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<unsigned int> PrintObject::object_extruders() const
{
std::vector<unsigned int> 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<coordf_t> &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

View file

@ -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 &region_config, std::vector<unsigned int> &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<unsigned int> &object_extruders) const
{
collect_object_printing_extruders(print()->config(), this->config(), object_extruders);
}
}

View file

@ -131,7 +131,7 @@ extern std::vector<coordf_t> 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,

View file

@ -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, &current_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<float>().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<ModelObject*>(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<int> GLVolumeCollection::load_object(
const ModelObject *model_object,
int obj_idx,
@ -603,19 +503,15 @@ std::vector<int> 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<LayersTexture> layer_height_texture = std::make_shared<LayersTexture>();
std::vector<int> 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<LayersTexture> &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<GLVolume*, double> GLVolumeWithZ;
typedef std::vector<GLVolumeWithZ> GLVolumesWithZList;
GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type)
static GLVolumesWithZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, std::function<bool(const GLVolume&)> 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) ||
if ((((type == GLVolumeCollection::Opaque) && !is_transparent) ||
((type == GLVolumeCollection::Transparent) && is_transparent) ||
(type == GLVolumeCollection::All))
list.push_back(std::make_pair(volume, 0.0));
(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<bool(const GLVolume&)> filter_func) const
#else
void GLVolumeCollection::render_VBOs() const
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
@ -862,24 +759,15 @@ 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
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);
}
#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING
@ -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<bool(const GLVolume&)>());
for (GLVolumeWithZ& volume : to_render)
{
volume.first->set_render_color();

View file

@ -9,6 +9,8 @@
#include "libslic3r/Model.hpp"
#include "slic3r/GUI/GLCanvas3DManager.hpp"
#include <functional>
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<char> 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];
@ -416,48 +375,12 @@ 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<LayersTexture> 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; }
};
@ -498,7 +421,6 @@ public:
int load_object_volume(
const ModelObject *model_object,
std::shared_ptr<LayersTexture> &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<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
void render_legacy(ERenderType type, bool disable_cullface) const;
#else
void render_VBOs() const;

View file

@ -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<double>& nozzle_diameters = dynamic_cast<const ConfigOptionFloats*>(print_config.option("nozzle_diameter"))->values;
const std::vector<double>& layer_heights_min = dynamic_cast<const ConfigOptionFloats*>(print_config.option("min_layer_height"))->values;
const std::vector<double>& layer_heights_max = dynamic_cast<const ConfigOptionFloats*>(print_config.option("max_layer_height"))->values;
const std::vector<double>& nozzle_diameters = dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"))->values;
const std::vector<double>& layer_heights_min = dynamic_cast<const ConfigOptionFloats*>(m_config->option("min_layer_height"))->values;
const std::vector<double>& layer_heights_max = dynamic_cast<const ConfigOptionFloats*>(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<double>(print_object.size(2));
double layer_height = dynamic_cast<const ConfigOptionFloat*>(print_object.config().option("layer_height"))->value;
// Get global layer height.
double layer_height = dynamic_cast<const ConfigOptionFloat*>(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<const ConfigOptionFloat*>(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<double>& profile = model_object->layer_height_profile;
::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);
}
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();
}
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, &current_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<LayersEditing*>(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<float>().data());
glvolume->render();
}
// 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<float>().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<ModelObject*>(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<LayersTexture> 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<LayersTexture>();
}
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,9 +5029,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
void GLCanvas3D::on_timer(wxTimerEvent& evt)
{
if (m_layers_editing.state != LayersEditing::Editing)
return;
if (m_layers_editing.state == LayersEditing::Editing)
_perform_layer_editing_action();
}
@ -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<PrintObject*>(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<LayersEditing&>(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
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<double>(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<PrintObject*>(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.

View file

@ -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);
@ -299,6 +300,36 @@ class GLCanvas3D
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<coordf_t> 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<char> 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.

View file

@ -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

View file

@ -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)

View file

@ -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);

View file

@ -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()