diff options
Diffstat (limited to 'src/slic3r/GUI/GLCanvas3D.cpp')
-rw-r--r-- | src/slic3r/GUI/GLCanvas3D.cpp | 1263 |
1 files changed, 707 insertions, 556 deletions
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 48e87f66e..9a63986c5 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2,6 +2,7 @@ #include "GLCanvas3D.hpp" #include "admesh/stl.h" +#include "polypartition.h" #include "libslic3r/libslic3r.h" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/PrintConfig.hpp" @@ -72,11 +73,8 @@ static const float DEFAULT_BG_DARK_COLOR[3] = { 0.478f, 0.478f, 0.478f }; static const float DEFAULT_BG_LIGHT_COLOR[3] = { 0.753f, 0.753f, 0.753f }; static const float ERROR_BG_DARK_COLOR[3] = { 0.478f, 0.192f, 0.039f }; static const float ERROR_BG_LIGHT_COLOR[3] = { 0.753f, 0.192f, 0.039f }; - -#if ENABLE_SIDEBAR_VISUAL_HINTS static const float UNIFORM_SCALE_COLOR[3] = { 1.0f, 0.38f, 0.0f }; static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; -#endif // ENABLE_SIDEBAR_VISUAL_HINTS namespace Slic3r { namespace GUI { @@ -299,9 +297,16 @@ std::string GLCanvas3D::Camera::get_type_as_string() const }; } -void GLCanvas3D::Camera::set_theta(float theta) +void GLCanvas3D::Camera::set_theta(float theta, bool apply_limit) { - m_theta = clamp(0.0f, GIMBALL_LOCK_THETA_MAX, theta); + if (apply_limit) + m_theta = clamp(0.0f, GIMBALL_LOCK_THETA_MAX, theta); + else + { + m_theta = fmod(theta, 360.0f); + if (m_theta < 0.0f) + m_theta += 360.0f; + } } void GLCanvas3D::Camera::set_target(const Vec3d& target, GLCanvas3D& canvas) @@ -513,14 +518,13 @@ GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const { if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research")) { - if (boost::contains(curr->name, "MK2")) + if (boost::contains(curr->name, "MK3") || boost::contains(curr->name, "MK2.5")) { - type = MK2; + type = MK3; break; - } - else if (boost::contains(curr->name, "MK3")) + } else if (boost::contains(curr->name, "MK2")) { - type = MK3; + type = MK2; break; } } @@ -545,6 +549,11 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const std::string model_path = resources_dir() + "/models/" + key; #endif // ENABLE_PRINT_BED_MODELS +#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + GLfloat max_anisotropy = 0.0f; + ::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy); +#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + std::string filename = tex_path + "_top.png"; if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) { @@ -553,6 +562,14 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const _render_custom(); return; } +#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + if (max_anisotropy > 0.0f) + { + ::glBindTexture(GL_TEXTURE_2D, m_top_texture.get_id()); + ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); + ::glBindTexture(GL_TEXTURE_2D, 0); + } +#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES } filename = tex_path + "_bottom.png"; @@ -563,14 +580,28 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const _render_custom(); return; } +#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + if (max_anisotropy > 0.0f) + { + ::glBindTexture(GL_TEXTURE_2D, m_bottom_texture.get_id()); + ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); + ::glBindTexture(GL_TEXTURE_2D, 0); + } +#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES } #if ENABLE_PRINT_BED_MODELS if (theta <= 90.0f) { filename = model_path + "_bed.stl"; - if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs)) - m_model.center_around(m_bounding_box.center() - Vec3d(0.0, 0.0, 1.0 + 0.5 * m_model.get_bounding_box().size()(2))); + if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs)) { + Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.1 + 0.5 * m_model.get_bounding_box().size()(2)); + if (key == "mk2") + offset.y() += 15. / 2.; + else if (key == "mk3") + offset += Vec3d(0., (19. - 8.) / 2., 2.); + m_model.center_around(offset); + } if (!m_model.get_filename().empty()) { @@ -819,12 +850,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(nullptr) + , 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 +870,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 +890,32 @@ bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, return true; } +void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config) +{ + m_config = config; + delete m_slicing_parameters; + m_slicing_parameters = nullptr; + m_layers_texture.valid = false; +} + +void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id) +{ + const ModelObject *model_object_new = (object_id >= 0) ? model.objects[object_id] : nullptr; + if (model_object_new == nullptr || this->last_object_id != object_id || m_model_object != model_object_new || m_model_object->id() != model_object_new->id()) { + m_layer_height_profile.clear(); + m_layer_height_profile_modified = false; + delete m_slicing_parameters; + m_slicing_parameters = nullptr; + m_layers_texture.valid = false; + } + this->last_object_id = object_id; + m_model_object = model_object_new; + m_object_max_z = (m_model_object == nullptr) ? 0.f : m_model_object->bounding_box().max.z(); +} + 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 +933,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 +950,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 +959,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,30 +1071,24 @@ 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; ::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, 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()); // Render the color bar float l = bar_rect.get_left(); @@ -1055,78 +1097,190 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas float b = bar_rect.get_bottom(); ::glBegin(GL_QUADS); + ::glNormal3f(0.0f, 0.0f, 1.0f); ::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. - - // Get a maximum layer height value. - // 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; - 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]); - double lh_max = (layer_heights_max[i] == 0.0) ? (0.75 * nozzle_diameters[i]) : layer_heights_max[i]; - layer_height_max = std::min(layer_height_max, std::max(lh_min, lh_max)); - } + //FIXME show some kind of legend. // 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; - 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 x = l + (float)layer_height * scale_x; + assert(m_slicing_parameters != nullptr); + float scale_x = bar_rect.get_width() / (float)(1.12 * m_slicing_parameters->max_layer_height); + float scale_y = bar_rect.get_height() / m_object_max_z; + float x = bar_rect.get_left() + (float)m_slicing_parameters->layer_height * scale_x; // Baseline ::glColor3f(0.0f, 0.0f, 0.0f); ::glBegin(GL_LINE_STRIP); - ::glVertex2f(x, b); - ::glVertex2f(x, t); + ::glVertex2f(x, bar_rect.get_bottom()); + ::glVertex2f(x, bar_rect.get_top()); ::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 < m_layer_height_profile.size(); i += 2) + ::glVertex2f(bar_rect.get_left() + (float)m_layer_height_profile[i + 1] * scale_x, bar_rect.get_bottom() + (float)m_layer_height_profile[i] * scale_y); + ::glEnd(); +} - ::glColor3f(0.0f, 0.0f, 1.0f); - ::glBegin(GL_LINE_STRIP); - for (unsigned int i = 0; i < profile.size(); i += 2) - { - ::glVertex2f(l + (float)profile[i + 1] * scale_x, b + (float)profile[i] * scale_y); +void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const GLVolumeCollection &volumes) const +{ + assert(this->is_allowed()); + assert(this->last_object_id != -1); + GLint shader_id = m_shader.get_shader()->shader_program_id; + assert(shader_id > 0); + + GLint current_program_id; + glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id); + if (shader_id > 0 && shader_id != current_program_id) + // The layer editing shader is not yet active. Activate it. + glUseProgram(shader_id); + else + // The layer editing shader was already active. + current_program_id = -1; + + GLint z_to_texture_row_id = glGetUniformLocation(shader_id, "z_to_texture_row"); + GLint z_texture_row_to_normalized_id = glGetUniformLocation(shader_id, "z_texture_row_to_normalized"); + GLint z_cursor_id = glGetUniformLocation(shader_id, "z_cursor"); + GLint z_cursor_band_width_id = glGetUniformLocation(shader_id, "z_cursor_band_width"); + GLint world_matrix_id = glGetUniformLocation(shader_id, "volume_world_matrix"); + + if (z_to_texture_row_id != -1 && z_texture_row_to_normalized_id != -1 && z_cursor_id != -1 && z_cursor_band_width_id != -1 && world_matrix_id != -1) + { + const_cast<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() +{ + this->update_slicing_parameters(); + 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::reset_layer_height_profile(GLCanvas3D& canvas) +{ + const_cast<ModelObject*>(m_model_object)->layer_height_profile.clear(); + m_layer_height_profile.clear(); + m_layers_texture.valid = false; + canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +} + +void GLCanvas3D::LayersEditing::generate_layer_height_texture() +{ + this->update_slicing_parameters(); + // Always try to update the layer height profile. + bool update = ! m_layers_texture.valid; + 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)); } - ::glEnd(); + } + m_layer_height_profile_modified = false; +} + +void GLCanvas3D::LayersEditing::update_slicing_parameters() +{ + if (m_slicing_parameters == nullptr) { + m_slicing_parameters = new SlicingParameters(); + *m_slicing_parameters = PrintObject::slicing_parameters(*m_config, *m_model_object); } } const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); const Vec3d GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX); +#if ENABLE_MOVE_MIN_THRESHOLD +const int GLCanvas3D::Mouse::Drag::MoveThresholdPx = 5; +#endif // ENABLE_MOVE_MIN_THRESHOLD GLCanvas3D::Mouse::Drag::Drag() : start_position_2D(Invalid_2D_Point) , start_position_3D(Invalid_3D_Point) , move_volume_idx(-1) +#if ENABLE_MOVE_MIN_THRESHOLD + , move_requires_threshold(false) + , move_start_threshold_position_2D(Invalid_2D_Point) +#endif // ENABLE_MOVE_MIN_THRESHOLD { } @@ -1139,26 +1293,6 @@ GLCanvas3D::Mouse::Mouse() { } -void GLCanvas3D::Mouse::set_start_position_2D_as_invalid() -{ - drag.start_position_2D = Drag::Invalid_2D_Point; -} - -void GLCanvas3D::Mouse::set_start_position_3D_as_invalid() -{ - drag.start_position_3D = Drag::Invalid_3D_Point; -} - -bool GLCanvas3D::Mouse::is_start_position_2D_defined() const -{ - return (drag.start_position_2D != Drag::Invalid_2D_Point); -} - -bool GLCanvas3D::Mouse::is_start_position_3D_defined() const -{ - return (drag.start_position_3D != Drag::Invalid_3D_Point); -} - GLCanvas3D::Selection::VolumeCache::TransformCache::TransformCache() : position(Vec3d::Zero()) , rotation(Vec3d::Zero()) @@ -1217,7 +1351,6 @@ void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes) _update_valid(); } -#if ENABLE_SIDEBAR_VISUAL_HINTS bool GLCanvas3D::Selection::init(bool useVBOs) { if (!m_arrow.init(useVBOs)) @@ -1231,7 +1364,6 @@ bool GLCanvas3D::Selection::init(bool useVBOs) m_curved_arrow.set_scale(5.0 * Vec3d::Ones()); return true; } -#endif // ENABLE_SIDEBAR_VISUAL_HINTS void GLCanvas3D::Selection::set_model(Model* model) { @@ -1605,7 +1737,6 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) for (unsigned int i : m_list) { if (is_single_full_instance()) -#if ENABLE_WORLD_ROTATIONS { if (local) (*m_volumes)[i]->set_instance_rotation(rotation); @@ -1616,25 +1747,17 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) (*m_volumes)[i]->set_instance_rotation(new_rotation); } } -#else - (*m_volumes)[i]->set_instance_rotation(rotation); -#endif // ENABLE_WORLD_ROTATIONS else if (is_single_volume() || is_single_modifier()) -#if ENABLE_WORLD_ROTATIONS { - if (requires_local_axes()) + if (local) (*m_volumes)[i]->set_volume_rotation(rotation); else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - const Transform3d& inst_m = m_cache.volumes_data[i].get_instance_rotation_matrix(); - Vec3d new_rotation = Geometry::extract_euler_angles(inst_m.inverse() * m * inst_m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix()); (*m_volumes)[i]->set_volume_rotation(new_rotation); } } -#else - (*m_volumes)[i]->set_volume_rotation(rotation); -#endif // ENABLE_WORLD_ROTATIONS else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); @@ -1682,11 +1805,14 @@ void GLCanvas3D::Selection::flattening_rotate(const Vec3d& normal) for (unsigned int i : m_list) { - Transform3d wst = m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_volume_scale_matrix(); + Transform3d wst = m_cache.volumes_data[i].get_instance_scale_matrix(); Vec3d scaling_factor = Vec3d(1./wst(0,0), 1./wst(1,1), 1./wst(2,2)); - Vec3d rotation = Geometry::extract_euler_angles(m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_volume_rotation_matrix()); - Vec3d transformed_normal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor) * normal; + Transform3d wmt = m_cache.volumes_data[i].get_instance_mirror_matrix(); + Vec3d mirror(wmt(0,0), wmt(1,1), wmt(2,2)); + + Vec3d rotation = Geometry::extract_euler_angles(m_cache.volumes_data[i].get_instance_rotation_matrix()); + Vec3d transformed_normal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor, mirror) * normal; transformed_normal.normalize(); Vec3d axis = transformed_normal(2) > 0.999f ? Vec3d(1., 0., 0.) : Vec3d(transformed_normal.cross(Vec3d(0., 0., -1.))); @@ -1700,8 +1826,10 @@ void GLCanvas3D::Selection::flattening_rotate(const Vec3d& normal) } #if !DISABLE_INSTANCES_SYNCH + // we want to synchronize z-rotation as well, otherwise the flattening behaves funny + // when applied on one of several identical instances if (m_mode == Instance) - _synchronize_unselected_instances(); + _synchronize_unselected_instances(true); #endif // !DISABLE_INSTANCES_SYNCH m_bounding_box_dirty = true; @@ -1753,9 +1881,7 @@ void GLCanvas3D::Selection::scale(const Vec3d& scale, bool local) _synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH -#if ENABLE_ENSURE_ON_BED_WHILE_SCALING _ensure_on_bed(); -#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING m_bounding_box_dirty = true; } @@ -1892,8 +2018,6 @@ void GLCanvas3D::Selection::erase() } wxGetApp().obj_list()->delete_from_model_and_list(items); } - else if (is_single_full_instance()) - wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itInstance, get_object_idx(), get_instance_idx()); else if (is_multiple_full_instance()) { std::set<std::pair<int, int>> instances_idxs; @@ -1911,9 +2035,10 @@ void GLCanvas3D::Selection::erase() { items.emplace_back(ItemType::itInstance, i.first, i.second); } - wxGetApp().obj_list()->delete_from_model_and_list(items); } + else if (is_single_full_instance()) + wxGetApp().obj_list()->delete_from_model_and_list(ItemType::itInstance, get_object_idx(), get_instance_idx()); else if (is_mixed()) { std::set<ItemForDelete> items_set; @@ -2022,7 +2147,6 @@ void GLCanvas3D::Selection::render_center() const } #endif // ENABLE_RENDER_SELECTION_CENTER -#if ENABLE_SIDEBAR_VISUAL_HINTS void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_field) const { if (sidebar_field.empty()) @@ -2038,14 +2162,18 @@ void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_fiel const Vec3d& center = get_bounding_box().center(); if (is_single_full_instance()) + { ::glTranslated(center(0), center(1), center(2)); + if (!boost::starts_with(sidebar_field, "position")) + { + Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + ::glMultMatrixd(orient_matrix.data()); + } + } else if (is_single_volume() || is_single_modifier()) { - const GLVolume* volume = (*m_volumes)[*m_list.begin()]; - Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true); - const Vec3d& offset = get_bounding_box().center(); - - ::glTranslated(offset(0), offset(1), offset(2)); + Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + ::glTranslated(center(0), center(1), center(2)); ::glMultMatrixd(orient_matrix.data()); } else @@ -2053,8 +2181,7 @@ void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_fiel ::glTranslated(center(0), center(1), center(2)); if (requires_local_axes()) { - const GLVolume* volume = (*m_volumes)[*m_list.begin()]; - Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true); + Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); ::glMultMatrixd(orient_matrix.data()); } } @@ -2072,7 +2199,6 @@ void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_fiel ::glDisable(GL_LIGHTING); } -#endif // ENABLE_SIDEBAR_VISUAL_HINTS bool GLCanvas3D::Selection::requires_local_axes() const { @@ -2315,7 +2441,7 @@ void GLCanvas3D::Selection::_update_type() void GLCanvas3D::Selection::_set_caches() { m_cache.volumes_data.clear(); - for (unsigned int i : m_list) + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { const GLVolume* v = (*m_volumes)[i]; m_cache.volumes_data.emplace(i, VolumeCache(v->get_volume_transformation(), v->get_instance_transformation())); @@ -2477,7 +2603,6 @@ void GLCanvas3D::Selection::_render_bounding_box(const BoundingBoxf3& box, float ::glEnd(); } -#if ENABLE_SIDEBAR_VISUAL_HINTS void GLCanvas3D::Selection::_render_sidebar_position_hints(const std::string& sidebar_field) const { if (boost::ends_with(sidebar_field, "x")) @@ -2512,7 +2637,9 @@ void GLCanvas3D::Selection::_render_sidebar_rotation_hints(const std::string& si void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const { - if (boost::ends_with(sidebar_field, "x") || requires_uniform_scale()) + bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); + + if (boost::ends_with(sidebar_field, "x") || uniform_scale) { ::glPushMatrix(); ::glRotated(-90.0, 0.0, 0.0, 1.0); @@ -2520,14 +2647,14 @@ void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sideb ::glPopMatrix(); } - if (boost::ends_with(sidebar_field, "y") || requires_uniform_scale()) + if (boost::ends_with(sidebar_field, "y") || uniform_scale) { ::glPushMatrix(); _render_sidebar_scale_hint(Y); ::glPopMatrix(); } - if (boost::ends_with(sidebar_field, "z") || requires_uniform_scale()) + if (boost::ends_with(sidebar_field, "z") || uniform_scale) { ::glPushMatrix(); ::glRotated(90.0, 1.0, 0.0, 0.0); @@ -2558,7 +2685,7 @@ void GLCanvas3D::Selection::_render_sidebar_rotation_hint(Axis axis) const void GLCanvas3D::Selection::_render_sidebar_scale_hint(Axis axis) const { - m_arrow.set_color((requires_uniform_scale() ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); + m_arrow.set_color(((requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling()) ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); ::glTranslated(0.0, 5.0, 0.0); m_arrow.render(); @@ -2571,9 +2698,8 @@ void GLCanvas3D::Selection::_render_sidebar_scale_hint(Axis axis) const void GLCanvas3D::Selection::_render_sidebar_size_hint(Axis axis, double length) const { } -#endif // ENABLE_SIDEBAR_VISUAL_HINTS -void GLCanvas3D::Selection::_synchronize_unselected_instances() +void GLCanvas3D::Selection::_synchronize_unselected_instances(bool including_z) { std::set<unsigned int> done; // prevent processing volumes twice done.insert(m_list.begin(), m_list.end()); @@ -2606,7 +2732,20 @@ void GLCanvas3D::Selection::_synchronize_unselected_instances() if ((v->object_idx() != object_idx) || (v->instance_idx() == instance_idx)) continue; - v->set_instance_rotation(Vec3d(rotation(0), rotation(1), v->get_instance_rotation()(2))); + auto is_approx = [](double value, double test_value) -> bool { return std::abs(value - test_value) < EPSILON; }; + + double z; + if (including_z) + // rotation comes from place on face -> force given z + z = rotation(2); + else if (is_approx(rotation(0), m_cache.volumes_data[j].get_instance_rotation()(0)) && is_approx(rotation(1), m_cache.volumes_data[j].get_instance_rotation()(1))) + // z only rotation -> keep instance z + z = v->get_instance_rotation()(2); + else + // generic rotation -> update instance z + z = m_cache.volumes_data[j].get_instance_rotation()(2) + rotation(2); + + v->set_instance_rotation(Vec3d(rotation(0), rotation(1), z)); v->set_instance_scaling_factor(scaling_factor); v->set_instance_mirror(mirror); @@ -2648,7 +2787,6 @@ void GLCanvas3D::Selection::_synchronize_unselected_volumes() } } -#if ENABLE_ENSURE_ON_BED_WHILE_SCALING void GLCanvas3D::Selection::_ensure_on_bed() { typedef std::map<std::pair<int, int>, double> InstancesToZMap; @@ -2676,7 +2814,6 @@ void GLCanvas3D::Selection::_ensure_on_bed() volume->set_instance_offset(Z, volume->get_instance_offset(Z) - it->second); } } -#endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING const float GLCanvas3D::Gizmos::OverlayIconsScale = 1.0f; const float GLCanvas3D::Gizmos::OverlayBorder = 5.0f; @@ -2806,13 +2943,13 @@ std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, con float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale; + bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size); + if (inside) + name = it->second->get_name(); + if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) - { - bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size); it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); - if (inside) - name = it->second->get_name(); - } + top_y += (icon_size + OverlayGapY); } @@ -2949,14 +3086,14 @@ bool GLCanvas3D::Gizmos::grabber_contains_mouse() const return (curr != nullptr) ? (curr->get_hover_id() != -1) : false; } -void GLCanvas3D::Gizmos::update(const Linef3& mouse_ray, bool shift_down, const Point* mouse_pos) +void GLCanvas3D::Gizmos::update(const Linef3& mouse_ray, const Selection& selection, bool shift_down, const Point* mouse_pos) { if (!m_enabled) return; GLGizmoBase* curr = _get_current(); if (curr != nullptr) - curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos, shift_down)); + curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos, shift_down), selection); } GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const @@ -2975,7 +3112,7 @@ bool GLCanvas3D::Gizmos::is_running() const bool GLCanvas3D::Gizmos::handle_shortcut(int key, const Selection& selection) { - if (!m_enabled) + if (!m_enabled || selection.is_empty()) return false; bool handled = false; @@ -3695,7 +3832,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); @@ -3724,7 +3860,11 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_dirty(true) , m_initialized(false) , m_use_VBOs(false) +#if ENABLE_REWORKED_BED_SHAPE_CHANGE + , m_requires_zoom_to_bed(false) +#else , m_force_zoom_to_bed_enabled(false) +#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE , m_apply_zoom_to_volumes_filter(false) , m_hover_volume_id(-1) , m_toolbar_action_running(false) @@ -3743,12 +3883,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) #endif // not ENABLE_IMGUI { if (m_canvas != nullptr) - { -#if !ENABLE_USE_UNIQUE_GLCONTEXT - m_context = new wxGLContext(m_canvas); -#endif // !ENABLE_USE_UNIQUE_GLCONTEXT m_timer.SetOwner(m_canvas); - } m_selection.set_volumes(&m_volumes.volumes); } @@ -3756,14 +3891,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) GLCanvas3D::~GLCanvas3D() { reset_volumes(); - -#if !ENABLE_USE_UNIQUE_GLCONTEXT - if (m_context != nullptr) - { - delete m_context; - m_context = nullptr; - } -#endif // !ENABLE_USE_UNIQUE_GLCONTEXT } void GLCanvas3D::post_event(wxEvent &&event) @@ -3859,10 +3986,8 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (!_init_toolbar()) return false; -#if ENABLE_SIDEBAR_VISUAL_HINTS if (!m_selection.init(m_use_VBOs)) return false; -#endif // ENABLE_SIDEBAR_VISUAL_HINTS post_event(SimpleEvent(EVT_GLCANVAS_INIT)); @@ -3871,16 +3996,6 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) return true; } -#if !ENABLE_USE_UNIQUE_GLCONTEXT -bool GLCanvas3D::set_current() -{ - if ((m_canvas != nullptr) && (m_context != nullptr)) - return m_canvas->SetCurrent(*m_context); - - return false; -} -#endif // !ENABLE_USE_UNIQUE_GLCONTEXT - void GLCanvas3D::set_as_dirty() { m_dirty = true; @@ -3895,12 +4010,6 @@ void GLCanvas3D::reset_volumes() { if (!m_volumes.empty()) { -#if !ENABLE_USE_UNIQUE_GLCONTEXT - // ensures this canvas is current - if (!set_current()) - return; -#endif // !ENABLE_USE_UNIQUE_GLCONTEXT - m_selection.clear(); m_volumes.release_geometry(); m_volumes.clear(); @@ -3918,9 +4027,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) @@ -3943,7 +4053,11 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) set_bed_axes_length(0.1 * m_bed.get_bounding_box().max_size()); if (new_shape) +#if ENABLE_REWORKED_BED_SHAPE_CHANGE + m_requires_zoom_to_bed = true; +#else zoom_to_bed(); +#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE m_dirty = true; } @@ -3978,6 +4092,12 @@ BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); bb.merge(m_bed.get_bounding_box()); + if (m_config != nullptr) + { + double h = m_config->opt_float("max_print_height"); + bb.min(2) = std::min(bb.min(2), -h); + bb.max(2) = std::max(bb.max(2), h); + } return bb; } @@ -4032,10 +4152,12 @@ void GLCanvas3D::enable_toolbar(bool enable) m_toolbar.set_enabled(enable); } +#if !ENABLE_REWORKED_BED_SHAPE_CHANGE void GLCanvas3D::enable_force_zoom_to_bed(bool enable) { m_force_zoom_to_bed_enabled = enable; } +#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE void GLCanvas3D::enable_dynamic_background(bool enable) { @@ -4100,7 +4222,7 @@ void GLCanvas3D::select_view(const std::string& direction) if (dir_vec != nullptr) { m_camera.phi = dir_vec[0]; - m_camera.set_theta(dir_vec[1]); + m_camera.set_theta(dir_vec[1], false); viewport_changed(); @@ -4112,10 +4234,13 @@ void GLCanvas3D::select_view(const std::string& direction) void GLCanvas3D::set_viewport_from_scene(const GLCanvas3D& other) { m_camera.phi = other.m_camera.phi; - m_camera.set_theta(other.m_camera.get_theta()); + m_camera.set_theta(other.m_camera.get_theta(), false); m_camera.set_scene_box(other.m_camera.get_scene_box(), *this); m_camera.set_target(other.m_camera.get_target(), *this); m_camera.zoom = other.m_camera.zoom; +#if ENABLE_REWORKED_BED_SHAPE_CHANGE + m_requires_zoom_to_bed = false; +#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE m_dirty = true; } @@ -4161,15 +4286,21 @@ void GLCanvas3D::render() return; // ensures this canvas is current and initialized -#if ENABLE_USE_UNIQUE_GLCONTEXT if (!_set_current() || !_3DScene::init(m_canvas)) -#else - if (!set_current() || !_3DScene::init(m_canvas)) -#endif // ENABLE_USE_UNIQUE_GLCONTEXT return; +#if ENABLE_REWORKED_BED_SHAPE_CHANGE + if (m_requires_zoom_to_bed) + { + zoom_to_bed(); + const Size& cnv_size = get_canvas_size(); + _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); + m_requires_zoom_to_bed = false; + } +#else if (m_force_zoom_to_bed_enabled) _force_zoom_to_bed(); +#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE _camera_tranform(); @@ -4179,6 +4310,10 @@ void GLCanvas3D::render() ::glLightfv(GL_LIGHT0, GL_POSITION, position_top); float theta = m_camera.get_theta(); + if (theta > 180.f) + // absolute value of the rotation + theta = 360.f - theta; + bool is_custom_bed = m_bed.is_custom(); #if ENABLE_IMGUI @@ -4192,13 +4327,9 @@ void GLCanvas3D::render() ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); _render_background(); -#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING // textured bed needs to be rendered after objects if the texture is transparent bool early_bed_render = is_custom_bed || (theta <= 90.0f); if (early_bed_render) -#else - if (is_custom_bed) // untextured bed needs to be rendered before objects -#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING _render_bed(theta); _render_objects(); @@ -4207,11 +4338,7 @@ void GLCanvas3D::render() _render_axes(); -#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING if (!early_bed_render) -#else - if (!is_custom_bed) // textured bed needs to be rendered after objects -#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING _render_bed(theta); #if ENABLE_RENDER_SELECTION_CENTER @@ -4223,9 +4350,7 @@ void GLCanvas3D::render() // this position is used later into on_mouse() to drag the objects m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast<int>()); -#if ENABLE_SIDEBAR_VISUAL_HINTS _render_selection_sidebar_hints(); -#endif // ENABLE_SIDEBAR_VISUAL_HINTS _render_current_gizmo(); #if ENABLE_SHOW_CAMERA_TARGET @@ -4239,7 +4364,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(); @@ -4338,11 +4464,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re { if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) return; -#if !ENABLE_USE_UNIQUE_GLCONTEXT - // ensures this canvas is current - if (!set_current()) - return; -#endif // !ENABLE_USE_UNIQUE_GLCONTEXT struct ModelVolumeState { ModelVolumeState(const GLVolume *volume) : @@ -4479,8 +4600,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) { @@ -4490,33 +4609,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. @@ -4524,11 +4617,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; - } } } } @@ -4661,6 +4749,19 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_camera.set_scene_box(scene_bounding_box(), *this); m_camera.set_target(m_camera.get_target(), *this); + if (m_selection.is_empty()) + { + // If no object is selected, deactivate the active gizmo, if any + // Otherwise it may be shown after cleaning the scene (if it was active while the objects were deleted) + m_gizmos.reset_all_states(); + + // If no object is selected, reset the objects manipulator on the sidebar + // to force a reset of its cache + auto manip = wxGetApp().obj_manipul(); + if (manip != nullptr) + manip->update_settings_value(m_selection); + } + // and force this canvas to be redrawn. m_dirty = true; } @@ -4670,12 +4771,6 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const const Print *print = this->fff_print(); if ((m_canvas != nullptr) && (print != nullptr)) { -#if !ENABLE_USE_UNIQUE_GLCONTEXT - // ensures that this canvas is current - if (!set_current()) - return; -#endif // !ENABLE_USE_UNIQUE_GLCONTEXT - std::vector<float> tool_colors = _parse_colors(str_tool_colors); if (m_volumes.empty()) @@ -4909,10 +5004,8 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) void GLCanvas3D::on_timer(wxTimerEvent& evt) { - if (m_layers_editing.state != LayersEditing::Editing) - return; - - _perform_layer_editing_action(); + if (m_layers_editing.state == LayersEditing::Editing) + _perform_layer_editing_action(); } void GLCanvas3D::on_mouse(wxMouseEvent& evt) @@ -4931,15 +5024,24 @@ 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; +#if ENABLE_MOVE_MIN_THRESHOLD + if (m_mouse.drag.move_requires_threshold && m_mouse.is_move_start_threshold_position_2D_defined() && m_mouse.is_move_threshold_met(pos)) + { + m_mouse.drag.move_requires_threshold = false; + m_mouse.set_move_start_threshold_position_2D_as_invalid(); + } +#endif // ENABLE_MOVE_MIN_THRESHOLD + if (evt.Entering()) { -#if defined(__WXMSW__) || defined(__linux__) - // On Windows and Linux needs focus in order to catch key events +//#if defined(__WXMSW__) || defined(__linux__) +// // On Windows and Linux needs focus in order to catch key events + // Set focus in order to remove it from sidebar fields if (m_canvas != nullptr) { // Only set focus, if the top level window of this canvas is active. auto p = dynamic_cast<wxWindow*>(evt.GetEventObject()); @@ -4951,8 +5053,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } m_mouse.set_start_position_2D_as_invalid(); - m_mouse.set_start_position_3D_as_invalid(); -#endif +//#endif } else if (evt.Leaving()) { @@ -4963,6 +5064,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.LeftDClick() && (toolbar_contains_mouse != -1)) { m_toolbar_action_running = true; + m_mouse.set_start_position_3D_as_invalid(); m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this); } else if (evt.LeftDClick() && (m_gizmos.get_current_type() != Gizmos::Undefined)) @@ -4987,10 +5089,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_layers_editing.reset_layer_height_profile(*this); // Index 2 means no editing, just wait for mouse up event. m_layers_editing.state = LayersEditing::Completed; @@ -5040,6 +5140,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (toolbar_contains_mouse != -1) { m_toolbar_action_running = true; + m_mouse.set_start_position_3D_as_invalid(); m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this); m_mouse.left_down = false; } @@ -5066,6 +5167,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { bool add_as_single = !already_selected && !shift_down; m_selection.add(m_hover_volume_id, add_as_single); +#if ENABLE_MOVE_MIN_THRESHOLD + m_mouse.drag.move_requires_threshold = !already_selected; + if (already_selected) + m_mouse.set_move_start_threshold_position_2D_as_invalid(); + else + m_mouse.drag.move_start_threshold_position_2D = pos; +#endif // ENABLE_MOVE_MIN_THRESHOLD } #if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION @@ -5116,7 +5224,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_volumes.volumes[m_hover_volume_id]->hover && !m_volumes.volumes[m_hover_volume_id]->is_wipe_tower) { // forces the selection of the volume - m_selection.add(m_hover_volume_id); + if (!m_selection.is_multiple_full_instance()) + m_selection.add(m_hover_volume_id); m_gizmos.update_on_off_state(m_selection); post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); _update_gizmos_data(); @@ -5132,19 +5241,26 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1)) { - m_mouse.dragging = true; +#if ENABLE_MOVE_MIN_THRESHOLD + if (!m_mouse.drag.move_requires_threshold) + { +#endif // ENABLE_MOVE_MIN_THRESHOLD + m_mouse.dragging = true; - // Get new position at the same Z of the initial click point. - float z0 = 0.0f; - float z1 = 1.0f; - // we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag - Vec3d cur_pos = m_selection.contains_volume(m_hover_volume_id) ? Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D(2)) : m_mouse.drag.start_position_3D; + // Get new position at the same Z of the initial click point. + float z0 = 0.0f; + float z1 = 1.0f; + // we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag + Vec3d cur_pos = m_selection.contains_volume(m_hover_volume_id) ? Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D(2)) : m_mouse.drag.start_position_3D; - m_regenerate_volumes = false; - m_selection.translate(cur_pos - m_mouse.drag.start_position_3D); - wxGetApp().obj_manipul()->update_settings_value(m_selection); + m_regenerate_volumes = false; + m_selection.translate(cur_pos - m_mouse.drag.start_position_3D); + wxGetApp().obj_manipul()->update_settings_value(m_selection); - m_dirty = true; + m_dirty = true; +#if ENABLE_MOVE_MIN_THRESHOLD + } +#endif // ENABLE_MOVE_MIN_THRESHOLD } else if (evt.Dragging() && m_gizmos.is_dragging()) { @@ -5152,7 +5268,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_canvas->CaptureMouse(); m_mouse.dragging = true; - m_gizmos.update(mouse_ray(pos), evt.ShiftDown(), &pos); + m_gizmos.update(mouse_ray(pos), m_selection, evt.ShiftDown(), &pos); switch (m_gizmos.get_current_type()) { @@ -5195,11 +5311,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.LeftIsDown()) { // if dragging over blank area with left button, rotate +#if ENABLE_MOVE_MIN_THRESHOLD + if ((m_hover_volume_id == -1) && m_mouse.is_start_position_3D_defined()) +#else if (m_mouse.is_start_position_3D_defined()) +#endif // ENABLE_MOVE_MIN_THRESHOLD { const Vec3d& orig = m_mouse.drag.start_position_3D; m_camera.phi += (((float)pos(0) - (float)orig(0)) * TRACKBALLSIZE); - m_camera.set_theta(m_camera.get_theta() - ((float)pos(1) - (float)orig(1)) * TRACKBALLSIZE); + m_camera.set_theta(m_camera.get_theta() - ((float)pos(1) - (float)orig(1)) * TRACKBALLSIZE, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); viewport_changed(); @@ -5232,9 +5352,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) { @@ -5261,8 +5379,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_selection.clear(); m_selection.set_mode(Selection::Instance); wxGetApp().obj_manipul()->update_settings_value(m_selection); - post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + m_gizmos.reset_all_states(); _update_gizmos_data(); + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); } m_mouse.ignore_up_event = false; } @@ -5294,9 +5413,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) break; } m_gizmos.stop_dragging(); -#if ENABLE_WORLD_ROTATIONS _update_gizmos_data(); -#endif // ENABLE_WORLD_ROTATIONS wxGetApp().obj_manipul()->update_settings_value(m_selection); // Let the platter know that the dragging finished, so a delayed refresh @@ -5324,9 +5441,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) std::string tooltip = ""; // updates gizmos overlay - if (!m_selection.is_empty()) - tooltip = m_gizmos.update_hover_state(*this, m_mouse.position, m_selection); - else + tooltip = m_gizmos.update_hover_state(*this, m_mouse.position, m_selection); + if (m_selection.is_empty()) m_gizmos.reset_all_states(); // updates toolbar overlay @@ -5396,11 +5512,6 @@ Point GLCanvas3D::get_local_mouse_position() const void GLCanvas3D::reset_legend_texture() { -#if !ENABLE_USE_UNIQUE_GLCONTEXT - if (!set_current()) - return; -#endif // !ENABLE_USE_UNIQUE_GLCONTEXT - m_legend_texture.reset(); } @@ -5411,7 +5522,9 @@ void GLCanvas3D::set_tooltip(const std::string& tooltip) const wxToolTip* t = m_canvas->GetToolTip(); if (t != nullptr) { - if (t->GetTip() != tooltip) + if (tooltip.empty()) + m_canvas->UnsetToolTip(); + else t->SetTip(tooltip); } else @@ -5667,7 +5780,7 @@ void GLCanvas3D::set_camera_zoom(float zoom) // Don't allow to zoom too far outside the scene. float zoom_min = _get_zoom_to_bounding_box_factor(_max_bounding_box()); if (zoom_min > 0.0f) - zoom = std::max(zoom, zoom_min * 0.8f); + zoom = std::max(zoom, zoom_min * 0.7f); m_camera.zoom = zoom; viewport_changed(); @@ -5677,6 +5790,7 @@ void GLCanvas3D::set_camera_zoom(float zoom) void GLCanvas3D::update_gizmos_on_off_state() { set_as_dirty(); + _update_gizmos_data(); m_gizmos.update_on_off_state(get_selection()); } @@ -5696,11 +5810,13 @@ bool GLCanvas3D::_is_shown_on_screen() const return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; } +#if !ENABLE_REWORKED_BED_SHAPE_CHANGE void GLCanvas3D::_force_zoom_to_bed() { zoom_to_bed(); m_force_zoom_to_bed_enabled = false; } +#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE bool GLCanvas3D::_init_toolbar() { @@ -5742,7 +5858,7 @@ bool GLCanvas3D::_init_toolbar() GLToolbarItem::Data item; item.name = "add"; - item.tooltip = GUI::L_str("Add..."); + item.tooltip = GUI::L_str("Add... [Ctrl+I]"); item.sprite_id = 0; item.is_toggable = false; item.action_event = EVT_GLTOOLBAR_ADD; @@ -5750,7 +5866,7 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "delete"; - item.tooltip = GUI::L_str("Delete"); + item.tooltip = GUI::L_str("Delete [Del]"); item.sprite_id = 1; item.is_toggable = false; item.action_event = EVT_GLTOOLBAR_DELETE; @@ -5758,7 +5874,7 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "deleteall"; - item.tooltip = GUI::L_str("Delete all"); + item.tooltip = GUI::L_str("Delete all [Ctrl+Del]"); item.sprite_id = 2; item.is_toggable = false; item.action_event = EVT_GLTOOLBAR_DELETE_ALL; @@ -5766,7 +5882,7 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "arrange"; - item.tooltip = GUI::L_str("Arrange"); + item.tooltip = GUI::L_str("Arrange [A]"); item.sprite_id = 3; item.is_toggable = false; item.action_event = EVT_GLTOOLBAR_ARRANGE; @@ -5777,7 +5893,7 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "more"; - item.tooltip = GUI::L_str("Add instance"); + item.tooltip = GUI::L_str("Add instance [+]"); item.sprite_id = 4; item.is_toggable = false; item.action_event = EVT_GLTOOLBAR_MORE; @@ -5785,7 +5901,7 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "fewer"; - item.tooltip = GUI::L_str("Remove instance"); + item.tooltip = GUI::L_str("Remove instance [-]"); item.sprite_id = 5; item.is_toggable = false; item.action_event = EVT_GLTOOLBAR_FEWER; @@ -5827,7 +5943,6 @@ bool GLCanvas3D::_init_toolbar() return true; } -#if ENABLE_USE_UNIQUE_GLCONTEXT bool GLCanvas3D::_set_current() { if ((m_canvas != nullptr) && (m_context != nullptr)) @@ -5835,7 +5950,6 @@ bool GLCanvas3D::_set_current() return false; } -#endif ENABLE_USE_UNIQUE_GLCONTEXT void GLCanvas3D::_resize(unsigned int w, unsigned int h) { @@ -5847,11 +5961,7 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) #endif // ENABLE_IMGUI // ensures that this canvas is current -#if ENABLE_USE_UNIQUE_GLCONTEXT _set_current(); -#else - set_current(); -#endif // ENABLE_USE_UNIQUE_GLCONTEXT ::glViewport(0, 0, w, h); ::glMatrixMode(GL_PROJECTION); @@ -5932,7 +6042,11 @@ void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) viewport_changed(); +#if ENABLE_REWORKED_BED_SHAPE_CHANGE + m_dirty = true; +#else _refresh_if_shown_on_screen(); +#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE } } @@ -6003,28 +6117,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()) @@ -6034,11 +6126,15 @@ void GLCanvas3D::_refresh_if_shown_on_screen() // Because of performance problems on macOS, where PaintEvents are not delivered // frequently enough, we call render() here directly when we can. +#if ENABLE_REWORKED_BED_SHAPE_CHANGE + render(); +#else // We can't do that when m_force_zoom_to_bed_enabled == true, because then render() // ends up calling back here via _force_zoom_to_bed(), causing a stack overflow. if (m_canvas != nullptr) { m_force_zoom_to_bed_enabled ? m_canvas->Refresh() : render(); } +#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE } } @@ -6166,7 +6262,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) { @@ -6174,10 +6271,6 @@ void GLCanvas3D::_render_objects() const m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); m_volumes.check_outside_state(m_config, nullptr); } -#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING - // do not cull backfaces to show broken geometry, if any - ::glDisable(GL_CULL_FACE); -#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING } if (m_use_clipping_planes) @@ -6186,19 +6279,20 @@ void GLCanvas3D::_render_objects() const m_volumes.set_z_range(-FLT_MAX, FLT_MAX); m_shader.start_using(); -#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING - // do not cull backfaces to show broken geometry, if any - m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled); + if (m_picking_enabled && m_layers_editing.is_enabled() && m_layers_editing.last_object_id != -1) { + int object_id = m_layers_editing.last_object_id; + m_volumes.render_VBOs(GLVolumeCollection::Opaque, false, [object_id](const GLVolume &volume) { + // Which volume to paint without the layer height profile shader? + return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); + }); + // Let LayersEditing handle rendering of the active object using the layer height profile shader. + m_layers_editing.render_volumes(*this, this->m_volumes); + } else { + // do not cull backfaces to show broken geometry, if any + m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled); + } m_volumes.render_VBOs(GLVolumeCollection::Transparent, false); -#else - m_volumes.render_VBOs(); -#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING m_shader.stop_using(); - -#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING - if (m_picking_enabled) - ::glEnable(GL_CULL_FACE); -#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING } else { @@ -6210,24 +6304,9 @@ void GLCanvas3D::_render_objects() const ::glEnable(GL_CLIP_PLANE1); } -#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING - // do not cull backfaces to show broken geometry, if any - if (m_picking_enabled) - ::glDisable(GL_CULL_FACE); -#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING - -#if ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING // do not cull backfaces to show broken geometry, if any m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled); m_volumes.render_legacy(GLVolumeCollection::Transparent, false); -#else - m_volumes.render_legacy(); -#endif // ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING - -#if !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING - if (m_picking_enabled) - ::glEnable(GL_CULL_FACE); -#endif // !ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING if (m_use_clipping_planes) { @@ -6269,39 +6348,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; @@ -6398,6 +6444,230 @@ void GLCanvas3D::_render_camera_target() const } #endif // ENABLE_SHOW_CAMERA_TARGET +class TessWrapper { +public: + static Pointf3s tesselate(const ExPolygon &expoly, double z_, bool flipped_) + { + z = z_; + flipped = flipped_; + triangles.clear(); + intersection_points.clear(); + std::vector<GLdouble> coords; + { + size_t num_coords = expoly.contour.points.size(); + for (const Polygon &poly : expoly.holes) + num_coords += poly.points.size(); + coords.reserve(num_coords * 3); + } + GLUtesselator *tess = gluNewTess(); // create a tessellator + // register callback functions +#ifndef _GLUfuncptr + #ifdef _MSC_VER + typedef void (__stdcall *_GLUfuncptr)(void); + #else /* _MSC_VER */ + #ifdef GLAPIENTRYP + typedef void (GLAPIENTRYP _GLUfuncptr)(void); + #else /* GLAPIENTRYP */ + typedef void (*_GLUfuncptr)(void); + #endif + #endif /* _MSC_VER */ +#endif /* _GLUfuncptr */ + gluTessCallback(tess, GLU_TESS_BEGIN, (_GLUfuncptr)tessBeginCB); + gluTessCallback(tess, GLU_TESS_END, (_GLUfuncptr)tessEndCB); + gluTessCallback(tess, GLU_TESS_ERROR, (_GLUfuncptr)tessErrorCB); + gluTessCallback(tess, GLU_TESS_VERTEX, (_GLUfuncptr)tessVertexCB); + gluTessCallback(tess, GLU_TESS_COMBINE, (_GLUfuncptr)tessCombineCB); + gluTessBeginPolygon(tess, 0); // with NULL data + gluTessBeginContour(tess); + for (const Point &pt : expoly.contour.points) { + coords.emplace_back(unscale<double>(pt[0])); + coords.emplace_back(unscale<double>(pt[1])); + coords.emplace_back(0.); + gluTessVertex(tess, &coords[coords.size() - 3], &coords[coords.size() - 3]); + } + gluTessEndContour(tess); + for (const Polygon &poly : expoly.holes) { + gluTessBeginContour(tess); + for (const Point &pt : poly.points) { + coords.emplace_back(unscale<double>(pt[0])); + coords.emplace_back(unscale<double>(pt[1])); + coords.emplace_back(0.); + gluTessVertex(tess, &coords[coords.size() - 3], &coords[coords.size() - 3]); + } + gluTessEndContour(tess); + } + gluTessEndPolygon(tess); + gluDeleteTess(tess); + return std::move(triangles); + } + +private: + static void tessBeginCB(GLenum which) + { + assert(which == GL_TRIANGLES || which == GL_TRIANGLE_FAN || which == GL_TRIANGLE_STRIP); + if (!(which == GL_TRIANGLES || which == GL_TRIANGLE_FAN || which == GL_TRIANGLE_STRIP)) + printf("Co je to za haluz!?\n"); + primitive_type = which; + num_points = 0; + } + + static void tessEndCB() + { + num_points = 0; + } + + static void tessVertexCB(const GLvoid *data) + { + if (data == nullptr) + return; + const GLdouble *ptr = (const GLdouble*)data; + ++ num_points; + if (num_points == 1) { + memcpy(pt0, ptr, sizeof(GLdouble) * 3); + } else if (num_points == 2) { + memcpy(pt1, ptr, sizeof(GLdouble) * 3); + } else { + bool flip = flipped; + if (primitive_type == GL_TRIANGLE_STRIP && num_points == 4) { + flip = !flip; + num_points = 2; + } + triangles.emplace_back(pt0[0], pt0[1], z); + if (flip) { + triangles.emplace_back(ptr[0], ptr[1], z); + triangles.emplace_back(pt1[0], pt1[1], z); + } else { + triangles.emplace_back(pt1[0], pt1[1], z); + triangles.emplace_back(ptr[0], ptr[1], z); + } + if (primitive_type == GL_TRIANGLE_STRIP) { + memcpy(pt0, pt1, sizeof(GLdouble) * 3); + memcpy(pt1, ptr, sizeof(GLdouble) * 3); + } else if (primitive_type == GL_TRIANGLE_FAN) { + memcpy(pt1, ptr, sizeof(GLdouble) * 3); + } else { + assert(primitive_type == GL_TRIANGLES); + assert(num_points == 3); + num_points = 0; + } + } + } + + static void tessCombineCB(const GLdouble newVertex[3], const GLdouble *neighborVertex[4], const GLfloat neighborWeight[4], GLdouble **outData) + { + intersection_points.emplace_back(newVertex[0], newVertex[1], newVertex[2]); + *outData = intersection_points.back().data(); + } + + static void tessErrorCB(GLenum errorCode) + { + const GLubyte *errorStr; + errorStr = gluErrorString(errorCode); + printf("Error: %s\n", (const char*)errorStr); + } + + static GLenum primitive_type; + static GLdouble pt0[3]; + static GLdouble pt1[3]; + static int num_points; + static Pointf3s triangles; + static std::deque<Vec3d> intersection_points; + static double z; + static bool flipped; +}; + +GLenum TessWrapper::primitive_type; +GLdouble TessWrapper::pt0[3]; +GLdouble TessWrapper::pt1[3]; +int TessWrapper::num_points; +Pointf3s TessWrapper::triangles; +std::deque<Vec3d> TessWrapper::intersection_points; +double TessWrapper::z; +bool TessWrapper::flipped; + +static Pointf3s triangulate_expolygons(const ExPolygons &polys, coordf_t z, bool flip) +{ + Pointf3s triangles; +#if 0 + for (const ExPolygon& poly : polys) { + Polygons poly_triangles; + // poly.triangulate() is based on a trapezoidal decomposition implemented in an extremely expensive way by clipping the whole input contour with a polygon! + poly.triangulate(&poly_triangles); + // poly.triangulate_p2t() is based on the poly2tri library, which is not quite stable, it often ends up in a nice stack overflow! + // poly.triangulate_p2t(&poly_triangles); + for (const Polygon &t : poly_triangles) + if (flip) { + triangles.emplace_back(to_3d(unscale(t.points[2]), z)); + triangles.emplace_back(to_3d(unscale(t.points[1]), z)); + triangles.emplace_back(to_3d(unscale(t.points[0]), z)); + } else { + triangles.emplace_back(to_3d(unscale(t.points[0]), z)); + triangles.emplace_back(to_3d(unscale(t.points[1]), z)); + triangles.emplace_back(to_3d(unscale(t.points[2]), z)); + } + } +#else + +// for (const ExPolygon &poly : union_ex(simplify_polygons(to_polygons(polys), true))) { + for (const ExPolygon &poly : polys) { + append(triangles, TessWrapper::tesselate(poly, z, flip)); + continue; + + std::list<TPPLPoly> input = expoly_to_polypartition_input(poly); + std::list<TPPLPoly> output; + // int res = TPPLPartition().Triangulate_MONO(&input, &output); + int res = TPPLPartition().Triangulate_EC(&input, &output); + if (res == 1) { + // Triangulation succeeded. Convert to triangles. + size_t num_triangles = 0; + for (const TPPLPoly &poly : output) + if (poly.GetNumPoints() >= 3) + num_triangles += (size_t)poly.GetNumPoints() - 2; + triangles.reserve(triangles.size() + num_triangles * 3); + for (const TPPLPoly &poly : output) { + long num_points = poly.GetNumPoints(); + if (num_points >= 3) { + const TPPLPoint *pt0 = &poly[0]; + const TPPLPoint *pt1 = nullptr; + const TPPLPoint *pt2 = &poly[1]; + for (long i = 2; i < num_points; ++i) { + pt1 = pt2; + pt2 = &poly[i]; + if (flip) { + triangles.emplace_back(unscale<double>(pt2->x), unscale<double>(pt2->y), z); + triangles.emplace_back(unscale<double>(pt1->x), unscale<double>(pt1->y), z); + triangles.emplace_back(unscale<double>(pt0->x), unscale<double>(pt0->y), z); + } else { + triangles.emplace_back(unscale<double>(pt0->x), unscale<double>(pt0->y), z); + triangles.emplace_back(unscale<double>(pt1->x), unscale<double>(pt1->y), z); + triangles.emplace_back(unscale<double>(pt2->x), unscale<double>(pt2->y), z); + } + } + } + } + } else { + // Triangulation by polypartition failed. Use the expensive slow implementation. + Polygons poly_triangles; + // poly.triangulate() is based on a trapezoidal decomposition implemented in an extremely expensive way by clipping the whole input contour with a polygon! + poly.triangulate(&poly_triangles); + // poly.triangulate_p2t() is based on the poly2tri library, which is not quite stable, it often ends up in a nice stack overflow! + // poly.triangulate_p2t(&poly_triangles); + for (const Polygon &t : poly_triangles) + if (flip) { + triangles.emplace_back(to_3d(unscale(t.points[2]), z)); + triangles.emplace_back(to_3d(unscale(t.points[1]), z)); + triangles.emplace_back(to_3d(unscale(t.points[0]), z)); + } else { + triangles.emplace_back(to_3d(unscale(t.points[0]), z)); + triangles.emplace_back(to_3d(unscale(t.points[1]), z)); + triangles.emplace_back(to_3d(unscale(t.points[2]), z)); + } + } + } +#endif + return triangles; +} + void GLCanvas3D::_render_sla_slices() const { if (!m_use_clipping_planes || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) @@ -6415,34 +6685,32 @@ void GLCanvas3D::_render_sla_slices() const { const SLAPrintObject* obj = print_objects[i]; - Pointf3s bottom_obj_triangles; - Pointf3s bottom_sup_triangles; - Pointf3s top_obj_triangles; - Pointf3s top_sup_triangles; - double shift_z = obj->get_current_elevation(); double min_z = clip_min_z - shift_z; double max_z = clip_max_z - shift_z; - if (m_sla_caps[0].matches(min_z)) + SlaCap::ObjectIdToTrianglesMap::iterator it_caps_bottom = m_sla_caps[0].triangles.find(i); + SlaCap::ObjectIdToTrianglesMap::iterator it_caps_top = m_sla_caps[1].triangles.find(i); { - SlaCap::ObjectIdToTrianglesMap::const_iterator it = m_sla_caps[0].triangles.find(i); - if (it != m_sla_caps[0].triangles.end()) - { - bottom_obj_triangles = it->second.object; - bottom_sup_triangles = it->second.suppports; + if (it_caps_bottom == m_sla_caps[0].triangles.end()) + it_caps_bottom = m_sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first; + if (! m_sla_caps[0].matches(min_z)) { + m_sla_caps[0].z = min_z; + it_caps_bottom->second.object.clear(); + it_caps_bottom->second.supports.clear(); } - } - - if (m_sla_caps[1].matches(max_z)) - { - SlaCap::ObjectIdToTrianglesMap::const_iterator it = m_sla_caps[1].triangles.find(i); - if (it != m_sla_caps[1].triangles.end()) - { - top_obj_triangles = it->second.object; - top_sup_triangles = it->second.suppports; + if (it_caps_top == m_sla_caps[1].triangles.end()) + it_caps_top = m_sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first; + if (! m_sla_caps[1].matches(max_z)) { + m_sla_caps[1].z = max_z; + it_caps_top->second.object.clear(); + it_caps_top->second.supports.clear(); } } + Pointf3s &bottom_obj_triangles = it_caps_bottom->second.object; + Pointf3s &bottom_sup_triangles = it_caps_bottom->second.supports; + Pointf3s &top_obj_triangles = it_caps_top->second.object; + Pointf3s &top_sup_triangles = it_caps_top->second.supports; const std::vector<SLAPrintObject::Instance>& instances = obj->instances(); struct InstanceTransform @@ -6468,86 +6736,22 @@ void GLCanvas3D::_render_sla_slices() const if (it_min_z != index.end()) { + // calculate model bottom cap if (bottom_obj_triangles.empty() && (it_min_z->second.model_slices_idx < model_slices.size())) - { - // calculate model bottom cap - const ExPolygons& polys = model_slices[it_min_z->second.model_slices_idx]; - for (const ExPolygon& poly : polys) - { - Polygons poly_triangles; - poly.triangulate(&poly_triangles); - for (const Polygon& t : poly_triangles) - { - for (int v = 2; v >= 0; --v) - { - bottom_obj_triangles.emplace_back(to_3d(unscale(t.points[v]), min_z)); - } - } - } - } - + bottom_obj_triangles = triangulate_expolygons(model_slices[it_min_z->second.model_slices_idx], min_z, true); + // calculate support bottom cap if (bottom_sup_triangles.empty() && (it_min_z->second.support_slices_idx < support_slices.size())) - { - // calculate support bottom cap - const ExPolygons& polys = support_slices[it_min_z->second.support_slices_idx]; - for (const ExPolygon& poly : polys) - { - Polygons poly_triangles; - poly.triangulate(&poly_triangles); - for (const Polygon& t : poly_triangles) - { - for (int v = 2; v >= 0; --v) - { - bottom_sup_triangles.emplace_back(to_3d(unscale(t.points[v]), min_z)); - } - } - } - - m_sla_caps[0].triangles.insert(SlaCap::ObjectIdToTrianglesMap::value_type(i, { bottom_obj_triangles, bottom_sup_triangles })); - m_sla_caps[0].z = min_z; - } + bottom_sup_triangles = triangulate_expolygons(support_slices[it_min_z->second.support_slices_idx], min_z, true); } if (it_max_z != index.end()) { + // calculate model top cap if (top_obj_triangles.empty() && (it_max_z->second.model_slices_idx < model_slices.size())) - { - // calculate model top cap - const ExPolygons& polys = model_slices[it_max_z->second.model_slices_idx]; - for (const ExPolygon& poly : polys) - { - Polygons poly_triangles; - poly.triangulate(&poly_triangles); - for (const Polygon& t : poly_triangles) - { - for (int v = 0; v < 3; ++v) - { - top_obj_triangles.emplace_back(to_3d(unscale(t.points[v]), max_z)); - } - } - } - } - + top_obj_triangles = triangulate_expolygons(model_slices[it_max_z->second.model_slices_idx], max_z, false); + // calculate support top cap if (top_sup_triangles.empty() && (it_max_z->second.support_slices_idx < support_slices.size())) - { - // calculate support top cap - const ExPolygons& polys = support_slices[it_max_z->second.support_slices_idx]; - for (const ExPolygon& poly : polys) - { - Polygons poly_triangles; - poly.triangulate(&poly_triangles); - for (const Polygon& t : poly_triangles) - { - for (int v = 0; v < 3; ++v) - { - top_sup_triangles.emplace_back(to_3d(unscale(t.points[v]), max_z)); - } - } - } - } - - m_sla_caps[1].triangles.insert(SlaCap::ObjectIdToTrianglesMap::value_type(i, { top_obj_triangles, top_sup_triangles })); - m_sla_caps[1].z = max_z; + top_sup_triangles = triangulate_expolygons(support_slices[it_max_z->second.support_slices_idx], max_z, false); } } @@ -6593,7 +6797,6 @@ void GLCanvas3D::_render_sla_slices() const } } -#if ENABLE_SIDEBAR_VISUAL_HINTS void GLCanvas3D::_render_selection_sidebar_hints() const { if (m_use_VBOs) @@ -6604,7 +6807,6 @@ void GLCanvas3D::_render_selection_sidebar_hints() const if (m_use_VBOs) m_shader.stop_using(); } -#endif // ENABLE_SIDEBAR_VISUAL_HINTS void GLCanvas3D::_update_volumes_hover_state() const { @@ -6659,11 +6861,7 @@ void GLCanvas3D::_update_gizmos_data() // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; m_gizmos.set_scale(volume->get_instance_scaling_factor()); -#if ENABLE_WORLD_ROTATIONS m_gizmos.set_rotation(Vec3d::Zero()); -#else - m_gizmos.set_rotation(volume->get_instance_rotation()); -#endif // ENABLE_WORLD_ROTATIONS ModelObject* model_object = m_model->objects[m_selection.get_object_idx()]; m_gizmos.set_flattening_data(model_object); #if ENABLE_SLA_SUPPORT_GIZMO_MOD @@ -6676,11 +6874,7 @@ void GLCanvas3D::_update_gizmos_data() { const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; m_gizmos.set_scale(volume->get_volume_scaling_factor()); -#if ENABLE_WORLD_ROTATIONS m_gizmos.set_rotation(Vec3d::Zero()); -#else - m_gizmos.set_rotation(volume->get_volume_rotation()); -#endif // ENABLE_WORLD_ROTATIONS m_gizmos.set_flattening_data(nullptr); #if ENABLE_SLA_SUPPORT_GIZMO_MOD m_gizmos.set_sla_support_data(nullptr, m_selection); @@ -6701,55 +6895,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); - } - - // 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_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); } - 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. @@ -6806,12 +6969,6 @@ void GLCanvas3D::_stop_timer() void GLCanvas3D::_load_print_toolpaths() { -#if !ENABLE_USE_UNIQUE_GLCONTEXT - // ensures this canvas is current - if (!set_current()) - return; -#endif // !ENABLE_USE_UNIQUE_GLCONTEXT - const Print *print = this->fff_print(); if (print == nullptr) return; @@ -6899,22 +7056,31 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } } ctxt; + ctxt.has_perimeters = print_object.is_step_done(posPerimeters); + ctxt.has_infill = print_object.is_step_done(posInfill); + ctxt.has_support = print_object.is_step_done(posSupportMaterial); + ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; + ctxt.shifted_copies = &print_object.copies(); // order layers by print_z - ctxt.layers.reserve(print_object.layers().size() + print_object.support_layers().size()); - for (const Layer *layer : print_object.layers()) - ctxt.layers.push_back(layer); - for (const Layer *layer : print_object.support_layers()) - ctxt.layers.push_back(layer); + { + size_t nlayers = 0; + if (ctxt.has_perimeters || ctxt.has_infill) + nlayers = print_object.layers().size(); + if (ctxt.has_support) + nlayers += print_object.support_layers().size(); + ctxt.layers.reserve(nlayers); + } + if (ctxt.has_perimeters || ctxt.has_infill) + for (const Layer *layer : print_object.layers()) + ctxt.layers.push_back(layer); + if (ctxt.has_support) + for (const Layer *layer : print_object.support_layers()) + ctxt.layers.push_back(layer); std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); // Maximum size of an allocation block: 32MB / sizeof(float) - ctxt.has_perimeters = print_object.is_step_done(posPerimeters); - ctxt.has_infill = print_object.is_step_done(posInfill); - ctxt.has_support = print_object.is_step_done(posSupportMaterial); - ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; //FIXME Improve the heuristics for a grain size. @@ -7948,31 +8114,16 @@ std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& col void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors) { -#if !ENABLE_USE_UNIQUE_GLCONTEXT - if (!set_current()) - return; -#endif // !ENABLE_USE_UNIQUE_GLCONTEXT - m_legend_texture.generate(preview_data, tool_colors, *this, m_dynamic_background_enabled && _is_any_volume_outside()); } void GLCanvas3D::_generate_warning_texture(const std::string& msg) { -#if !ENABLE_USE_UNIQUE_GLCONTEXT - if (!set_current()) - return; -#endif // !ENABLE_USE_UNIQUE_GLCONTEXT - m_warning_texture.generate(msg); } void GLCanvas3D::_reset_warning_texture() { -#if !ENABLE_USE_UNIQUE_GLCONTEXT - if (!set_current()) - return; -#endif // !ENABLE_USE_UNIQUE_GLCONTEXT - m_warning_texture.reset(); } |