WIP: Synchronization of SLA background processing.
This commit is contained in:
parent
a21f1783a9
commit
314f7dc674
@ -1515,6 +1515,71 @@ Transform3d ModelInstance::get_matrix(bool dont_translate, bool dont_rotate, boo
|
|||||||
}
|
}
|
||||||
#endif // !ENABLE_MODELVOLUME_TRANSFORM
|
#endif // !ENABLE_MODELVOLUME_TRANSFORM
|
||||||
|
|
||||||
|
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
||||||
|
// ordered in the same order. In that case it is not necessary to kill the background processing.
|
||||||
|
bool model_object_list_equal(const Model &model_old, const Model &model_new)
|
||||||
|
{
|
||||||
|
if (model_old.objects.size() != model_new.objects.size())
|
||||||
|
return false;
|
||||||
|
for (size_t i = 0; i < model_old.objects.size(); ++ i)
|
||||||
|
if (model_old.objects[i]->id() != model_new.objects[i]->id())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test whether the new model is just an extension of the old model (new objects were added
|
||||||
|
// to the end of the original list. In that case it is not necessary to kill the background processing.
|
||||||
|
bool model_object_list_extended(const Model &model_old, const Model &model_new)
|
||||||
|
{
|
||||||
|
if (model_old.objects.size() >= model_new.objects.size())
|
||||||
|
return false;
|
||||||
|
for (size_t i = 0; i < model_old.objects.size(); ++ i)
|
||||||
|
if (model_old.objects[i]->id() != model_new.objects[i]->id())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type)
|
||||||
|
{
|
||||||
|
bool modifiers_differ = false;
|
||||||
|
size_t i_old, i_new;
|
||||||
|
for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) {
|
||||||
|
const ModelVolume &mv_old = *model_object_old.volumes[i_old];
|
||||||
|
const ModelVolume &mv_new = *model_object_new.volumes[i_new];
|
||||||
|
if (mv_old.type() != type) {
|
||||||
|
++ i_old;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mv_new.type() != type) {
|
||||||
|
++ i_new;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (mv_old.id() != mv_new.id())
|
||||||
|
return true;
|
||||||
|
//FIXME test for the content of the mesh!
|
||||||
|
|
||||||
|
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||||
|
if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
|
||||||
|
return true;
|
||||||
|
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||||
|
++i_old;
|
||||||
|
++ i_new;
|
||||||
|
}
|
||||||
|
for (; i_old < model_object_old.volumes.size(); ++ i_old) {
|
||||||
|
const ModelVolume &mv_old = *model_object_old.volumes[i_old];
|
||||||
|
if (mv_old.type() == type)
|
||||||
|
// ModelVolume was deleted.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (; i_new < model_object_new.volumes.size(); ++ i_new) {
|
||||||
|
const ModelVolume &mv_new = *model_object_new.volumes[i_new];
|
||||||
|
if (mv_new.type() == type)
|
||||||
|
// ModelVolume was added.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
||||||
void check_model_ids_validity(const Model &model)
|
void check_model_ids_validity(const Model &model)
|
||||||
|
@ -23,6 +23,7 @@ class ModelMaterial;
|
|||||||
class ModelObject;
|
class ModelObject;
|
||||||
class ModelVolume;
|
class ModelVolume;
|
||||||
class Print;
|
class Print;
|
||||||
|
class SLAPrint;
|
||||||
|
|
||||||
typedef std::string t_model_material_id;
|
typedef std::string t_model_material_id;
|
||||||
typedef std::string t_model_material_attribute;
|
typedef std::string t_model_material_attribute;
|
||||||
@ -252,6 +253,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Print;
|
friend class Print;
|
||||||
|
friend class SLAPrint;
|
||||||
// Called by Print::apply() to set the model pointer after making a copy.
|
// Called by Print::apply() to set the model pointer after making a copy.
|
||||||
void set_model(Model *model) { m_model = model; }
|
void set_model(Model *model) { m_model = model; }
|
||||||
|
|
||||||
@ -368,6 +370,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Print;
|
friend class Print;
|
||||||
|
friend class SLAPrint;
|
||||||
friend class ModelObject;
|
friend class ModelObject;
|
||||||
|
|
||||||
explicit ModelVolume(const ModelVolume &rhs) = default;
|
explicit ModelVolume(const ModelVolume &rhs) = default;
|
||||||
@ -535,6 +538,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Print;
|
friend class Print;
|
||||||
|
friend class SLAPrint;
|
||||||
friend class ModelObject;
|
friend class ModelObject;
|
||||||
|
|
||||||
explicit ModelInstance(const ModelInstance &rhs) = default;
|
explicit ModelInstance(const ModelInstance &rhs) = default;
|
||||||
@ -652,6 +656,18 @@ private:
|
|||||||
#undef MODELBASE_DERIVED_COPY_MOVE_CLONE
|
#undef MODELBASE_DERIVED_COPY_MOVE_CLONE
|
||||||
#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE
|
#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE
|
||||||
|
|
||||||
|
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
||||||
|
// ordered in the same order. In that case it is not necessary to kill the background processing.
|
||||||
|
extern bool model_object_list_equal(const Model &model_old, const Model &model_new);
|
||||||
|
|
||||||
|
// Test whether the new model is just an extension of the old model (new objects were added
|
||||||
|
// to the end of the original list. In that case it is not necessary to kill the background processing.
|
||||||
|
extern bool model_object_list_extended(const Model &model_old, const Model &model_new);
|
||||||
|
|
||||||
|
// Test whether the new ModelObject contains a different set of volumes (or sorted in a different order)
|
||||||
|
// than the old ModelObject.
|
||||||
|
extern bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type);
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
||||||
void check_model_ids_validity(const Model &model);
|
void check_model_ids_validity(const Model &model);
|
||||||
|
@ -550,71 +550,6 @@ exit_for_rearrange_regions:
|
|||||||
return invalidated;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
|
||||||
// ordered in the same order. In that case it is not necessary to kill the background processing.
|
|
||||||
static inline bool model_object_list_equal(const Model &model_old, const Model &model_new)
|
|
||||||
{
|
|
||||||
if (model_old.objects.size() != model_new.objects.size())
|
|
||||||
return false;
|
|
||||||
for (size_t i = 0; i < model_old.objects.size(); ++ i)
|
|
||||||
if (model_old.objects[i]->id() != model_new.objects[i]->id())
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test whether the new model is just an extension of the old model (new objects were added
|
|
||||||
// to the end of the original list. In that case it is not necessary to kill the background processing.
|
|
||||||
static inline bool model_object_list_extended(const Model &model_old, const Model &model_new)
|
|
||||||
{
|
|
||||||
if (model_old.objects.size() >= model_new.objects.size())
|
|
||||||
return false;
|
|
||||||
for (size_t i = 0; i < model_old.objects.size(); ++ i)
|
|
||||||
if (model_old.objects[i]->id() != model_new.objects[i]->id())
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type)
|
|
||||||
{
|
|
||||||
bool modifiers_differ = false;
|
|
||||||
size_t i_old, i_new;
|
|
||||||
for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) {
|
|
||||||
const ModelVolume &mv_old = *model_object_old.volumes[i_old];
|
|
||||||
const ModelVolume &mv_new = *model_object_new.volumes[i_new];
|
|
||||||
if (mv_old.type() != type) {
|
|
||||||
++ i_old;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (mv_new.type() != type) {
|
|
||||||
++ i_new;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (mv_old.id() != mv_new.id())
|
|
||||||
return true;
|
|
||||||
//FIXME test for the content of the mesh!
|
|
||||||
|
|
||||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
|
||||||
if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
|
|
||||||
return true;
|
|
||||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
|
||||||
++i_old;
|
|
||||||
++ i_new;
|
|
||||||
}
|
|
||||||
for (; i_old < model_object_old.volumes.size(); ++ i_old) {
|
|
||||||
const ModelVolume &mv_old = *model_object_old.volumes[i_old];
|
|
||||||
if (mv_old.type() == type)
|
|
||||||
// ModelVolume was deleted.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (; i_new < model_object_new.volumes.size(); ++ i_new) {
|
|
||||||
const ModelVolume &mv_new = *model_object_new.volumes[i_new];
|
|
||||||
if (mv_new.type() == type)
|
|
||||||
// ModelVolume was added.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
|
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
|
||||||
// in the exact order and with the same IDs.
|
// in the exact order and with the same IDs.
|
||||||
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
|
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
|
||||||
|
@ -131,9 +131,6 @@ public:
|
|||||||
SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
|
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);
|
void delete_support_layer(int idx);
|
||||||
|
|
||||||
// methods for handling state
|
|
||||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
|
||||||
|
|
||||||
// To be used over the layer_height_profile of both the PrintObject and ModelObject
|
// To be used over the layer_height_profile of both the PrintObject and ModelObject
|
||||||
// to initialize the height profile with the height ranges.
|
// to initialize the height profile with the height ranges.
|
||||||
bool update_layer_height_profile(std::vector<coordf_t> &layer_height_profile) const;
|
bool update_layer_height_profile(std::vector<coordf_t> &layer_height_profile) const;
|
||||||
@ -174,6 +171,8 @@ protected:
|
|||||||
bool invalidate_step(PrintObjectStep step);
|
bool invalidate_step(PrintObjectStep step);
|
||||||
// Invalidates all PrintObject and Print steps.
|
// Invalidates all PrintObject and Print steps.
|
||||||
bool invalidate_all_steps();
|
bool invalidate_all_steps();
|
||||||
|
// Invalidate steps based on a set of parameters changed.
|
||||||
|
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void make_perimeters();
|
void make_perimeters();
|
||||||
|
@ -902,7 +902,7 @@ class SLAPrintObjectConfig : public StaticPrintConfig
|
|||||||
{
|
{
|
||||||
STATIC_PRINT_CONFIG_CACHE(SLAPrintObjectConfig)
|
STATIC_PRINT_CONFIG_CACHE(SLAPrintObjectConfig)
|
||||||
public:
|
public:
|
||||||
ConfigOptionFloat layer_height;
|
ConfigOptionFloat layer_height;
|
||||||
|
|
||||||
// Radius in mm of the pointing side of the head.
|
// Radius in mm of the pointing side of the head.
|
||||||
ConfigOptionFloat support_head_front_radius /*= 0.2*/;
|
ConfigOptionFloat support_head_front_radius /*= 0.2*/;
|
||||||
|
@ -73,53 +73,296 @@ void SLAPrint::clear()
|
|||||||
m_objects.clear();
|
m_objects.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transformation without rotation around Z and without a shift by X and Y.
|
||||||
|
static Transform3d sla_trafo(const ModelObject &model_object)
|
||||||
|
{
|
||||||
|
ModelInstance &model_instance = *model_object.instances.front();
|
||||||
|
Vec3d offset = model_instance.get_offset();
|
||||||
|
Vec3d rotation = model_instance.get_rotation();
|
||||||
|
offset(0) = 0.;
|
||||||
|
offset(1) = 0.;
|
||||||
|
rotation(2) = 0.;
|
||||||
|
return Geometry::assemble_transform(offset, rotation, model_instance.get_scaling_factor(), model_instance.get_mirror());
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of instances, where the ModelInstance transformation is a composite of sla_trafo and the transformation defined by SLAPrintObject::Instance.
|
||||||
|
static std::vector<SLAPrintObject::Instance> sla_instances(const ModelObject &model_object)
|
||||||
|
{
|
||||||
|
std::vector<SLAPrintObject::Instance> instances;
|
||||||
|
for (ModelInstance *model_instance : model_object.instances)
|
||||||
|
if (model_instance->is_printable()) {
|
||||||
|
instances.emplace_back(SLAPrintObject::Instance(
|
||||||
|
model_instance->id(),
|
||||||
|
Point::new_scale(model_instance->get_offset(X), model_instance->get_offset(Y)),
|
||||||
|
float(model_instance->get_rotation(Z))));
|
||||||
|
}
|
||||||
|
return instances;
|
||||||
|
}
|
||||||
|
|
||||||
SLAPrint::ApplyStatus SLAPrint::apply(const Model &model,
|
SLAPrint::ApplyStatus SLAPrint::apply(const Model &model,
|
||||||
const DynamicPrintConfig &config_in)
|
const DynamicPrintConfig &config_in)
|
||||||
{
|
{
|
||||||
// if (m_objects.empty())
|
#ifdef _DEBUG
|
||||||
// return APPLY_STATUS_UNCHANGED;
|
check_model_ids_validity(model);
|
||||||
|
#endif /* _DEBUG */
|
||||||
|
|
||||||
|
// Make a copy of the config, normalize it.
|
||||||
|
DynamicPrintConfig config(config_in);
|
||||||
|
config.normalize();
|
||||||
|
// Collect changes to print config.
|
||||||
|
t_config_option_keys printer_diff = m_printer_config.diff(config);
|
||||||
|
t_config_option_keys material_diff = m_material_config.diff(config);
|
||||||
|
t_config_option_keys object_diff = m_default_object_config.diff(config);
|
||||||
|
|
||||||
|
// Do not use the ApplyStatus as we will use the max function when updating apply_status.
|
||||||
|
unsigned int apply_status = APPLY_STATUS_UNCHANGED;
|
||||||
|
auto update_apply_status = [&apply_status](bool invalidated)
|
||||||
|
{ apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
|
||||||
|
if (! (printer_diff.empty() && material_diff.empty() && object_diff.empty()))
|
||||||
|
update_apply_status(false);
|
||||||
|
|
||||||
// Grab the lock for the Print / PrintObject milestones.
|
// Grab the lock for the Print / PrintObject milestones.
|
||||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||||
if (m_objects.empty() && model.objects.empty() && m_model.objects.empty())
|
|
||||||
return APPLY_STATUS_UNCHANGED;
|
|
||||||
|
|
||||||
// Temporary: just to have to correct layer height for the rasterization
|
// The following call may stop the background processing.
|
||||||
DynamicPrintConfig config(config_in);
|
update_apply_status(this->invalidate_state_by_config_options(printer_diff));
|
||||||
config.normalize();
|
update_apply_status(this->invalidate_state_by_config_options(material_diff));
|
||||||
m_material_config.initial_layer_height.set(
|
|
||||||
config.opt<ConfigOptionFloat>("initial_layer_height"));
|
|
||||||
|
|
||||||
// Temporary quick fix, just invalidate everything.
|
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
|
||||||
{
|
m_printer_config.apply_only(config, printer_diff, true);
|
||||||
for (SLAPrintObject *print_object : m_objects) {
|
// Handle changes to material config.
|
||||||
print_object->invalidate_all_steps();
|
m_material_config.apply_only(config, material_diff, true);
|
||||||
delete print_object;
|
// Handle changes to object config defaults
|
||||||
}
|
m_default_object_config.apply_only(config, object_diff, true);
|
||||||
m_objects.clear();
|
|
||||||
this->invalidate_all_steps();
|
|
||||||
|
|
||||||
// Copy the model by value (deep copy),
|
struct ModelObjectStatus {
|
||||||
// keep the Model / ModelObject / ModelInstance / ModelVolume IDs.
|
enum Status {
|
||||||
|
Unknown,
|
||||||
|
Old,
|
||||||
|
New,
|
||||||
|
Moved,
|
||||||
|
Deleted,
|
||||||
|
};
|
||||||
|
ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
|
||||||
|
ModelID id;
|
||||||
|
Status status;
|
||||||
|
// Search by id.
|
||||||
|
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
|
||||||
|
};
|
||||||
|
std::set<ModelObjectStatus> model_object_status;
|
||||||
|
|
||||||
|
// 1) Synchronize model objects.
|
||||||
|
if (model.id() != m_model.id()) {
|
||||||
|
// Kill everything, initialize from scratch.
|
||||||
|
// Stop background processing.
|
||||||
|
this->call_cancell_callback();
|
||||||
|
update_apply_status(this->invalidate_all_steps());
|
||||||
|
for (SLAPrintObject *object : m_objects) {
|
||||||
|
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
|
||||||
|
delete object;
|
||||||
|
}
|
||||||
|
m_objects.clear();
|
||||||
m_model.assign_copy(model);
|
m_model.assign_copy(model);
|
||||||
// Generate new SLAPrintObjects.
|
for (const ModelObject *model_object : m_model.objects)
|
||||||
for (ModelObject *model_object : m_model.objects) {
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
|
||||||
auto po = new SLAPrintObject(this, model_object);
|
} else {
|
||||||
|
if (model_object_list_equal(m_model, model)) {
|
||||||
// po->m_config.layer_height.set(lh);
|
// The object list did not change.
|
||||||
po->m_config.apply(config, true);
|
for (const ModelObject *model_object : m_model.objects)
|
||||||
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
|
||||||
m_objects.emplace_back(po);
|
} else if (model_object_list_extended(m_model, model)) {
|
||||||
for (ModelInstance *oinst : model_object->instances) {
|
// Add new objects. Their volumes and configs will be synchronized later.
|
||||||
Point tr = Point::new_scale(oinst->get_offset()(X),
|
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||||
oinst->get_offset()(Y));
|
for (const ModelObject *model_object : m_model.objects)
|
||||||
auto rotZ = float(oinst->get_rotation()(Z));
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
|
||||||
po->m_instances.emplace_back(oinst->id(), tr, rotZ);
|
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
|
||||||
|
model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
|
||||||
|
m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i]));
|
||||||
|
m_model.objects.back()->set_model(&m_model);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Reorder the objects, add new objects.
|
||||||
|
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
|
||||||
|
this->call_cancell_callback();
|
||||||
|
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||||
|
// Second create a new list of objects.
|
||||||
|
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
|
||||||
|
m_model.objects.clear();
|
||||||
|
m_model.objects.reserve(model.objects.size());
|
||||||
|
auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
|
||||||
|
std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower);
|
||||||
|
for (const ModelObject *mobj : model.objects) {
|
||||||
|
auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower);
|
||||||
|
if (it == model_objects_old.end() || (*it)->id() != mobj->id()) {
|
||||||
|
// New ModelObject added.
|
||||||
|
m_model.objects.emplace_back(ModelObject::new_copy(*mobj));
|
||||||
|
m_model.objects.back()->set_model(&m_model);
|
||||||
|
model_object_status.emplace(mobj->id(), ModelObjectStatus::New);
|
||||||
|
} else {
|
||||||
|
// Existing ModelObject re-added (possibly moved in the list).
|
||||||
|
m_model.objects.emplace_back(*it);
|
||||||
|
model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool deleted_any = false;
|
||||||
|
for (ModelObject *&model_object : model_objects_old) {
|
||||||
|
if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) {
|
||||||
|
model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted);
|
||||||
|
deleted_any = true;
|
||||||
|
} else
|
||||||
|
// Do not delete this ModelObject instance.
|
||||||
|
model_object = nullptr;
|
||||||
|
}
|
||||||
|
if (deleted_any) {
|
||||||
|
// Delete PrintObjects of the deleted ModelObjects.
|
||||||
|
std::vector<SLAPrintObject*> print_objects_old = std::move(m_objects);
|
||||||
|
m_objects.clear();
|
||||||
|
m_objects.reserve(print_objects_old.size());
|
||||||
|
for (SLAPrintObject *print_object : print_objects_old) {
|
||||||
|
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
|
||||||
|
assert(it_status != model_object_status.end());
|
||||||
|
if (it_status->status == ModelObjectStatus::Deleted) {
|
||||||
|
update_apply_status(print_object->invalidate_all_steps());
|
||||||
|
delete print_object;
|
||||||
|
} else
|
||||||
|
m_objects.emplace_back(print_object);
|
||||||
|
}
|
||||||
|
for (ModelObject *model_object : model_objects_old)
|
||||||
|
delete model_object;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return APPLY_STATUS_INVALIDATED;
|
// 2) Map print objects including their transformation matrices.
|
||||||
|
struct PrintObjectStatus {
|
||||||
|
enum Status {
|
||||||
|
Unknown,
|
||||||
|
Deleted,
|
||||||
|
Reused,
|
||||||
|
New
|
||||||
|
};
|
||||||
|
PrintObjectStatus(SLAPrintObject *print_object, Status status = Unknown) :
|
||||||
|
id(print_object->model_object()->id()),
|
||||||
|
print_object(print_object),
|
||||||
|
trafo(print_object->trafo()),
|
||||||
|
status(status) {}
|
||||||
|
PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
|
||||||
|
// ID of the ModelObject & PrintObject
|
||||||
|
ModelID id;
|
||||||
|
// Pointer to the old PrintObject
|
||||||
|
SLAPrintObject *print_object;
|
||||||
|
// Trafo generated with model_object->world_matrix(true)
|
||||||
|
Transform3d trafo;
|
||||||
|
Status status;
|
||||||
|
// Search by id.
|
||||||
|
bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; }
|
||||||
|
};
|
||||||
|
std::multiset<PrintObjectStatus> print_object_status;
|
||||||
|
for (SLAPrintObject *print_object : m_objects)
|
||||||
|
print_object_status.emplace(PrintObjectStatus(print_object));
|
||||||
|
|
||||||
|
// 3) Synchronize ModelObjects & PrintObjects.
|
||||||
|
std::vector<SLAPrintObject*> print_objects_new;
|
||||||
|
print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
|
||||||
|
bool new_objects = false;
|
||||||
|
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
|
||||||
|
ModelObject &model_object = *m_model.objects[idx_model_object];
|
||||||
|
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
|
||||||
|
assert(it_status != model_object_status.end());
|
||||||
|
assert(it_status->status != ModelObjectStatus::Deleted);
|
||||||
|
if (it_status->status == ModelObjectStatus::New)
|
||||||
|
// PrintObject instances will be added in the next loop.
|
||||||
|
continue;
|
||||||
|
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
|
||||||
|
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
|
||||||
|
const ModelObject &model_object_new = *model.objects[idx_model_object];
|
||||||
|
auto it_print_object_status = print_object_status.lower_bound(PrintObjectStatus(model_object.id()));
|
||||||
|
if (it_print_object_status != print_object_status.end() && it_print_object_status->id != model_object.id())
|
||||||
|
it_print_object_status = print_object_status.end();
|
||||||
|
// Check whether a model part volume was added or removed, their transformations or order changed.
|
||||||
|
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::MODEL_PART);
|
||||||
|
bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() ||
|
||||||
|
(! model_object.instances.empty() && ! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)));
|
||||||
|
if (model_parts_differ || sla_trafo_differs) {
|
||||||
|
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
||||||
|
if (it_print_object_status != print_object_status.end()) {
|
||||||
|
update_apply_status(it_print_object_status->print_object->invalidate_all_steps());
|
||||||
|
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Deleted;
|
||||||
|
}
|
||||||
|
// Copy content of the ModelObject including its ID, do not change the parent.
|
||||||
|
model_object.assign_copy(model_object_new);
|
||||||
|
} else {
|
||||||
|
// Synchronize Object's config.
|
||||||
|
bool object_config_changed = model_object.config != model_object_new.config;
|
||||||
|
if (object_config_changed)
|
||||||
|
model_object.config = model_object_new.config;
|
||||||
|
if (! object_diff.empty() || object_config_changed) {
|
||||||
|
SLAPrintObjectConfig new_config = m_default_object_config;
|
||||||
|
normalize_and_apply_config(new_config, model_object.config);
|
||||||
|
if (it_print_object_status != print_object_status.end()) {
|
||||||
|
t_config_option_keys diff = it_print_object_status->print_object->config().diff(new_config);
|
||||||
|
if (! diff.empty()) {
|
||||||
|
update_apply_status(it_print_object_status->print_object->invalidate_state_by_config_options(diff));
|
||||||
|
it_print_object_status->print_object->config_apply_only(new_config, diff, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (model_object.sla_support_points != model_object_new.sla_support_points) {
|
||||||
|
model_object.sla_support_points = model_object_new.sla_support_points;
|
||||||
|
if (it_print_object_status != print_object_status.end())
|
||||||
|
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
||||||
|
}
|
||||||
|
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
|
||||||
|
model_object.name = model_object_new.name;
|
||||||
|
model_object.input_file = model_object_new.input_file;
|
||||||
|
model_object.clear_instances();
|
||||||
|
model_object.instances.reserve(model_object_new.instances.size());
|
||||||
|
for (const ModelInstance *model_instance : model_object_new.instances) {
|
||||||
|
model_object.instances.emplace_back(new ModelInstance(*model_instance));
|
||||||
|
model_object.instances.back()->set_model_object(&model_object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SLAPrintObject::Instance> new_instances = sla_instances(model_object);
|
||||||
|
if (it_print_object_status != print_object_status.end() && it_print_object_status->status != PrintObjectStatus::Deleted) {
|
||||||
|
// The SLAPrintObject is already there.
|
||||||
|
if (new_instances != it_print_object_status->print_object->instances()) {
|
||||||
|
// Instances changed.
|
||||||
|
it_print_object_status->print_object->set_instances(new_instances);
|
||||||
|
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||||
|
}
|
||||||
|
print_objects_new.emplace_back(it_print_object_status->print_object);
|
||||||
|
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Reused;
|
||||||
|
} else {
|
||||||
|
auto print_object = new SLAPrintObject(this, &model_object);
|
||||||
|
print_object->set_trafo(sla_trafo(model_object));
|
||||||
|
print_object->set_instances(new_instances);
|
||||||
|
print_object->config_apply(config, true);
|
||||||
|
print_objects_new.emplace_back(print_object);
|
||||||
|
new_objects = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_objects != print_objects_new) {
|
||||||
|
this->call_cancell_callback();
|
||||||
|
update_apply_status(this->invalidate_all_steps());
|
||||||
|
m_objects = print_objects_new;
|
||||||
|
// Delete the PrintObjects marked as Unknown or Deleted.
|
||||||
|
bool deleted_objects = false;
|
||||||
|
for (auto &pos : print_object_status)
|
||||||
|
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
|
||||||
|
// update_apply_status(pos.print_object->invalidate_all_steps());
|
||||||
|
delete pos.print_object;
|
||||||
|
deleted_objects = true;
|
||||||
|
}
|
||||||
|
update_apply_status(new_objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
check_model_ids_equal(m_model, model);
|
||||||
|
#endif /* _DEBUG */
|
||||||
|
|
||||||
|
return static_cast<ApplyStatus>(apply_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SLAPrint::process()
|
void SLAPrint::process()
|
||||||
@ -498,6 +741,61 @@ void SLAPrint::process()
|
|||||||
set_status(100, L("Slicing done"));
|
set_status(100, L("Slicing done"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
|
||||||
|
{
|
||||||
|
if (opt_keys.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Cache the plenty of parameters, which influence the final rasterization only,
|
||||||
|
// or they are only notes not influencing the rasterization step.
|
||||||
|
static std::unordered_set<std::string> steps_rasterize = {
|
||||||
|
"exposure_time",
|
||||||
|
"initial_exposure_time",
|
||||||
|
"material_correction_printing",
|
||||||
|
"material_correction_curing",
|
||||||
|
"display_width",
|
||||||
|
"display_height",
|
||||||
|
"display_pixels_x",
|
||||||
|
"display_pixels_y",
|
||||||
|
"printer_correction"
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unordered_set<std::string> steps_ignore = {
|
||||||
|
"bed_shape",
|
||||||
|
"max_print_height",
|
||||||
|
"printer_technology",
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<SLAPrintStep> steps;
|
||||||
|
std::vector<SLAPrintObjectStep> osteps;
|
||||||
|
bool invalidated = false;
|
||||||
|
|
||||||
|
for (const t_config_option_key &opt_key : opt_keys) {
|
||||||
|
if (steps_rasterize.find(opt_key) != steps_rasterize.end()) {
|
||||||
|
// These options only affect the final rasterization, or they are just notes without influence on the output,
|
||||||
|
// so there is nothing to invalidate.
|
||||||
|
steps.emplace_back(slapsRasterize);
|
||||||
|
} else if (steps_ignore.find(opt_key) != steps_ignore.end()) {
|
||||||
|
// These steps have no influence on the output. Just ignore them.
|
||||||
|
} else if (opt_key == "initial_layer_height") {
|
||||||
|
steps.emplace_back(slapsRasterize);
|
||||||
|
osteps.emplace_back(slaposObjectSlice);
|
||||||
|
} else {
|
||||||
|
// All values should be covered.
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort_remove_duplicates(steps);
|
||||||
|
for (SLAPrintStep step : steps)
|
||||||
|
invalidated |= this->invalidate_step(step);
|
||||||
|
sort_remove_duplicates(osteps);
|
||||||
|
for (SLAPrintObjectStep ostep : osteps)
|
||||||
|
for (SLAPrintObject *object : m_objects)
|
||||||
|
invalidated |= object->invalidate_step(ostep);
|
||||||
|
return invalidated;
|
||||||
|
}
|
||||||
|
|
||||||
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
||||||
Inherited(print, model_object),
|
Inherited(print, model_object),
|
||||||
m_stepmask(slaposCount, true)
|
m_stepmask(slaposCount, true)
|
||||||
@ -506,6 +804,75 @@ SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
|||||||
|
|
||||||
SLAPrintObject::~SLAPrintObject() {}
|
SLAPrintObject::~SLAPrintObject() {}
|
||||||
|
|
||||||
|
// Called by SLAPrint::apply_config().
|
||||||
|
// This method only accepts SLAPrintObjectConfig option keys.
|
||||||
|
bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
|
||||||
|
{
|
||||||
|
if (opt_keys.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<SLAPrintObjectStep> steps;
|
||||||
|
bool invalidated = false;
|
||||||
|
for (const t_config_option_key &opt_key : opt_keys) {
|
||||||
|
if ( opt_key == "support_head_front_radius"
|
||||||
|
|| opt_key == "support_head_penetration"
|
||||||
|
|| opt_key == "support_head_back_radius"
|
||||||
|
|| opt_key == "support_head_width"
|
||||||
|
|| opt_key == "support_pillar_radius"
|
||||||
|
|| opt_key == "support_base_radius"
|
||||||
|
|| opt_key == "support_base_height"
|
||||||
|
|| opt_key == "support_critical_angle"
|
||||||
|
|| opt_key == "support_max_bridge_length"
|
||||||
|
|| opt_key == "support_object_elevation") {
|
||||||
|
steps.emplace_back(slaposSupportTree);
|
||||||
|
} else if (
|
||||||
|
opt_key == "pad_enable"
|
||||||
|
|| opt_key == "pad_wall_thickness"
|
||||||
|
|| opt_key == "pad_wall_height"
|
||||||
|
|| opt_key == "pad_max_merge_distance"
|
||||||
|
|| opt_key == "pad_edge_radius") {
|
||||||
|
steps.emplace_back(slaposBasePool);
|
||||||
|
} else {
|
||||||
|
// All keys should be covered.
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort_remove_duplicates(steps);
|
||||||
|
for (SLAPrintObjectStep step : steps)
|
||||||
|
invalidated |= this->invalidate_step(step);
|
||||||
|
return invalidated;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step)
|
||||||
|
{
|
||||||
|
bool invalidated = Inherited::invalidate_step(step);
|
||||||
|
// propagate to dependent steps
|
||||||
|
if (step == slaposObjectSlice) {
|
||||||
|
invalidated |= this->invalidate_all_steps();
|
||||||
|
} else if (step == slaposSupportIslands) {
|
||||||
|
invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposBasePool, slaposSliceSupports });
|
||||||
|
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||||
|
} else if (step == slaposSupportPoints) {
|
||||||
|
invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports });
|
||||||
|
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||||
|
} else if (step == slaposSupportTree) {
|
||||||
|
invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports });
|
||||||
|
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||||
|
} else if (step == slaposBasePool) {
|
||||||
|
invalidated |= this->invalidate_step(slaposSliceSupports);
|
||||||
|
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||||
|
} else if (step == slaposSliceSupports) {
|
||||||
|
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||||
|
}
|
||||||
|
return invalidated;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SLAPrintObject::invalidate_all_steps()
|
||||||
|
{
|
||||||
|
return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
|
||||||
|
}
|
||||||
|
|
||||||
double SLAPrintObject::get_elevation() const {
|
double SLAPrintObject::get_elevation() const {
|
||||||
double ret = m_config.support_object_elevation.getFloat();
|
double ret = m_config.support_object_elevation.getFloat();
|
||||||
|
|
||||||
|
@ -35,16 +35,18 @@ private: // Prevents erroneous use by other classes.
|
|||||||
using Inherited = _SLAPrintObjectBase;
|
using Inherited = _SLAPrintObjectBase;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const Transform3d& trafo() const { return m_trafo; }
|
const SLAPrintObjectConfig& config() const { return m_config; }
|
||||||
|
const Transform3d& trafo() const { return m_trafo; }
|
||||||
|
|
||||||
struct Instance {
|
struct Instance {
|
||||||
Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
|
Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
|
||||||
|
bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; }
|
||||||
// ID of the corresponding ModelInstance.
|
// ID of the corresponding ModelInstance.
|
||||||
ModelID instance_id;
|
ModelID instance_id;
|
||||||
// Slic3r::Point objects in scaled G-code coordinates
|
// Slic3r::Point objects in scaled G-code coordinates
|
||||||
Point shift;
|
Point shift;
|
||||||
// Rotation along the Z axis, in radians.
|
// Rotation along the Z axis, in radians.
|
||||||
float rotation;
|
float rotation;
|
||||||
};
|
};
|
||||||
const std::vector<Instance>& instances() const { return m_instances; }
|
const std::vector<Instance>& instances() const { return m_instances; }
|
||||||
|
|
||||||
@ -88,12 +90,14 @@ protected:
|
|||||||
{ this->m_config.apply_only(other, keys, ignore_nonexistent); }
|
{ this->m_config.apply_only(other, keys, ignore_nonexistent); }
|
||||||
void set_trafo(const Transform3d& trafo) { m_trafo = trafo; m_trmesh_valid = false; }
|
void set_trafo(const Transform3d& trafo) { m_trafo = trafo; m_trmesh_valid = false; }
|
||||||
|
|
||||||
bool set_instances(const std::vector<Instance> &instances);
|
void set_instances(const std::vector<Instance> &instances) { m_instances = instances; }
|
||||||
// Invalidates the step, and its depending steps in SLAPrintObject and SLAPrint.
|
// Invalidates the step, and its depending steps in SLAPrintObject and SLAPrint.
|
||||||
bool invalidate_step(SLAPrintObjectStep step);
|
bool invalidate_step(SLAPrintObjectStep step);
|
||||||
|
bool invalidate_all_steps();
|
||||||
|
// Invalidate steps based on a set of parameters changed.
|
||||||
|
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Object specific configuration, pulled from the configuration layer.
|
// Object specific configuration, pulled from the configuration layer.
|
||||||
SLAPrintObjectConfig m_config;
|
SLAPrintObjectConfig m_config;
|
||||||
// Translation in Z + Rotation by Y and Z + Scaling / Mirroring.
|
// Translation in Z + Rotation by Y and Z + Scaling / Mirroring.
|
||||||
@ -150,8 +154,13 @@ private:
|
|||||||
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
|
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
|
||||||
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
|
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
|
||||||
|
|
||||||
|
// Invalidate steps based on a set of parameters changed.
|
||||||
|
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||||
|
|
||||||
SLAPrinterConfig m_printer_config;
|
SLAPrinterConfig m_printer_config;
|
||||||
SLAMaterialConfig m_material_config;
|
SLAMaterialConfig m_material_config;
|
||||||
|
SLAPrintObjectConfig m_default_object_config;
|
||||||
|
|
||||||
PrintObjects m_objects;
|
PrintObjects m_objects;
|
||||||
std::vector<bool> m_stepmask;
|
std::vector<bool> m_stepmask;
|
||||||
SLAPrinterPtr m_printer;
|
SLAPrinterPtr m_printer;
|
||||||
|
@ -822,16 +822,13 @@ void GLVolumeCollection::load_object_auxiliary(
|
|||||||
bool use_VBOs)
|
bool use_VBOs)
|
||||||
{
|
{
|
||||||
assert(print_object->is_step_done(milestone));
|
assert(print_object->is_step_done(milestone));
|
||||||
|
Transform3d mesh_trafo_inv = print_object->trafo().inverse();
|
||||||
// Get the support mesh.
|
// Get the support mesh.
|
||||||
TriangleMesh mesh;
|
TriangleMesh mesh = print_object->get_mesh(milestone);
|
||||||
switch (milestone) {
|
mesh.transform(mesh_trafo_inv);
|
||||||
case slaposSupportTree: mesh = print_object->support_mesh(); break;
|
|
||||||
case slaposBasePool: mesh = print_object->pad_mesh(); break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
// Convex hull is required for out of print bed detection.
|
// Convex hull is required for out of print bed detection.
|
||||||
TriangleMesh convex_hull = mesh.convex_hull_3d();
|
TriangleMesh convex_hull = mesh.convex_hull_3d();
|
||||||
|
convex_hull.transform(mesh_trafo_inv);
|
||||||
for (const std::pair<size_t, size_t> &instance_idx : instances) {
|
for (const std::pair<size_t, size_t> &instance_idx : instances) {
|
||||||
const ModelInstance &model_instance = *print_object->model_object()->instances[instance_idx.first];
|
const ModelInstance &model_instance = *print_object->model_object()->instances[instance_idx.first];
|
||||||
const SLAPrintObject::Instance &print_instance = print_object->instances()[instance_idx.second];
|
const SLAPrintObject::Instance &print_instance = print_object->instances()[instance_idx.second];
|
||||||
@ -851,7 +848,6 @@ void GLVolumeCollection::load_object_auxiliary(
|
|||||||
v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true);
|
v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true);
|
||||||
v.is_modifier = false;
|
v.is_modifier = false;
|
||||||
v.shader_outside_printer_detection_enabled = true;
|
v.shader_outside_printer_detection_enabled = true;
|
||||||
//FIXME adjust with print_instance?
|
|
||||||
v.set_instance_transformation(model_instance.get_transformation());
|
v.set_instance_transformation(model_instance.get_transformation());
|
||||||
// Leave the volume transformation at identity.
|
// Leave the volume transformation at identity.
|
||||||
// v.set_volume_transformation(model_volume->get_transformation());
|
// v.set_volume_transformation(model_volume->get_transformation());
|
||||||
|
Loading…
Reference in New Issue
Block a user