diff options
author | bubnikv <bubnikv@gmail.com> | 2018-09-12 12:59:02 +0300 |
---|---|---|
committer | bubnikv <bubnikv@gmail.com> | 2018-09-12 12:59:02 +0300 |
commit | 0235f1a8211cfecf75ac0a585295bc1378747972 (patch) | |
tree | 9c1d06a43a691d9691c7eff6f21646b0dcc8cc74 /xs/src/slic3r/GUI/3DScene.cpp | |
parent | 41ce69f327a2fe3324dc47756b64a6fdb001cfa3 (diff) | |
parent | a97df55592f524eb73bd7d04ef8b7c1b3525d27d (diff) |
Merged with dev
Diffstat (limited to 'xs/src/slic3r/GUI/3DScene.cpp')
-rw-r--r-- | xs/src/slic3r/GUI/3DScene.cpp | 2506 |
1 files changed, 1052 insertions, 1454 deletions
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 356d27b96..a488bfcde 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2,13 +2,13 @@ #include "3DScene.hpp" -#include "../../libslic3r/libslic3r.h" #include "../../libslic3r/ExtrusionEntity.hpp" #include "../../libslic3r/ExtrusionEntityCollection.hpp" #include "../../libslic3r/Geometry.hpp" #include "../../libslic3r/GCode/PreviewData.hpp" #include "../../libslic3r/Print.hpp" #include "../../libslic3r/Slicing.hpp" +#include "../../slic3r/GUI/PresetBundle.hpp" #include "GCode/Analyzer.hpp" #include <stdio.h> @@ -22,10 +22,7 @@ #include <tbb/parallel_for.h> #include <tbb/spin_mutex.h> -#include <wx/bitmap.h> -#include <wx/dcmemory.h> -#include <wx/image.h> -#include <wx/settings.h> +#include <Eigen/Dense> #include "GUI.hpp" @@ -42,7 +39,7 @@ void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh) for (int i = 0; i < mesh.stl.stats.number_of_facets; ++ i) { const stl_facet &facet = mesh.stl.facet_start[i]; for (int j = 0; j < 3; ++ j) - this->push_geometry(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z, facet.normal.x, facet.normal.y, facet.normal.z); + this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); } } @@ -58,7 +55,7 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh) for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) { const stl_facet &facet = mesh.stl.facet_start[i]; for (int j = 0; j < 3; ++j) - this->push_geometry(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z, facet.normal.x, facet.normal.y, facet.normal.z); + this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2); vertices_count += 3; @@ -194,8 +191,40 @@ void GLIndexedVertexArray::render( const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; -const float GLVolume::OUTSIDE_COLOR[4] = { 0.75f, 0.0f, 0.75f, 1.0f }; -const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 1.0f, 0.0f, 1.0f, 1.0f }; +const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f }; +const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; + +GLVolume::GLVolume(float r, float g, float b, float a) + : m_offset(Vec3d::Zero()) + , m_rotation(0.0) + , m_scaling_factor(1.0) + , m_world_matrix(Transform3f::Identity()) + , m_world_matrix_dirty(true) + , m_transformed_bounding_box_dirty(true) + , m_transformed_convex_hull_bounding_box_dirty(true) + , m_convex_hull(nullptr) + , composite_id(-1) + , select_group_id(-1) + , drag_group_id(-1) + , extruder_id(0) + , selected(false) + , is_active(true) + , zoom_to_volumes(true) + , shader_outside_printer_detection_enabled(false) + , is_outside(false) + , hover(false) + , is_modifier(false) + , is_wipe_tower(false) + , is_extrusion_path(false) + , tverts_range(0, size_t(-1)) + , qverts_range(0, size_t(-1)) +{ + color[0] = r; + color[1] = g; + color[2] = b; + color[3] = a; + set_render_color(r, g, b, a); +} void GLVolume::set_render_color(float r, float g, float b, float a) { @@ -217,20 +246,120 @@ void GLVolume::set_render_color(const float* rgba, unsigned int size) void GLVolume::set_render_color() { if (selected) - { - if (is_outside) - set_render_color(SELECTED_OUTSIDE_COLOR, 4); - else - set_render_color(SELECTED_COLOR, 4); - } + set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); else if (hover) set_render_color(HOVER_COLOR, 4); - else if (is_outside) + else if (is_outside && shader_outside_printer_detection_enabled) set_render_color(OUTSIDE_COLOR, 4); else set_render_color(color, 4); } +double GLVolume::get_rotation() +{ + return m_rotation; +} + +void GLVolume::set_rotation(double rotation) +{ + if (m_rotation != rotation) + { + m_rotation = rotation; + m_world_matrix_dirty = true; + m_transformed_bounding_box_dirty = true; + m_transformed_convex_hull_bounding_box_dirty = true; + } +} + +const Vec3d& GLVolume::get_offset() const +{ + return m_offset; +} + +void GLVolume::set_offset(const Vec3d& offset) +{ + if (m_offset != offset) + { + m_offset = offset; + m_world_matrix_dirty = true; + m_transformed_bounding_box_dirty = true; + m_transformed_convex_hull_bounding_box_dirty = true; + } +} + +void GLVolume::set_scaling_factor(double factor) +{ + if (m_scaling_factor != factor) + { + m_scaling_factor = factor; + m_world_matrix_dirty = true; + m_transformed_bounding_box_dirty = true; + m_transformed_convex_hull_bounding_box_dirty = true; + } +} + +void GLVolume::set_convex_hull(const TriangleMesh& convex_hull) +{ + m_convex_hull = &convex_hull; +} + +void GLVolume::set_select_group_id(const std::string& select_by) +{ + if (select_by == "object") + select_group_id = object_idx() * 1000000; + else if (select_by == "volume") + select_group_id = object_idx() * 1000000 + volume_idx() * 1000; + else if (select_by == "instance") + select_group_id = composite_id; +} + +void GLVolume::set_drag_group_id(const std::string& drag_by) +{ + if (drag_by == "object") + drag_group_id = object_idx() * 1000; + else if (drag_by == "instance") + drag_group_id = object_idx() * 1000 + instance_idx(); +} + +const Transform3f& GLVolume::world_matrix() const +{ + if (m_world_matrix_dirty) + { + m_world_matrix = Transform3f::Identity(); + m_world_matrix.translate(m_offset.cast<float>()); + m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation, Vec3f::UnitZ())); + m_world_matrix.scale((float)m_scaling_factor); + m_world_matrix_dirty = false; + } + return m_world_matrix; +} + +const BoundingBoxf3& GLVolume::transformed_bounding_box() const +{ + if (m_transformed_bounding_box_dirty) + { + m_transformed_bounding_box = bounding_box.transformed(world_matrix().cast<double>()); + m_transformed_bounding_box_dirty = false; + } + + return m_transformed_bounding_box; +} + +const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const +{ + if (m_transformed_convex_hull_bounding_box_dirty) + { + if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0)) + m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix().cast<double>()); + else + m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix().cast<double>()); + + m_transformed_convex_hull_bounding_box_dirty = false; + } + + return m_transformed_convex_hull_bounding_box; +} + void GLVolume::set_range(double min_z, double max_z) { this->qverts_range.first = 0; @@ -271,14 +400,16 @@ void GLVolume::render() const if (!is_active) return; - glCullFace(GL_BACK); - glPushMatrix(); - glTranslated(this->origin.x, this->origin.y, this->origin.z); + ::glCullFace(GL_BACK); + ::glPushMatrix(); + ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); + ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); if (this->indexed_vertex_array.indexed()) this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); else this->indexed_vertex_array.render(); - glPopMatrix(); + ::glPopMatrix(); } void GLVolume::render_using_layer_height() const @@ -296,6 +427,7 @@ void GLVolume::render_using_layer_height() const GLint z_texture_row_to_normalized_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_texture_row_to_normalized") : -1; GLint z_cursor_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor") : -1; GLint z_cursor_band_width_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor_band_width") : -1; + GLint world_matrix_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "volume_world_matrix") : -1; if (z_to_texture_row_id >= 0) glUniform1f(z_to_texture_row_id, (GLfloat)layer_height_texture_z_to_row_id()); @@ -304,19 +436,25 @@ void GLVolume::render_using_layer_height() const glUniform1f(z_texture_row_to_normalized_id, (GLfloat)(1.0f / layer_height_texture_height())); if (z_cursor_id >= 0) - glUniform1f(z_cursor_id, (GLfloat)(bounding_box.max.z * layer_height_texture_data.z_cursor_relative)); + glUniform1f(z_cursor_id, (GLfloat)(layer_height_texture_data.print_object->model_object()->bounding_box().max(2) * layer_height_texture_data.z_cursor_relative)); if (z_cursor_band_width_id >= 0) glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width); - unsigned int w = layer_height_texture_width(); - unsigned int h = layer_height_texture_height(); + if (world_matrix_id >= 0) + ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); + + GLsizei w = (GLsizei)layer_height_texture_width(); + GLsizei h = (GLsizei)layer_height_texture_height(); + GLsizei half_w = w / 2; + GLsizei half_h = h / 2; + ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glBindTexture(GL_TEXTURE_2D, layer_height_texture_data.texture_id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, w / 2, h / 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level0()); - glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, w / 2, h / 2, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1()); + glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1()); render(); @@ -326,15 +464,142 @@ void GLVolume::render_using_layer_height() const glUseProgram(current_program_id); } -void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool force) +void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const { - GLTexture *tex = this->layer_height_texture.get(); - if (tex == nullptr) + if (!is_active) + return; + + if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id) + return; + + if (layer_height_texture_data.can_use()) + { + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); + render_using_layer_height(); + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + return; + } + + GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first)); + GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first)); + if (n_triangles + n_quads == 0) + { + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); + + if (color_id >= 0) + { + float color[4]; + ::memcpy((void*)color, (const void*)render_color, 4 * sizeof(float)); + ::glUniform4fv(color_id, 1, (const GLfloat*)color); + } + else + ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + + if (detection_id != -1) + ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0); + + if (worldmatrix_id != -1) + ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); + + render(); + + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + + return; + } + + if (color_id >= 0) + ::glUniform4fv(color_id, 1, (const GLfloat*)render_color); + else + ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + + if (detection_id != -1) + ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0); + + if (worldmatrix_id != -1) + ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); + + ::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); + ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); + ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr); + + ::glPushMatrix(); + ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); + ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); + + if (n_triangles > 0) + { + ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.triangle_indices_VBO_id); + ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)); + } + if (n_quads > 0) + { + ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.quad_indices_VBO_id); + ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)); + } + + ::glPopMatrix(); +} + +void GLVolume::render_legacy() const +{ + assert(!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); + if (!is_active) + return; + + GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first)); + GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first)); + if (n_triangles + n_quads == 0) + { + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); + + ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + render(); + + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + + return; + } + + ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); + ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data() + 3); + ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data()); + + ::glPushMatrix(); + ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); + ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); + ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); + + if (n_triangles > 0) + ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first); + + if (n_quads > 0) + ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, indexed_vertex_array.quad_indices.data() + qverts_range.first); + + ::glPopMatrix(); +} + +double GLVolume::layer_height_texture_z_to_row_id() const +{ + return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max(2)); +} + +void GLVolume::generate_layer_height_texture(const PrintObject *print_object, bool force) +{ + LayersTexture *tex = this->layer_height_texture.get(); + if (tex == nullptr) // No layer_height_texture is assigned to this GLVolume, therefore the layer height texture cannot be filled. return; // Always try to update the layer height profile. - bool update = print_object->update_layer_height_profile(print_object->model_object()->layer_height_profile) || force; + bool update = print_object->update_layer_height_profile(const_cast<ModelObject*>(print_object->model_object())->layer_height_profile) || force; // Update if the layer height profile was changed, or when the texture is not valid. if (! update && ! tex->data.empty() && tex->cells > 0) // Texture is valid, don't update. @@ -376,15 +641,23 @@ std::vector<int> GLVolumeCollection::load_object( }; // Object will have a single common layer height texture for all volumes. - std::shared_ptr<GLTexture> layer_height_texture = std::make_shared<GLTexture>(); - + std::shared_ptr<LayersTexture> layer_height_texture = std::make_shared<LayersTexture>(); + std::vector<int> volumes_idx; for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) { const ModelVolume *model_volume = model_object->volumes[volume_idx]; + + int extruder_id = -1; + if (!model_volume->modifier) + { + extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0; + if (extruder_id == 0) + extruder_id = model_object->config.has("extruder") ? model_object->config.option("extruder")->getInt() : 0; + } + for (int instance_idx : instance_idxs) { const ModelInstance *instance = model_object->instances[instance_idx]; TriangleMesh mesh = model_volume->mesh; - instance->transform_mesh(&mesh); volumes_idx.push_back(int(this->volumes.size())); float color[4]; memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); @@ -400,18 +673,20 @@ std::vector<int> GLVolumeCollection::load_object( v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx; - if (select_by == "object") - v.select_group_id = obj_idx * 1000000; - else if (select_by == "volume") - v.select_group_id = obj_idx * 1000000 + volume_idx * 1000; - else if (select_by == "instance") - v.select_group_id = v.composite_id; - if (drag_by == "object") - v.drag_group_id = obj_idx * 1000; - else if (drag_by == "instance") - v.drag_group_id = obj_idx * 1000 + instance_idx; - if (! model_volume->modifier) + v.set_select_group_id(select_by); + v.set_drag_group_id(drag_by); + if (!model_volume->modifier) + { + v.set_convex_hull(model_volume->get_convex_hull()); v.layer_height_texture = layer_height_texture; + if (extruder_id != -1) + v.extruder_id = extruder_id; + } + v.is_modifier = model_volume->modifier; + v.shader_outside_printer_detection_enabled = !model_volume->modifier; + v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0)); + v.set_rotation(instance->rotation); + v.set_scaling_factor(instance->scaling_factor); } } @@ -420,24 +695,76 @@ std::vector<int> GLVolumeCollection::load_object( int GLVolumeCollection::load_wipe_tower_preview( - int obj_idx, float pos_x, float pos_y, float width, float depth, float height, bool use_VBOs) + int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width) { - float color[4] = { 1.0f, 1.0f, 0.0f, 0.5f }; + if (depth < 0.01f) + return int(this->volumes.size() - 1); + if (height == 0.0f) + height = 0.1f; + Point origin_of_rotation(0.f, 0.f); + TriangleMesh mesh; + float color[4] = { 0.5f, 0.5f, 0.0f, 1.f }; + + // In case we don't know precise dimensions of the wipe tower yet, we'll draw the box with different color with one side jagged: + if (size_unknown) { + color[0] = 0.9f; + color[1] = 0.6f; + + depth = std::max(depth, 10.f); // Too narrow tower would interfere with the teeth. The estimate is not precise anyway. + float min_width = 30.f; + // We'll now create the box with jagged edge. y-coordinates of the pre-generated model are shifted so that the front + // edge has y=0 and centerline of the back edge has y=depth: + Pointf3s points; + std::vector<Vec3crd> facets; + float out_points_idx[][3] = {{0, -depth, 0}, {0, 0, 0}, {38.453, 0, 0}, {61.547, 0, 0}, {100, 0, 0}, {100, -depth, 0}, {55.7735, -10, 0}, {44.2265, 10, 0}, + {38.453, 0, 1}, {0, 0, 1}, {0, -depth, 1}, {100, -depth, 1}, {100, 0, 1}, {61.547, 0, 1}, {55.7735, -10, 1}, {44.2265, 10, 1}}; + int out_facets_idx[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 5, 0}, {3, 5, 6}, {6, 2, 7}, {6, 0, 2}, {8, 9, 10}, {11, 12, 13}, {10, 11, 14}, {14, 11, 13}, {15, 8, 14}, + {8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8}, + {0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11}}; + for (int i=0;i<16;++i) + points.push_back(Vec3d(out_points_idx[i][0] / (100.f/min_width), out_points_idx[i][1] + depth, out_points_idx[i][2])); + for (int i=0;i<28;++i) + facets.push_back(Vec3crd(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2])); + TriangleMesh tooth_mesh(points, facets); + + // We have the mesh ready. It has one tooth and width of min_width. We will now append several of these together until we are close to + // the required width of the block. Than we can scale it precisely. + size_t n = std::max(1, int(width/min_width)); // How many shall be merged? + for (size_t i=0;i<n;++i) { + mesh.merge(tooth_mesh); + tooth_mesh.translate(min_width, 0.f, 0.f); + } + + mesh.scale(Vec3d(width/(n*min_width), 1.f, height)); // Scaling to proper width + } + else + mesh = make_cube(width, depth, height); + + // We'll make another mesh to show the brim (fixed layer height): + TriangleMesh brim_mesh = make_cube(width+2.f*brim_width, depth+2.f*brim_width, 0.2f); + brim_mesh.translate(-brim_width, -brim_width, 0.f); + mesh.merge(brim_mesh); + + mesh.rotate(rotation_angle, &origin_of_rotation); // rotates the box according to the config rotation setting + this->volumes.emplace_back(new GLVolume(color)); GLVolume &v = *this->volumes.back(); - auto mesh = make_cube(width, depth, height); + if (use_VBOs) v.indexed_vertex_array.load_mesh_full_shading(mesh); else v.indexed_vertex_array.load_mesh_flat_shading(mesh); - v.origin = Pointf3(pos_x, pos_y, 0.); + v.set_offset(Vec3d(pos_x, pos_y, 0.0)); + // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); v.indexed_vertex_array.finalize_geometry(use_VBOs); v.composite_id = obj_idx * 1000000; v.select_group_id = obj_idx * 1000000; v.drag_group_id = obj_idx * 1000; + v.is_wipe_tower = true; + v.shader_outside_printer_detection_enabled = ! size_unknown; return int(this->volumes.size() - 1); } @@ -455,102 +782,23 @@ void GLVolumeCollection::render_VBOs() const GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1; GLint print_box_min_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.min") : -1; GLint print_box_max_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.max") : -1; - GLint print_box_origin_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_origin") : -1; + GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1; + GLint print_box_worldmatrix_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1; - for (GLVolume *volume : this->volumes) { - if (!volume->is_active) - continue; + if (print_box_min_id != -1) + ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min); - if (!volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id) - continue; + if (print_box_max_id != -1) + ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max); + for (GLVolume *volume : this->volumes) + { if (volume->layer_height_texture_data.can_use()) - { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); volume->generate_layer_height_texture(volume->layer_height_texture_data.print_object, false); - volume->render_using_layer_height(); - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - continue; - } - - volume->set_render_color(); - - GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first)); - GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first)); - if (n_triangles + n_quads == 0) - { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); - - if (color_id >= 0) - { - float color[4]; - ::memcpy((void*)color, (const void*)volume->render_color, 4 * sizeof(float)); - ::glUniform4fv(color_id, 1, (const GLfloat*)color); - } - else - ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); - - if (print_box_min_id != -1) - ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min); - - if (print_box_max_id != -1) - ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max); - - if (print_box_origin_id != -1) - { - float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f }; - ::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin); - } - - volume->render(); - - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - - continue; - } - - if (color_id >= 0) - ::glUniform4fv(color_id, 1, (const GLfloat*)volume->render_color); else - ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); - - if (print_box_min_id != -1) - ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min); + volume->set_render_color(); - if (print_box_max_id != -1) - ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max); - - if (print_box_origin_id != -1) - { - float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f }; - ::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin); - } - - ::glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); - ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); - ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr); - - bool has_offset = (volume->origin.x != 0) || (volume->origin.y != 0) || (volume->origin.z != 0); - if (has_offset) { - ::glPushMatrix(); - ::glTranslated(volume->origin.x, volume->origin.y, volume->origin.z); - } - - if (n_triangles > 0) { - ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id); - ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(volume->tverts_range.first * 4)); - } - if (n_quads > 0) { - ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id); - ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(volume->qverts_range.first * 4)); - } - - if (has_offset) - ::glPopMatrix(); + volume->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id); } ::glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -571,43 +819,10 @@ void GLVolumeCollection::render_legacy() const glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); - for (GLVolume *volume : this->volumes) { - assert(! volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); - if (!volume->is_active) - continue; - + for (GLVolume *volume : this->volumes) + { volume->set_render_color(); - - GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first)); - GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first)); - if (n_triangles + n_quads == 0) - { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); - - ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); - volume->render(); - - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - - continue; - } - - glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]); - glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data() + 3); - glNormalPointer(GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data()); - bool has_offset = volume->origin.x != 0 || volume->origin.y != 0 || volume->origin.z != 0; - if (has_offset) { - glPushMatrix(); - glTranslated(volume->origin.x, volume->origin.y, volume->origin.z); - } - if (n_triangles > 0) - glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, volume->indexed_vertex_array.triangle_indices.data() + volume->tverts_range.first); - if (n_quads > 0) - glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, volume->indexed_vertex_array.quad_indices.data() + volume->qverts_range.first); - if (has_offset) - glPopMatrix(); + volume->render_legacy(); } glDisableClientState(GL_VERTEX_ARRAY); @@ -616,41 +831,174 @@ void GLVolumeCollection::render_legacy() const glDisable(GL_BLEND); } -void GLVolumeCollection::update_outside_state(const DynamicPrintConfig* config, bool all_inside) +bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state) { if (config == nullptr) - return; + return false; const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape")); if (opt == nullptr) - return; + return false; BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); - BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height"))); + BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config->opt_float("max_print_height"))); + // Allow the objects to protrude below the print bed + print_volume.min(2) = -1e10; + + ModelInstance::EPrintVolumeState state = ModelInstance::PVS_Inside; + bool all_contained = true; for (GLVolume* volume : this->volumes) { - if (all_inside) + if ((volume != nullptr) && !volume->is_modifier && (!volume->is_wipe_tower || (volume->is_wipe_tower && volume->shader_outside_printer_detection_enabled))) { + const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); + bool contained = print_volume.contains(bb); + all_contained &= contained; + + volume->is_outside = !contained; + + if ((state == ModelInstance::PVS_Inside) && volume->is_outside) + state = ModelInstance::PVS_Fully_Outside; + + if ((state == ModelInstance::PVS_Fully_Outside) && volume->is_outside && print_volume.intersects(bb)) + state = ModelInstance::PVS_Partly_Outside; + } + } + + if (out_state != nullptr) + *out_state = state; + + return all_contained; +} + +void GLVolumeCollection::reset_outside_state() +{ + for (GLVolume* volume : this->volumes) + { + if (volume != nullptr) volume->is_outside = false; - continue; + } +} + +void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* config) +{ + static const float inv_255 = 1.0f / 255.0f; + + struct Color + { + std::string text; + unsigned char rgb[3]; + + Color() + : text("") + { + rgb[0] = 255; + rgb[1] = 255; + rgb[2] = 255; + } + + void set(const std::string& text, unsigned char* rgb) + { + this->text = text; + ::memcpy((void*)this->rgb, (const void*)rgb, 3 * sizeof(unsigned char)); } + }; - volume->is_outside = !print_volume.contains(volume->transformed_bounding_box()); + if (config == nullptr) + return; + + const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("extruder_colour")); + if (extruders_opt == nullptr) + return; + + const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("filament_colour")); + if (filamemts_opt == nullptr) + return; + + unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size()); + if (colors_count == 0) + return; + + std::vector<Color> colors(colors_count); + + unsigned char rgb[3]; + for (unsigned int i = 0; i < colors_count; ++i) + { + const std::string& txt_color = config->opt_string("extruder_colour", i); + if (PresetBundle::parse_color(txt_color, rgb)) + { + colors[i].set(txt_color, rgb); + } + else + { + const std::string& txt_color = config->opt_string("filament_colour", i); + if (PresetBundle::parse_color(txt_color, rgb)) + colors[i].set(txt_color, rgb); + } + } + + for (GLVolume* volume : volumes) + { + if ((volume == nullptr) || volume->is_modifier || volume->is_wipe_tower) + continue; + + int extruder_id = volume->extruder_id - 1; + if ((extruder_id < 0) || ((unsigned int)colors.size() <= extruder_id)) + extruder_id = 0; + + const Color& color = colors[extruder_id]; + if (!color.text.empty()) + { + for (int i = 0; i < 3; ++i) + { + volume->color[i] = (float)color.rgb[i] * inv_255; + } + } } } -std::vector<double> GLVolumeCollection::get_current_print_zs() const +void GLVolumeCollection::set_select_by(const std::string& select_by) { - std::vector<double> print_zs; + for (GLVolume *vol : this->volumes) + { + if (vol != nullptr) + vol->set_select_group_id(select_by); + } +} +void GLVolumeCollection::set_drag_by(const std::string& drag_by) +{ for (GLVolume *vol : this->volumes) { - for (coordf_t z : vol->print_zs) - print_zs.emplace_back((double)round(z * 100000.0f) / 100000.0f); + if (vol != nullptr) + vol->set_drag_group_id(drag_by); } +} - sort_remove_duplicates(print_zs); +std::vector<double> GLVolumeCollection::get_current_print_zs(bool active_only) const +{ + // Collect layer top positions of all volumes. + std::vector<double> print_zs; + for (GLVolume *vol : this->volumes) + { + if (!active_only || vol->is_active) + append(print_zs, vol->print_zs); + } + std::sort(print_zs.begin(), print_zs.end()); + + // Replace intervals of layers with similar top positions with their average value. + int n = int(print_zs.size()); + int k = 0; + for (int i = 0; i < n;) { + int j = i + 1; + coordf_t zmax = print_zs[i] + EPSILON; + for (; j < n && print_zs[j] <= zmax; ++ j) ; + print_zs[k ++] = (j > i + 1) ? (0.5 * (print_zs[i] + print_zs[j - 1])) : print_zs[i]; + i = j; + } + if (k < n) + print_zs.erase(print_zs.begin() + k, print_zs.end()); return print_zs; } @@ -673,47 +1021,53 @@ static void thick_lines_to_indexed_vertex_array( #define TOP 2 #define BOTTOM 3 - Line prev_line; // right, left, top, bottom int idx_prev[4] = { -1, -1, -1, -1 }; double bottom_z_prev = 0.; - Pointf b1_prev; - Pointf b2_prev; - Vectorf v_prev; + Vec2d b1_prev(Vec2d::Zero()); + Vec2d v_prev(Vec2d::Zero()); int idx_initial[4] = { -1, -1, -1, -1 }; double width_initial = 0.; + double bottom_z_initial = 0.0; // loop once more in case of closed loops size_t lines_end = closed ? (lines.size() + 1) : lines.size(); for (size_t ii = 0; ii < lines_end; ++ ii) { size_t i = (ii == lines.size()) ? 0 : ii; const Line &line = lines[i]; - double len = unscale(line.length()); + double len = unscale<double>(line.length()); + double inv_len = 1.0 / len; double bottom_z = top_z - heights[i]; - double middle_z = (top_z + bottom_z) / 2.; + double middle_z = 0.5 * (top_z + bottom_z); double width = widths[i]; - - Vectorf v = Vectorf::new_unscale(line.vector()); - v.scale(1. / len); - - Pointf a = Pointf::new_unscale(line.a); - Pointf b = Pointf::new_unscale(line.b); - Pointf a1 = a; - Pointf a2 = a; - Pointf b1 = b; - Pointf b2 = b; + + bool is_first = (ii == 0); + bool is_last = (ii == lines_end - 1); + bool is_closing = closed && is_last; + + Vec2d v = unscale(line.vector()); + v *= inv_len; + + Vec2d a = unscale(line.a); + Vec2d b = unscale(line.b); + Vec2d a1 = a; + Vec2d a2 = a; + Vec2d b1 = b; + Vec2d b2 = b; { - double dist = width / 2.; // scaled - a1.translate(+dist*v.y, -dist*v.x); - a2.translate(-dist*v.y, +dist*v.x); - b1.translate(+dist*v.y, -dist*v.x); - b2.translate(-dist*v.y, +dist*v.x); + double dist = 0.5 * width; // scaled + double dx = dist * v(0); + double dy = dist * v(1); + a1 += Vec2d(+dy, -dx); + a2 += Vec2d(-dy, +dx); + b1 += Vec2d(+dy, -dx); + b2 += Vec2d(-dy, +dx); } // calculate new XY normals Vector n = line.normal(); - Vectorf3 xy_right_normal = Vectorf3::new_unscale(n.x, n.y, 0); - xy_right_normal.scale(1.f / len); + Vec3d xy_right_normal = unscale(n(0), n(1), 0); + xy_right_normal *= inv_len; int idx_a[4]; int idx_b[4]; @@ -722,72 +1076,92 @@ static void thick_lines_to_indexed_vertex_array( bool bottom_z_different = bottom_z_prev != bottom_z; bottom_z_prev = bottom_z; + if (!is_first && bottom_z_different) + { + // Found a change of the layer thickness -> Add a cap at the end of the previous segment. + volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]); + } + // Share top / bottom vertices if possible. - if (ii == 0) { - idx_a[TOP] = idx_last ++; - volume.push_geometry(a.x, a.y, top_z , 0., 0., 1.); + if (is_first) { + idx_a[TOP] = idx_last++; + volume.push_geometry(a(0), a(1), top_z , 0., 0., 1.); } else { idx_a[TOP] = idx_prev[TOP]; } - if (ii == 0 || bottom_z_different) { + + if (is_first || bottom_z_different) { // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z. idx_a[BOTTOM] = idx_last ++; - volume.push_geometry(a.x, a.y, bottom_z, 0., 0., -1.); + volume.push_geometry(a(0), a(1), bottom_z, 0., 0., -1.); idx_a[LEFT ] = idx_last ++; - volume.push_geometry(a2.x, a2.y, middle_z, -xy_right_normal.x, -xy_right_normal.y, -xy_right_normal.z); + volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); idx_a[RIGHT] = idx_last ++; - volume.push_geometry(a1.x, a1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z); - } else { + volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); + } + else { idx_a[BOTTOM] = idx_prev[BOTTOM]; } - if (ii == 0) { + if (is_first) { // Start of the 1st line segment. width_initial = width; + bottom_z_initial = bottom_z; memcpy(idx_initial, idx_a, sizeof(int) * 4); } else { // Continuing a previous segment. // Share left / right vertices if possible. - double v_dot = dot(v_prev, v); + double v_dot = v_prev.dot(v); bool sharp = v_dot < 0.707; // sin(45 degrees) if (sharp) { - // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. - idx_a[RIGHT] = idx_last ++; - volume.push_geometry(a1.x, a1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z); - idx_a[LEFT ] = idx_last ++; - volume.push_geometry(a2.x, a2.y, middle_z, -xy_right_normal.x, -xy_right_normal.y, -xy_right_normal.z); + if (!bottom_z_different) + { + // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. + idx_a[RIGHT] = idx_last++; + volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); + idx_a[LEFT] = idx_last++; + volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); + } } if (v_dot > 0.9) { - // The two successive segments are nearly collinear. - idx_a[LEFT ] = idx_prev[LEFT]; - idx_a[RIGHT] = idx_prev[RIGHT]; - } else if (! sharp) { - // Create a sharp corner with an overshot and average the left / right normals. - // At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc. - Pointf intersection; - Geometry::ray_ray_intersection(b1_prev, v_prev, a1, v, intersection); - a1 = intersection; - a2 = 2. * a - intersection; - assert(length(a1.vector_to(a)) < width); - assert(length(a2.vector_to(a)) < width); - float *n_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6; - float *p_left_prev = n_left_prev + 3; - float *n_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6; - float *p_right_prev = n_right_prev + 3; - p_left_prev [0] = float(a2.x); - p_left_prev [1] = float(a2.y); - p_right_prev[0] = float(a1.x); - p_right_prev[1] = float(a1.y); - xy_right_normal.x += n_right_prev[0]; - xy_right_normal.y += n_right_prev[1]; - xy_right_normal.scale(1. / length(xy_right_normal)); - n_left_prev [0] = float(-xy_right_normal.x); - n_left_prev [1] = float(-xy_right_normal.y); - n_right_prev[0] = float( xy_right_normal.x); - n_right_prev[1] = float( xy_right_normal.y); - idx_a[LEFT ] = idx_prev[LEFT ]; - idx_a[RIGHT] = idx_prev[RIGHT]; - } else if (cross(v_prev, v) > 0.) { + if (!bottom_z_different) + { + // The two successive segments are nearly collinear. + idx_a[LEFT ] = idx_prev[LEFT]; + idx_a[RIGHT] = idx_prev[RIGHT]; + } + } + else if (!sharp) { + if (!bottom_z_different) + { + // Create a sharp corner with an overshot and average the left / right normals. + // At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc. + Vec2d intersection(Vec2d::Zero()); + Geometry::ray_ray_intersection(b1_prev, v_prev, a1, v, intersection); + a1 = intersection; + a2 = 2. * a - intersection; + assert((a - a1).norm() < width); + assert((a - a2).norm() < width); + float *n_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6; + float *p_left_prev = n_left_prev + 3; + float *n_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6; + float *p_right_prev = n_right_prev + 3; + p_left_prev [0] = float(a2(0)); + p_left_prev [1] = float(a2(1)); + p_right_prev[0] = float(a1(0)); + p_right_prev[1] = float(a1(1)); + xy_right_normal(0) += n_right_prev[0]; + xy_right_normal(1) += n_right_prev[1]; + xy_right_normal *= 1. / xy_right_normal.norm(); + n_left_prev [0] = float(-xy_right_normal(0)); + n_left_prev [1] = float(-xy_right_normal(1)); + n_right_prev[0] = float( xy_right_normal(0)); + n_right_prev[1] = float( xy_right_normal(1)); + idx_a[LEFT ] = idx_prev[LEFT ]; + idx_a[RIGHT] = idx_prev[RIGHT]; + } + } + else if (cross2(v_prev, v) > 0.) { // Right turn. Fill in the right turn wedge. volume.push_triangle(idx_prev[RIGHT], idx_a [RIGHT], idx_prev[TOP] ); volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a [RIGHT] ); @@ -796,18 +1170,21 @@ static void thick_lines_to_indexed_vertex_array( volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a [LEFT] ); volume.push_triangle(idx_prev[LEFT], idx_a [LEFT], idx_prev[BOTTOM]); } - if (ii == lines.size()) { - if (! sharp) { - // Closing a loop with smooth transition. Unify the closing left / right vertices. - memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[LEFT ] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6, sizeof(float) * 6); - memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[RIGHT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6, sizeof(float) * 6); - volume.vertices_and_normals_interleaved.erase(volume.vertices_and_normals_interleaved.end() - 12, volume.vertices_and_normals_interleaved.end()); - // Replace the left / right vertex indices to point to the start of the loop. - for (size_t u = volume.quad_indices.size() - 16; u < volume.quad_indices.size(); ++ u) { - if (volume.quad_indices[u] == idx_prev[LEFT]) - volume.quad_indices[u] = idx_initial[LEFT]; - else if (volume.quad_indices[u] == idx_prev[RIGHT]) - volume.quad_indices[u] = idx_initial[RIGHT]; + if (is_closing) { + if (!sharp) { + if (!bottom_z_different) + { + // Closing a loop with smooth transition. Unify the closing left / right vertices. + memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[LEFT ] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6, sizeof(float) * 6); + memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[RIGHT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6, sizeof(float) * 6); + volume.vertices_and_normals_interleaved.erase(volume.vertices_and_normals_interleaved.end() - 12, volume.vertices_and_normals_interleaved.end()); + // Replace the left / right vertex indices to point to the start of the loop. + for (size_t u = volume.quad_indices.size() - 16; u < volume.quad_indices.size(); ++ u) { + if (volume.quad_indices[u] == idx_prev[LEFT]) + volume.quad_indices[u] = idx_initial[LEFT]; + else if (volume.quad_indices[u] == idx_prev[RIGHT]) + volume.quad_indices[u] = idx_initial[RIGHT]; + } } } // This is the last iteration, only required to solve the transition. @@ -816,40 +1193,45 @@ static void thick_lines_to_indexed_vertex_array( } // Only new allocate top / bottom vertices, if not closing a loop. - if (closed && ii + 1 == lines.size()) { + if (is_closing) { idx_b[TOP] = idx_initial[TOP]; } else { idx_b[TOP] = idx_last ++; - volume.push_geometry(b.x, b.y, top_z , 0., 0., 1.); + volume.push_geometry(b(0), b(1), top_z , 0., 0., 1.); } - if (closed && ii + 1 == lines.size() && width == width_initial) { + + if (is_closing && (width == width_initial) && (bottom_z == bottom_z_initial)) { idx_b[BOTTOM] = idx_initial[BOTTOM]; } else { idx_b[BOTTOM] = idx_last ++; - volume.push_geometry(b.x, b.y, bottom_z, 0., 0., -1.); + volume.push_geometry(b(0), b(1), bottom_z, 0., 0., -1.); } // Generate new vertices for the end of this line segment. idx_b[LEFT ] = idx_last ++; - volume.push_geometry(b2.x, b2.y, middle_z, -xy_right_normal.x, -xy_right_normal.y, -xy_right_normal.z); + volume.push_geometry(b2(0), b2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); idx_b[RIGHT ] = idx_last ++; - volume.push_geometry(b1.x, b1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z); + volume.push_geometry(b1(0), b1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); - prev_line = line; memcpy(idx_prev, idx_b, 4 * sizeof(int)); bottom_z_prev = bottom_z; b1_prev = b1; - b2_prev = b2; - v_prev = v; + v_prev = v; + + if (bottom_z_different && (closed || (!is_first && !is_last))) + { + // Found a change of the layer thickness -> Add a cap at the beginning of this segment. + volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]); + } if (! closed) { // Terminate open paths with caps. - if (i == 0) + if (is_first) volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]); // We don't use 'else' because both cases are true if we have only one line. - if (i + 1 == lines.size()) + if (is_last) volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]); } - + // Add quads for a straight hollow tube-like segment. // bottom-right face volume.push_quad(idx_a[BOTTOM], idx_b[BOTTOM], idx_b[RIGHT], idx_a[RIGHT]); @@ -887,15 +1269,15 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, int idx_initial[4] = { -1, -1, -1, -1 }; int idx_prev[4] = { -1, -1, -1, -1 }; double z_prev = 0.0; - Vectorf3 n_right_prev; - Vectorf3 n_top_prev; - Vectorf3 unit_v_prev; + Vec3d n_right_prev = Vec3d::Zero(); + Vec3d n_top_prev = Vec3d::Zero(); + Vec3d unit_v_prev = Vec3d::Zero(); double width_initial = 0.0; // new vertices around the line endpoints // left, right, top, bottom - Pointf3 a[4]; - Pointf3 b[4]; + Vec3d a[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; + Vec3d b[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; // loop once more in case of closed loops size_t lines_end = closed ? (lines.size() + 1) : lines.size(); @@ -907,29 +1289,29 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, double height = heights[i]; double width = widths[i]; - Vectorf3 unit_v = normalize(Vectorf3::new_unscale(line.vector())); + Vec3d unit_v = unscale(line.vector()).normalized(); - Vectorf3 n_top; - Vectorf3 n_right; - Vectorf3 unit_positive_z(0.0, 0.0, 1.0); + Vec3d n_top = Vec3d::Zero(); + Vec3d n_right = Vec3d::Zero(); + Vec3d unit_positive_z(0.0, 0.0, 1.0); - if ((line.a.x == line.b.x) && (line.a.y == line.b.y)) + if ((line.a(0) == line.b(0)) && (line.a(1) == line.b(1))) { // vertical segment - n_right = (line.a.z < line.b.z) ? Vectorf3(-1.0, 0.0, 0.0) : Vectorf3(1.0, 0.0, 0.0); - n_top = Vectorf3(0.0, 1.0, 0.0); + n_right = (line.a(2) < line.b(2)) ? Vec3d(-1.0, 0.0, 0.0) : Vec3d(1.0, 0.0, 0.0); + n_top = Vec3d(0.0, 1.0, 0.0); } else { // generic segment - n_right = normalize(cross(unit_v, unit_positive_z)); - n_top = normalize(cross(n_right, unit_v)); + n_right = unit_v.cross(unit_positive_z).normalized(); + n_top = n_right.cross(unit_v).normalized(); } - Vectorf3 rl_displacement = 0.5 * width * n_right; - Vectorf3 tb_displacement = 0.5 * height * n_top; - Pointf3 l_a = Pointf3::new_unscale(line.a); - Pointf3 l_b = Pointf3::new_unscale(line.b); + Vec3d rl_displacement = 0.5 * width * n_right; + Vec3d tb_displacement = 0.5 * height * n_top; + Vec3d l_a = unscale(line.a); + Vec3d l_b = unscale(line.b); a[RIGHT] = l_a + rl_displacement; a[LEFT] = l_a - rl_displacement; @@ -940,15 +1322,15 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, b[TOP] = l_b + tb_displacement; b[BOTTOM] = l_b - tb_displacement; - Vectorf3 n_bottom = -n_top; - Vectorf3 n_left = -n_right; + Vec3d n_bottom = -n_top; + Vec3d n_left = -n_right; int idx_a[4]; int idx_b[4]; int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6); - bool z_different = (z_prev != l_a.z); - z_prev = l_b.z; + bool z_different = (z_prev != l_a(2)); + z_prev = l_b(2); // Share top / bottom vertices if possible. if (ii == 0) @@ -982,9 +1364,9 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, { // Continuing a previous segment. // Share left / right vertices if possible. - double v_dot = dot(unit_v_prev, unit_v); + double v_dot = unit_v_prev.dot(unit_v); bool is_sharp = v_dot < 0.707; // sin(45 degrees) - bool is_right_turn = dot(n_top_prev, cross(unit_v_prev, unit_v)) > 0.0; + bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0; if (is_sharp) { @@ -1007,9 +1389,9 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, // At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc. // averages normals - Vectorf3 average_n_right = normalize(0.5 * (n_right + n_right_prev)); - Vectorf3 average_n_left = -average_n_right; - Vectorf3 average_rl_displacement = 0.5 * width * average_n_right; + Vec3d average_n_right = 0.5 * (n_right + n_right_prev).normalized(); + Vec3d average_n_left = -average_n_right; + Vec3d average_rl_displacement = 0.5 * width * average_n_right; // updates vertices around a a[RIGHT] = l_a + average_rl_displacement; @@ -1017,25 +1399,25 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, // updates previous line normals float* normal_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT] * 6; - normal_left_prev[0] = float(average_n_left.x); - normal_left_prev[1] = float(average_n_left.y); - normal_left_prev[2] = float(average_n_left.z); + normal_left_prev[0] = float(average_n_left(0)); + normal_left_prev[1] = float(average_n_left(1)); + normal_left_prev[2] = float(average_n_left(2)); float* normal_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6; - normal_right_prev[0] = float(average_n_right.x); - normal_right_prev[1] = float(average_n_right.y); - normal_right_prev[2] = float(average_n_right.z); + normal_right_prev[0] = float(average_n_right(0)); + normal_right_prev[1] = float(average_n_right(1)); + normal_right_prev[2] = float(average_n_right(2)); // updates previous line's vertices around b float* b_left_prev = normal_left_prev + 3; - b_left_prev[0] = float(a[LEFT].x); - b_left_prev[1] = float(a[LEFT].y); - b_left_prev[2] = float(a[LEFT].z); + b_left_prev[0] = float(a[LEFT](0)); + b_left_prev[1] = float(a[LEFT](1)); + b_left_prev[2] = float(a[LEFT](2)); float* b_right_prev = normal_right_prev + 3; - b_right_prev[0] = float(a[RIGHT].x); - b_right_prev[1] = float(a[RIGHT].y); - b_right_prev[2] = float(a[RIGHT].z); + b_right_prev[0] = float(a[RIGHT](0)); + b_right_prev[1] = float(a[RIGHT](1)); + b_right_prev[2] = float(a[RIGHT](2)); idx_a[LEFT] = idx_prev[LEFT]; idx_a[RIGHT] = idx_prev[RIGHT]; @@ -1132,14 +1514,14 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines, #undef BOTTOM } -static void point_to_indexed_vertex_array(const Point3& point, +static void point_to_indexed_vertex_array(const Vec3crd& point, double width, double height, GLIndexedVertexArray& volume) { // builds a double piramid, with vertices on the local axes, around the point - Pointf3 center = Pointf3::new_unscale(point); + Vec3d center = unscale(point); double scale_factor = 1.0; double w = scale_factor * width; @@ -1153,13 +1535,13 @@ static void point_to_indexed_vertex_array(const Point3& point, idxs[i] = idx_last + i; } - Vectorf3 displacement_x(w, 0.0, 0.0); - Vectorf3 displacement_y(0.0, w, 0.0); - Vectorf3 displacement_z(0.0, 0.0, h); + Vec3d displacement_x(w, 0.0, 0.0); + Vec3d displacement_y(0.0, w, 0.0); + Vec3d displacement_z(0.0, 0.0, h); - Vectorf3 unit_x(1.0, 0.0, 0.0); - Vectorf3 unit_y(0.0, 1.0, 0.0); - Vectorf3 unit_z(0.0, 0.0, 1.0); + Vec3d unit_x(1.0, 0.0, 0.0); + Vec3d unit_y(0.0, 1.0, 0.0); + Vec3d unit_z(0.0, 0.0, 1.0); // vertices volume.push_geometry(center - displacement_x, -unit_x); // idxs[0] @@ -1182,8 +1564,8 @@ static void point_to_indexed_vertex_array(const Point3& point, volume.push_triangle(idxs[0], idxs[3], idxs[4]); } -static void thick_lines_to_verts( - const Lines &lines, +void _3DScene::thick_lines_to_verts( + const Lines &lines, const std::vector<double> &widths, const std::vector<double> &heights, bool closed, @@ -1193,7 +1575,7 @@ static void thick_lines_to_verts( thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, top_z, volume.indexed_vertex_array); } -static void thick_lines_to_verts(const Lines3& lines, +void _3DScene::thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, @@ -1202,7 +1584,7 @@ static void thick_lines_to_verts(const Lines3& lines, thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, volume.indexed_vertex_array); } -static void thick_point_to_verts(const Point3& point, +static void thick_point_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume) @@ -1211,7 +1593,7 @@ static void thick_point_to_verts(const Point3& point, } // Fill in the qverts and tverts with quads and triangles for the extrusion_path. -static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) +void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) { Lines lines = extrusion_path.polyline.lines(); std::vector<double> widths(lines.size(), extrusion_path.width); @@ -1220,7 +1602,7 @@ static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, } // Fill in the qverts and tverts with quads and triangles for the extrusion_path. -static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) +void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) { Polyline polyline = extrusion_path.polyline; polyline.remove_duplicate_points(); @@ -1232,7 +1614,7 @@ static inline void extrusionentity_to_verts(const ExtrusionPath &extrusion_path, } // Fill in the qverts and tverts with quads and triangles for the extrusion_loop. -static inline void extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) +void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) { Lines lines; std::vector<double> widths; @@ -1250,7 +1632,7 @@ static inline void extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, } // Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path. -static inline void extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) +void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) { Lines lines; std::vector<double> widths; @@ -1267,15 +1649,13 @@ static inline void extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_ thick_lines_to_verts(lines, widths, heights, false, print_z, volume); } -static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume); - -static inline void extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume) +void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume) { for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities) extrusionentity_to_verts(extrusion_entity, print_z, copy, volume); } -static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) +void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) { if (extrusion_entity != nullptr) { auto *extrusion_path = dynamic_cast<const ExtrusionPath*>(extrusion_entity); @@ -1302,7 +1682,7 @@ static void extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, fl } } -static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume) +void _3DScene::polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume) { Lines3 lines = polyline.lines(); std::vector<double> widths(lines.size(), width); @@ -1310,1280 +1690,498 @@ static void polyline3_to_verts(const Polyline3& polyline, double width, double h thick_lines_to_verts(lines, widths, heights, false, volume); } -static void point3_to_verts(const Point3& point, double width, double height, GLVolume& volume) +void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume) { thick_point_to_verts(point, width, height, volume); } -_3DScene::GCodePreviewVolumeIndex _3DScene::s_gcode_preview_volume_index; -_3DScene::LegendTexture _3DScene::s_legend_texture; -_3DScene::WarningTexture _3DScene::s_warning_texture; +GUI::GLCanvas3DManager _3DScene::s_canvas_mgr; -unsigned int _3DScene::TextureBase::finalize() +void _3DScene::init_gl() { - if (!m_data.empty()) { - // sends buffer to gpu - ::glGenTextures(1, &m_tex_id); - ::glBindTexture(GL_TEXTURE_2D, m_tex_id); - ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)m_data.data()); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - ::glBindTexture(GL_TEXTURE_2D, 0); - m_data.clear(); - } - return (m_tex_width > 0 && m_tex_height > 0) ? m_tex_id : 0; + s_canvas_mgr.init_gl(); } -void _3DScene::TextureBase::_destroy_texture() +std::string _3DScene::get_gl_info(bool format_as_html, bool extensions) { - if (m_tex_id > 0) - { - ::glDeleteTextures(1, &m_tex_id); - m_tex_id = 0; - m_tex_height = 0; - m_tex_width = 0; - } - m_data.clear(); + return s_canvas_mgr.get_gl_info(format_as_html, extensions); } - -const unsigned char _3DScene::WarningTexture::Background_Color[3] = { 9, 91, 134 }; -const unsigned char _3DScene::WarningTexture::Opacity = 255; - -// Generate a texture data, but don't load it into the GPU yet, as the GPU context may not yet be valid. -bool _3DScene::WarningTexture::generate(const std::string& msg) +bool _3DScene::use_VBOs() { - // Mark the texture as released, but don't release the texture from the GPU yet. - m_tex_width = m_tex_height = 0; - m_data.clear(); - - if (msg.empty()) - return false; - - wxMemoryDC memDC; - // select default font - memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - - // calculates texture size - wxCoord w, h; - memDC.GetTextExtent(msg, &w, &h); - m_tex_width = (unsigned int)w; - m_tex_height = (unsigned int)h; - - // generates bitmap - wxBitmap bitmap(m_tex_width, m_tex_height); - -#if defined(__APPLE__) || defined(_MSC_VER) - bitmap.UseAlpha(); -#endif - - memDC.SelectObject(bitmap); - memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2]))); - memDC.Clear(); - - memDC.SetTextForeground(*wxWHITE); - - // draw message - memDC.DrawText(msg, 0, 0); - - memDC.SelectObject(wxNullBitmap); - - // Convert the bitmap into a linear data ready to be loaded into the GPU. - { - wxImage image = bitmap.ConvertToImage(); - image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]); - - // prepare buffer - m_data.assign(4 * m_tex_width * m_tex_height, 0); - for (unsigned int h = 0; h < m_tex_height; ++h) - { - unsigned int hh = h * m_tex_width; - unsigned char* px_ptr = m_data.data() + 4 * hh; - for (unsigned int w = 0; w < m_tex_width; ++w) - { - *px_ptr++ = image.GetRed(w, h); - *px_ptr++ = image.GetGreen(w, h); - *px_ptr++ = image.GetBlue(w, h); - *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity; - } - } - } - return true; + return s_canvas_mgr.use_VBOs(); } -const unsigned char _3DScene::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 }; -const unsigned char _3DScene::LegendTexture::Background_Color[3] = { 9, 91, 134 }; -const unsigned char _3DScene::LegendTexture::Opacity = 255; - -// Generate a texture data, but don't load it into the GPU yet, as the GPU context may not yet be valid. -bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors) +bool _3DScene::add_canvas(wxGLCanvas* canvas) { - // Mark the texture as released, but don't release the texture from the GPU yet. - m_tex_width = m_tex_height = 0; - m_data.clear(); - - // collects items to render - auto title = GUI::L_str(preview_data.get_legend_title()); - const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors); - - unsigned int items_count = (unsigned int)items.size(); - if (items_count == 0) - // nothing to render, return - return false; - - wxMemoryDC memDC; - // select default font - memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - - // calculates texture size - wxCoord w, h; - memDC.GetTextExtent(title, &w, &h); - unsigned int title_width = (unsigned int)w; - unsigned int title_height = (unsigned int)h; - - unsigned int max_text_width = 0; - unsigned int max_text_height = 0; - for (const GCodePreviewData::LegendItem& item : items) - { - memDC.GetTextExtent(GUI::from_u8(item.text), &w, &h); - max_text_width = std::max(max_text_width, (unsigned int)w); - max_text_height = std::max(max_text_height, (unsigned int)h); - } - - m_tex_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width); - m_tex_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square; - if (items_count > 1) - m_tex_height += (items_count - 1) * Px_Square_Contour; - - // generates bitmap - wxBitmap bitmap(m_tex_width, m_tex_height); - -#if defined(__APPLE__) || defined(_MSC_VER) - bitmap.UseAlpha(); -#endif - - memDC.SelectObject(bitmap); - memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2]))); - memDC.Clear(); - - memDC.SetTextForeground(*wxWHITE); - - // draw title - unsigned int title_x = Px_Border; - unsigned int title_y = Px_Border; - memDC.DrawText(title, title_x, title_y); - - // draw icons contours as background - unsigned int squares_contour_x = Px_Border; - unsigned int squares_contour_y = Px_Border + title_height + Px_Title_Offset; - unsigned int squares_contour_width = Px_Square + 2 * Px_Square_Contour; - unsigned int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour; - if (items_count > 1) - squares_contour_height += (items_count - 1) * Px_Square_Contour; - - wxColour color(Squares_Border_Color[0], Squares_Border_Color[1], Squares_Border_Color[2]); - wxPen pen(color); - wxBrush brush(color); - memDC.SetPen(pen); - memDC.SetBrush(brush); - memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height)); - - // draw items (colored icon + text) - unsigned int icon_x = squares_contour_x + Px_Square_Contour; - unsigned int icon_x_inner = icon_x + 1; - unsigned int icon_y = squares_contour_y + Px_Square_Contour; - unsigned int icon_y_step = Px_Square + Px_Square_Contour; - - unsigned int text_x = icon_x + Px_Square + Px_Text_Offset; - unsigned int text_y_offset = (Px_Square - max_text_height) / 2; - - unsigned int px_inner_square = Px_Square - 2; - - for (const GCodePreviewData::LegendItem& item : items) - { - // draw darker icon perimeter - const std::vector<unsigned char>& item_color_bytes = item.color.as_bytes(); - wxImage::HSVValue dark_hsv = wxImage::RGBtoHSV(wxImage::RGBValue(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2])); - dark_hsv.value *= 0.75; - wxImage::RGBValue dark_rgb = wxImage::HSVtoRGB(dark_hsv); - color.Set(dark_rgb.red, dark_rgb.green, dark_rgb.blue, item_color_bytes[3]); - pen.SetColour(color); - brush.SetColour(color); - memDC.SetPen(pen); - memDC.SetBrush(brush); - memDC.DrawRectangle(wxRect(icon_x, icon_y, Px_Square, Px_Square)); - - // draw icon interior - color.Set(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2], item_color_bytes[3]); - pen.SetColour(color); - brush.SetColour(color); - memDC.SetPen(pen); - memDC.SetBrush(brush); - memDC.DrawRectangle(wxRect(icon_x_inner, icon_y + 1, px_inner_square, px_inner_square)); - - // draw text - memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset); - - // update y - icon_y += icon_y_step; - } - - memDC.SelectObject(wxNullBitmap); - - // Convert the bitmap into a linear data ready to be loaded into the GPU. - { - wxImage image = bitmap.ConvertToImage(); - image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]); - - // prepare buffer - m_data.assign(4 * m_tex_width * m_tex_height, 0); - for (unsigned int h = 0; h < m_tex_height; ++h) - { - unsigned int hh = h * m_tex_width; - unsigned char* px_ptr = m_data.data() + 4 * hh; - for (unsigned int w = 0; w < m_tex_width; ++w) - { - *px_ptr++ = image.GetRed(w, h); - *px_ptr++ = image.GetGreen(w, h); - *px_ptr++ = image.GetBlue(w, h); - *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity; - } - } - } - return true; + return s_canvas_mgr.add(canvas); } -void _3DScene::_glew_init() -{ - glewInit(); +bool _3DScene::remove_canvas(wxGLCanvas* canvas) +{ + return s_canvas_mgr.remove(canvas); } -static inline int hex_digit_to_int(const char c) +void _3DScene::remove_all_canvases() { - return - (c >= '0' && c <= '9') ? int(c - '0') : - (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : - (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; + s_canvas_mgr.remove_all(); } -static inline std::vector<float> parse_colors(const std::vector<std::string> &scolors) +bool _3DScene::init(wxGLCanvas* canvas) { - std::vector<float> output(scolors.size() * 4, 1.f); - for (size_t i = 0; i < scolors.size(); ++ i) { - const std::string &scolor = scolors[i]; - const char *c = scolor.data() + 1; - if (scolor.size() == 7 && scolor.front() == '#') { - for (size_t j = 0; j < 3; ++j) { - int digit1 = hex_digit_to_int(*c ++); - int digit2 = hex_digit_to_int(*c ++); - if (digit1 == -1 || digit2 == -1) - break; - output[i * 4 + j] = float(digit1 * 16 + digit2) / 255.f; - } - } - } - return output; + return s_canvas_mgr.init(canvas); } -void _3DScene::load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector<std::string>& str_tool_colors, bool use_VBOs) +void _3DScene::set_as_dirty(wxGLCanvas* canvas) { - if ((preview_data == nullptr) || (volumes == nullptr)) - return; - - if (volumes->empty()) - { - std::vector<float> tool_colors = parse_colors(str_tool_colors); - - s_gcode_preview_volume_index.reset(); - - _load_gcode_extrusion_paths(*preview_data, *volumes, tool_colors, use_VBOs); - _load_gcode_travel_paths(*preview_data, *volumes, tool_colors, use_VBOs); - _load_gcode_retractions(*preview_data, *volumes, use_VBOs); - _load_gcode_unretractions(*preview_data, *volumes, use_VBOs); - - if (volumes->empty()) - reset_legend_texture(); - else - { - _generate_legend_texture(*preview_data, tool_colors); - _load_shells(*print, *volumes, use_VBOs); - } - } - - _update_gcode_volumes_visibility(*preview_data, *volumes); + s_canvas_mgr.set_as_dirty(canvas); } -unsigned int _3DScene::get_legend_texture_width() +unsigned int _3DScene::get_volumes_count(wxGLCanvas* canvas) { - return s_legend_texture.get_texture_width(); + return s_canvas_mgr.get_volumes_count(canvas); } -unsigned int _3DScene::get_legend_texture_height() +void _3DScene::reset_volumes(wxGLCanvas* canvas) { - return s_legend_texture.get_texture_height(); + s_canvas_mgr.reset_volumes(canvas); } -void _3DScene::reset_legend_texture() +void _3DScene::deselect_volumes(wxGLCanvas* canvas) { - s_legend_texture.reset_texture(); + s_canvas_mgr.deselect_volumes(canvas); } -unsigned int _3DScene::finalize_legend_texture() +void _3DScene::select_volume(wxGLCanvas* canvas, unsigned int id) { - return s_legend_texture.finalize(); + s_canvas_mgr.select_volume(canvas, id); } -unsigned int _3DScene::get_warning_texture_width() +void _3DScene::update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections) { - return s_warning_texture.get_texture_width(); + s_canvas_mgr.update_volumes_selection(canvas, selections); } -unsigned int _3DScene::get_warning_texture_height() +int _3DScene::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) { - return s_warning_texture.get_texture_height(); + return s_canvas_mgr.check_volumes_outside_state(canvas, config); } -void _3DScene::generate_warning_texture(const std::string& msg) +bool _3DScene::move_volume_up(wxGLCanvas* canvas, unsigned int id) { - s_warning_texture.generate(msg); + return s_canvas_mgr.move_volume_up(canvas, id); } -void _3DScene::reset_warning_texture() +bool _3DScene::move_volume_down(wxGLCanvas* canvas, unsigned int id) { - s_warning_texture.reset_texture(); + return s_canvas_mgr.move_volume_down(canvas, id); } -unsigned int _3DScene::finalize_warning_texture() +void _3DScene::set_objects_selections(wxGLCanvas* canvas, const std::vector<int>& selections) { - return s_warning_texture.finalize(); + s_canvas_mgr.set_objects_selections(canvas, selections); } -// Create 3D thick extrusion lines for a skirt and brim. -// Adds a new Slic3r::GUI::3DScene::Volume to volumes. -void _3DScene::_load_print_toolpaths( - const Print *print, - GLVolumeCollection *volumes, - const std::vector<std::string> &tool_colors, - bool use_VBOs) +void _3DScene::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) { - // The skirt and brim steps should be marked as done, so their paths are valid. - assert(print->is_step_done(psSkirt) && print->is_step_done(psBrim)); - - if (!print->has_skirt() && print->config().brim_width.value == 0) - return; - - const float color[] = { 0.5f, 1.0f, 0.5f, 1.f }; // greenish - - // number of skirt layers - size_t total_layer_count = 0; - for (const PrintObject *print_object : print->objects()) - total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); - size_t skirt_height = print->has_infinite_skirt() ? - total_layer_count : - std::min<size_t>(print->config().skirt_height.value, total_layer_count); - if (skirt_height == 0 && print->config().brim_width.value > 0) - skirt_height = 1; - - // get first skirt_height layers (maybe this should be moved to a PrintObject method?) - const PrintObject *object0 = print->objects().front(); - std::vector<float> print_zs; - print_zs.reserve(skirt_height * 2); - for (size_t i = 0; i < std::min(skirt_height, object0->layers().size()); ++ i) - print_zs.push_back(float(object0->layers()[i]->print_z)); - //FIXME why there are support layers? - for (size_t i = 0; i < std::min(skirt_height, object0->support_layers().size()); ++ i) - print_zs.push_back(float(object0->support_layers()[i]->print_z)); - sort_remove_duplicates(print_zs); - if (print_zs.size() > skirt_height) - print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); - - volumes->volumes.emplace_back(new GLVolume(color)); - GLVolume &volume = *volumes->volumes.back(); - for (size_t i = 0; i < skirt_height; ++ i) { - volume.print_zs.push_back(print_zs[i]); - volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size()); - volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size()); - if (i == 0) - extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), volume); - extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), volume); - } - volume.bounding_box = volume.indexed_vertex_array.bounding_box(); - volume.indexed_vertex_array.finalize_geometry(use_VBOs); + s_canvas_mgr.set_config(canvas, config); } -// Create 3D thick extrusion lines for object forming extrusions. -// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, -// one for perimeters, one for infill and one for supports. -void _3DScene::_load_print_object_toolpaths( - const PrintObject *print_object, - GLVolumeCollection *volumes, - const std::vector<std::string> &tool_colors_str, - bool use_VBOs) +void _3DScene::set_print(wxGLCanvas* canvas, Print* print) { - std::vector<float> tool_colors = parse_colors(tool_colors_str); - - struct Ctxt - { - const Points *shifted_copies; - std::vector<const Layer*> layers; - bool has_perimeters; - bool has_infill; - bool has_support; - const std::vector<float>* tool_colors; - - // Number of vertices (each vertex is 6x4=24 bytes long) - static const size_t alloc_size_max () { return 131072; } // 3.15MB -// static const size_t alloc_size_max () { return 65536; } // 1.57MB -// static const size_t alloc_size_max () { return 32768; } // 786kB - static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } - - static const float* color_perimeters () { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow - static const float* color_infill () { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish - static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish - - // For cloring by a tool, return a parsed color. - bool color_by_tool() const { return tool_colors != nullptr; } - size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } - const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } - int volume_idx(int extruder, int feature) const - { return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(extruder - 1, 0)) : feature; } - } ctxt; - - ctxt.shifted_copies = &print_object->_shifted_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); - 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. - size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1)); - tbb::spin_mutex new_volume_mutex; - auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { - auto *volume = new GLVolume(color); - new_volume_mutex.lock(); - volume->outside_printer_detection_enabled = false; - volumes->volumes.emplace_back(volume); - new_volume_mutex.unlock(); - return volume; - }; - const size_t volumes_cnt_initial = volumes->volumes.size(); - std::vector<GLVolumeCollection> volumes_per_thread(ctxt.layers.size()); - tbb::parallel_for( - tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size), - [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) { - std::vector<GLVolume*> vols; - if (ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++ i) - vols.emplace_back(new_volume(ctxt.color_tool(i))); - } else - vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; - for (GLVolume *vol : vols) - vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - const Layer *layer = ctxt.layers[idx_layer]; - for (size_t i = 0; i < vols.size(); ++ i) { - GLVolume &vol = *vols[i]; - if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) { - vol.print_zs.push_back(layer->print_z); - vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); - vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); - } - } - for (const Point ©: *ctxt.shifted_copies) { - for (const LayerRegion *layerm : layer->regions()) { - if (ctxt.has_perimeters) - extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, - *vols[ctxt.volume_idx(layerm->region()->config().perimeter_extruder.value, 0)]); - if (ctxt.has_infill) { - for (const ExtrusionEntity *ee : layerm->fills.entities) { - // fill represents infill extrusions of a single island. - const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); - if (! fill->entities.empty()) - extrusionentity_to_verts(*fill, float(layer->print_z), copy, - *vols[ctxt.volume_idx( - is_solid_infill(fill->entities.front()->role()) ? - layerm->region()->config().solid_infill_extruder : - layerm->region()->config().infill_extruder, - 1)]); - } - } - } - if (ctxt.has_support) { - const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer); - if (support_layer) { - for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) - extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, - *vols[ctxt.volume_idx( - (extrusion_entity->role() == erSupportMaterial) ? - support_layer->object()->config().support_material_extruder : - support_layer->object()->config().support_material_interface_extruder, - 2)]); - } - } - } - for (size_t i = 0; i < vols.size(); ++ i) { - GLVolume &vol = *vols[i]; - if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { - // Store the vertex arrays and restart their containers, - vols[i] = new_volume(vol.color); - GLVolume &vol_new = *vols[i]; - // Assign the large pre-allocated buffers to the new GLVolume. - vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); - // Copy the content back to the old GLVolume. - vol.indexed_vertex_array = vol_new.indexed_vertex_array; - // Finalize a bounding box of the old GLVolume. - vol.bounding_box = vol.indexed_vertex_array.bounding_box(); - // Clear the buffers, but keep them pre-allocated. - vol_new.indexed_vertex_array.clear(); - // Just make sure that clear did not clear the reserved memory. - vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - } - } - } - for (GLVolume *vol : vols) { - vol->bounding_box = vol->indexed_vertex_array.bounding_box(); - vol->indexed_vertex_array.shrink_to_fit(); - } - }); - - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results"; - // Remove empty volumes from the newly added volumes. - volumes->volumes.erase( - std::remove_if(volumes->volumes.begin() + volumes_cnt_initial, volumes->volumes.end(), - [](const GLVolume *volume) { return volume->empty(); }), - volumes->volumes.end()); - for (size_t i = volumes_cnt_initial; i < volumes->volumes.size(); ++ i) - volumes->volumes[i]->indexed_vertex_array.finalize_geometry(use_VBOs); - - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end"; -} - -void _3DScene::_load_wipe_tower_toolpaths( - const Print *print, - GLVolumeCollection *volumes, - const std::vector<std::string> &tool_colors_str, - bool use_VBOs) -{ - if (print->wipe_tower_data().tool_changes.empty()) - return; - - std::vector<float> tool_colors = parse_colors(tool_colors_str); - - struct Ctxt - { - const Print *print; - const std::vector<float> *tool_colors; - - // Number of vertices (each vertex is 6x4=24 bytes long) - static const size_t alloc_size_max () { return 131072; } // 3.15MB - static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } - - static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish - - // For cloring by a tool, return a parsed color. - bool color_by_tool() const { return tool_colors != nullptr; } - size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } - const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } - int volume_idx(int tool, int feature) const - { return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(tool, 0)) : feature; } - - const std::vector<WipeTower::ToolChangeResult>& tool_change(size_t idx) { - return priming.empty() ? - ((idx == print->wipe_tower_data().tool_changes.size()) ? final : print->wipe_tower_data().tool_changes[idx]) : - ((idx == 0) ? priming : (idx == print->wipe_tower_data().tool_changes.size() + 1) ? final : print->wipe_tower_data().tool_changes[idx - 1]); - } - std::vector<WipeTower::ToolChangeResult> priming; - std::vector<WipeTower::ToolChangeResult> final; - } ctxt; - - ctxt.print = print; - ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; - if (print->wipe_tower_data().priming != nullptr) - ctxt.priming.emplace_back(*print->wipe_tower_data().priming); - if (print->wipe_tower_data().final_purge) - ctxt.final.emplace_back(*print->wipe_tower_data().final_purge); - - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start"; - - //FIXME Improve the heuristics for a grain size. - size_t n_items = print->wipe_tower_data().tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); - size_t grain_size = std::max(n_items / 128, size_t(1)); - tbb::spin_mutex new_volume_mutex; - auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { - auto *volume = new GLVolume(color); - new_volume_mutex.lock(); - volume->outside_printer_detection_enabled = false; - volumes->volumes.emplace_back(volume); - new_volume_mutex.unlock(); - return volume; - }; - const size_t volumes_cnt_initial = volumes->volumes.size(); - std::vector<GLVolumeCollection> volumes_per_thread(n_items); - tbb::parallel_for( - tbb::blocked_range<size_t>(0, n_items, grain_size), - [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) { - // Bounding box of this slab of a wipe tower. - std::vector<GLVolume*> vols; - if (ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++ i) - vols.emplace_back(new_volume(ctxt.color_tool(i))); - } else - vols = { new_volume(ctxt.color_support()) }; - for (GLVolume *volume : vols) - volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { - const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer); - for (size_t i = 0; i < vols.size(); ++ i) { - GLVolume &vol = *vols[i]; - if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) { - vol.print_zs.push_back(layer.front().print_z); - vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); - vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); - } - } - for (const WipeTower::ToolChangeResult &extrusions : layer) { - for (size_t i = 1; i < extrusions.extrusions.size();) { - const WipeTower::Extrusion &e = extrusions.extrusions[i]; - if (e.width == 0.) { - ++ i; - continue; - } - size_t j = i + 1; - if (ctxt.color_by_tool()) - for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++ j) ; - else - for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++ j) ; - size_t n_lines = j - i; - Lines lines; - std::vector<double> widths; - std::vector<double> heights; - lines.reserve(n_lines); - widths.reserve(n_lines); - heights.assign(n_lines, extrusions.layer_height); - for (; i < j; ++ i) { - const WipeTower::Extrusion &e = extrusions.extrusions[i]; - assert(e.width > 0.f); - const WipeTower::Extrusion &e_prev = *(&e - 1); - lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y)); - widths.emplace_back(e.width); - } - thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, - *vols[ctxt.volume_idx(e.tool, 0)]); - } - } - } - for (size_t i = 0; i < vols.size(); ++ i) { - GLVolume &vol = *vols[i]; - if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { - // Store the vertex arrays and restart their containers, - vols[i] = new_volume(vol.color); - GLVolume &vol_new = *vols[i]; - // Assign the large pre-allocated buffers to the new GLVolume. - vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); - // Copy the content back to the old GLVolume. - vol.indexed_vertex_array = vol_new.indexed_vertex_array; - // Finalize a bounding box of the old GLVolume. - vol.bounding_box = vol.indexed_vertex_array.bounding_box(); - // Clear the buffers, but keep them pre-allocated. - vol_new.indexed_vertex_array.clear(); - // Just make sure that clear did not clear the reserved memory. - vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - } - } - for (GLVolume *vol : vols) { - vol->bounding_box = vol->indexed_vertex_array.bounding_box(); - vol->indexed_vertex_array.shrink_to_fit(); - } - }); - - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results"; - // Remove empty volumes from the newly added volumes. - volumes->volumes.erase( - std::remove_if(volumes->volumes.begin() + volumes_cnt_initial, volumes->volumes.end(), - [](const GLVolume *volume) { return volume->empty(); }), - volumes->volumes.end()); - for (size_t i = volumes_cnt_initial; i < volumes->volumes.size(); ++ i) - volumes->volumes[i]->indexed_vertex_array.finalize_geometry(use_VBOs); - - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end"; + s_canvas_mgr.set_print(canvas, print); } -void _3DScene::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector<float>& tool_colors, bool use_VBOs) +void _3DScene::set_model(wxGLCanvas* canvas, Model* model) { - // helper functions to select data in dependence of the extrusion view type - struct Helper - { - static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path) - { - switch (type) - { - case GCodePreviewData::Extrusion::FeatureType: - return (float)path.role(); - case GCodePreviewData::Extrusion::Height: - return path.height; - case GCodePreviewData::Extrusion::Width: - return path.width; - case GCodePreviewData::Extrusion::Feedrate: - return path.feedrate; - case GCodePreviewData::Extrusion::Tool: - return (float)path.extruder_id; - } - - return 0.0f; - } - - static const GCodePreviewData::Color& path_color(const GCodePreviewData& data, const std::vector<float>& tool_colors, float value) - { - switch (data.extrusion.view_type) - { - case GCodePreviewData::Extrusion::FeatureType: - return data.get_extrusion_role_color((ExtrusionRole)(int)value); - case GCodePreviewData::Extrusion::Height: - return data.get_extrusion_height_color(value); - case GCodePreviewData::Extrusion::Width: - return data.get_extrusion_width_color(value); - case GCodePreviewData::Extrusion::Feedrate: - return data.get_extrusion_feedrate_color(value); - case GCodePreviewData::Extrusion::Tool: - { - static GCodePreviewData::Color color; - ::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float)); - return color; - } - } + s_canvas_mgr.set_model(canvas, model); +} - return GCodePreviewData::Color::Dummy; - } - }; +void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) +{ + s_canvas_mgr.set_bed_shape(canvas, shape); +} - // Helper structure for filters - struct Filter - { - float value; - ExtrusionRole role; - GLVolume* volume; - - Filter(float value, ExtrusionRole role) - : value(value) - , role(role) - , volume(nullptr) - { - } +void _3DScene::set_auto_bed_shape(wxGLCanvas* canvas) +{ + s_canvas_mgr.set_auto_bed_shape(canvas); +} - bool operator == (const Filter& other) const - { - if (value != other.value) - return false; +BoundingBoxf3 _3DScene::get_volumes_bounding_box(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_volumes_bounding_box(canvas); +} - if (role != other.role) - return false; +void _3DScene::set_axes_length(wxGLCanvas* canvas, float length) +{ + s_canvas_mgr.set_axes_length(canvas, length); +} - return true; - } - }; +void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons) +{ + s_canvas_mgr.set_cutting_plane(canvas, z, polygons); +} - typedef std::vector<Filter> FiltersList; +void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value) +{ + s_canvas_mgr.set_color_by(canvas, value); +} - size_t initial_volumes_count = volumes.volumes.size(); +void _3DScene::set_select_by(wxGLCanvas* canvas, const std::string& value) +{ + s_canvas_mgr.set_select_by(canvas, value); +} - // detects filters - FiltersList filters; - for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) - { - for (const ExtrusionPath& path : layer.paths) - { - ExtrusionRole role = path.role(); - float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); - if (std::find(filters.begin(), filters.end(), Filter(path_filter, role)) == filters.end()) - filters.emplace_back(path_filter, role); - } - } +void _3DScene::set_drag_by(wxGLCanvas* canvas, const std::string& value) +{ + s_canvas_mgr.set_drag_by(canvas, value); +} - // nothing to render, return - if (filters.empty()) - return; +std::string _3DScene::get_select_by(wxGLCanvas* canvas) +{ + return s_canvas_mgr.get_select_by(canvas); +} - // creates a new volume for each filter - for (Filter& filter : filters) - { - s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)volumes.volumes.size()); +bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_layers_editing_enabled(canvas); +} - GLVolume* volume = new GLVolume(Helper::path_color(preview_data, tool_colors, filter.value).rgba); - if (volume != nullptr) - { - filter.volume = volume; - volumes.volumes.emplace_back(volume); - } - else - { - // an error occourred - restore to previous state and return - s_gcode_preview_volume_index.first_volumes.pop_back(); - if (initial_volumes_count != volumes.volumes.size()) - { - std::vector<GLVolume*>::iterator begin = volumes.volumes.begin() + initial_volumes_count; - std::vector<GLVolume*>::iterator end = volumes.volumes.end(); - for (std::vector<GLVolume*>::iterator it = begin; it < end; ++it) - { - GLVolume* volume = *it; - delete volume; - } - volumes.volumes.erase(begin, end); - return; - } - } - } +bool _3DScene::is_layers_editing_allowed(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_layers_editing_allowed(canvas); +} - // populates volumes - for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) - { - for (const ExtrusionPath& path : layer.paths) - { - float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); - FiltersList::iterator filter = std::find(filters.begin(), filters.end(), Filter(path_filter, path.role())); - if (filter != filters.end()) - { - filter->volume->print_zs.push_back(layer.z); - filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.quad_indices.size()); - filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.triangle_indices.size()); +bool _3DScene::is_shader_enabled(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_shader_enabled(canvas); +} - extrusionentity_to_verts(path, layer.z, *filter->volume); - } - } - } +bool _3DScene::is_reload_delayed(wxGLCanvas* canvas) +{ + return s_canvas_mgr.is_reload_delayed(canvas); +} - // finalize volumes and sends geometry to gpu - if (volumes.volumes.size() > initial_volumes_count) - { - for (size_t i = initial_volumes_count; i < volumes.volumes.size(); ++i) - { - GLVolume* volume = volumes.volumes[i]; - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(use_VBOs); - } - } +void _3DScene::enable_layers_editing(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_layers_editing(canvas, enable); } -void _3DScene::_load_gcode_travel_paths(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector<float>& tool_colors, bool use_VBOs) +void _3DScene::enable_warning_texture(wxGLCanvas* canvas, bool enable) { - size_t initial_volumes_count = volumes.volumes.size(); - s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count); + s_canvas_mgr.enable_warning_texture(canvas, enable); +} - bool res = true; - switch (preview_data.extrusion.view_type) - { - case GCodePreviewData::Extrusion::Feedrate: - { - res = _travel_paths_by_feedrate(preview_data, volumes); - break; - } - case GCodePreviewData::Extrusion::Tool: - { - res = _travel_paths_by_tool(preview_data, volumes, tool_colors); - break; - } - default: - { - res = _travel_paths_by_type(preview_data, volumes); - break; - } - } +void _3DScene::enable_legend_texture(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_legend_texture(canvas, enable); +} - if (!res) - { - // an error occourred - restore to previous state and return - if (initial_volumes_count != volumes.volumes.size()) - { - std::vector<GLVolume*>::iterator begin = volumes.volumes.begin() + initial_volumes_count; - std::vector<GLVolume*>::iterator end = volumes.volumes.end(); - for (std::vector<GLVolume*>::iterator it = begin; it < end; ++it) - { - GLVolume* volume = *it; - delete volume; - } - volumes.volumes.erase(begin, end); - } +void _3DScene::enable_picking(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_picking(canvas, enable); +} - return; - } +void _3DScene::enable_moving(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_moving(canvas, enable); +} - // finalize volumes and sends geometry to gpu - if (volumes.volumes.size() > initial_volumes_count) - { - for (size_t i = initial_volumes_count; i < volumes.volumes.size(); ++i) - { - GLVolume* volume = volumes.volumes[i]; - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(use_VBOs); - } - } +void _3DScene::enable_gizmos(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_gizmos(canvas, enable); } -bool _3DScene::_travel_paths_by_type(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) +void _3DScene::enable_toolbar(wxGLCanvas* canvas, bool enable) { - // Helper structure for types - struct Type - { - GCodePreviewData::Travel::EType value; - GLVolume* volume; + s_canvas_mgr.enable_toolbar(canvas, enable); +} - explicit Type(GCodePreviewData::Travel::EType value) - : value(value) - , volume(nullptr) - { - } +void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_shader(canvas, enable); +} - bool operator == (const Type& other) const - { - return value == other.value; - } - }; +void _3DScene::enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_force_zoom_to_bed(canvas, enable); +} - typedef std::vector<Type> TypesList; +void _3DScene::enable_dynamic_background(wxGLCanvas* canvas, bool enable) +{ + s_canvas_mgr.enable_dynamic_background(canvas, enable); +} - // colors travels by travel type +void _3DScene::allow_multisample(wxGLCanvas* canvas, bool allow) +{ + s_canvas_mgr.allow_multisample(canvas, allow); +} - // detects types - TypesList types; - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - if (std::find(types.begin(), types.end(), Type(polyline.type)) == types.end()) - types.emplace_back(polyline.type); - } +void _3DScene::enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable) +{ + s_canvas_mgr.enable_toolbar_item(canvas, name, enable); +} - // nothing to render, return - if (types.empty()) - return true; +bool _3DScene::is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name) +{ + return s_canvas_mgr.is_toolbar_item_pressed(canvas, name); +} - // creates a new volume for each type - for (Type& type : types) - { - GLVolume* volume = new GLVolume(preview_data.travel.type_colors[type.value].rgba); - if (volume == nullptr) - return false; - else - { - type.volume = volume; - volumes.volumes.emplace_back(volume); - } - } +void _3DScene::zoom_to_bed(wxGLCanvas* canvas) +{ + s_canvas_mgr.zoom_to_bed(canvas); +} - // populates volumes - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type)); - if (type != types.end()) - { - type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().max.z)); - type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size()); - type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size()); +void _3DScene::zoom_to_volumes(wxGLCanvas* canvas) +{ + s_canvas_mgr.zoom_to_volumes(canvas); +} - polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *type->volume); - } - } +void _3DScene::select_view(wxGLCanvas* canvas, const std::string& direction) +{ + s_canvas_mgr.select_view(canvas, direction); +} - return true; +void _3DScene::set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other) +{ + s_canvas_mgr.set_viewport_from_scene(canvas, other); } -bool _3DScene::_travel_paths_by_feedrate(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) +void _3DScene::update_volumes_colors_by_extruder(wxGLCanvas* canvas) { - // Helper structure for feedrate - struct Feedrate - { - float value; - GLVolume* volume; + s_canvas_mgr.update_volumes_colors_by_extruder(canvas); +} - explicit Feedrate(float value) - : value(value) - , volume(nullptr) - { - } +void _3DScene::update_gizmos_data(wxGLCanvas* canvas) +{ + s_canvas_mgr.update_gizmos_data(canvas); +} - bool operator == (const Feedrate& other) const - { - return value == other.value; - } - }; +void _3DScene::render(wxGLCanvas* canvas) +{ + s_canvas_mgr.render(canvas); +} - typedef std::vector<Feedrate> FeedratesList; +std::vector<double> _3DScene::get_current_print_zs(wxGLCanvas* canvas, bool active_only) +{ + return s_canvas_mgr.get_current_print_zs(canvas, active_only); +} - // colors travels by feedrate +void _3DScene::set_toolpaths_range(wxGLCanvas* canvas, double low, double high) +{ + s_canvas_mgr.set_toolpaths_range(canvas, low, high); +} - // detects feedrates - FeedratesList feedrates; - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - if (std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)) == feedrates.end()) - feedrates.emplace_back(polyline.feedrate); - } +void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); +} - // nothing to render, return - if (feedrates.empty()) - return true; +void _3DScene::register_on_double_click_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_double_click_callback(canvas, callback); +} - // creates a new volume for each feedrate - for (Feedrate& feedrate : feedrates) - { - GLVolume* volume = new GLVolume(preview_data.get_extrusion_feedrate_color(feedrate.value).rgba); - if (volume == nullptr) - return false; - else - { - feedrate.volume = volume; - volumes.volumes.emplace_back(volume); - } - } +void _3DScene::register_on_right_click_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_right_click_callback(canvas, callback); +} - // populates volumes - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)); - if (feedrate != feedrates.end()) - { - feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().max.z)); - feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size()); - feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size()); +void _3DScene::register_on_select_object_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_select_object_callback(canvas, callback); +} - polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *feedrate->volume); - } - } +void _3DScene::register_on_model_update_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_model_update_callback(canvas, callback); +} - return true; +void _3DScene::register_on_remove_object_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_remove_object_callback(canvas, callback); } -bool _3DScene::_travel_paths_by_tool(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, const std::vector<float>& tool_colors) +void _3DScene::register_on_arrange_callback(wxGLCanvas* canvas, void* callback) { - // Helper structure for tool - struct Tool - { - unsigned int value; - GLVolume* volume; + s_canvas_mgr.register_on_arrange_callback(canvas, callback); +} - explicit Tool(unsigned int value) - : value(value) - , volume(nullptr) - { - } +void _3DScene::register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_rotate_object_left_callback(canvas, callback); +} - bool operator == (const Tool& other) const - { - return value == other.value; - } - }; +void _3DScene::register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_rotate_object_right_callback(canvas, callback); +} - typedef std::vector<Tool> ToolsList; +void _3DScene::register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_scale_object_uniformly_callback(canvas, callback); +} - // colors travels by tool +void _3DScene::register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_increase_objects_callback(canvas, callback); +} - // detects tools - ToolsList tools; - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - if (std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)) == tools.end()) - tools.emplace_back(polyline.extruder_id); - } +void _3DScene::register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_decrease_objects_callback(canvas, callback); +} - // nothing to render, return - if (tools.empty()) - return true; +void _3DScene::register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_instance_moved_callback(canvas, callback); +} - // creates a new volume for each tool - for (Tool& tool : tools) - { - GLVolume* volume = new GLVolume(tool_colors.data() + tool.value * 4); - if (volume == nullptr) - return false; - else - { - tool.volume = volume; - volumes.volumes.emplace_back(volume); - } - } +void _3DScene::register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_wipe_tower_moved_callback(canvas, callback); +} - // populates volumes - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)); - if (tool != tools.end()) - { - tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().max.z)); - tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size()); - tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size()); +void _3DScene::register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_enable_action_buttons_callback(canvas, callback); +} - polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *tool->volume); - } - } +void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_gizmo_scale_uniformly_callback(canvas, callback); +} - return true; +void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback); } -void _3DScene::_load_gcode_retractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs) +void _3DScene::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback) { - s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)volumes.volumes.size()); + s_canvas_mgr.register_on_gizmo_flatten_callback(canvas, callback); +} - // nothing to render, return - if (preview_data.retraction.positions.empty()) - return; +void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_on_update_geometry_info_callback(canvas, callback); +} - GLVolume* volume = new GLVolume(preview_data.retraction.color.rgba); - if (volume != nullptr) - { - volumes.volumes.emplace_back(volume); +void _3DScene::register_action_add_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_action_add_callback(canvas, callback); +} - for (const GCodePreviewData::Retraction::Position& position : preview_data.retraction.positions) - { - volume->print_zs.push_back(unscale(position.position.z)); - volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); - volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); +void _3DScene::register_action_delete_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_action_delete_callback(canvas, callback); +} - point3_to_verts(position.position, position.width, position.height, *volume); - } +void _3DScene::register_action_deleteall_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_action_deleteall_callback(canvas, callback); +} - // finalize volumes and sends geometry to gpu - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(use_VBOs); - } +void _3DScene::register_action_arrange_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_action_arrange_callback(canvas, callback); } -void _3DScene::_load_gcode_unretractions(const GCodePreviewData& preview_data, GLVolumeCollection& volumes, bool use_VBOs) +void _3DScene::register_action_more_callback(wxGLCanvas* canvas, void* callback) { - s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)volumes.volumes.size()); + s_canvas_mgr.register_action_more_callback(canvas, callback); +} - // nothing to render, return - if (preview_data.unretraction.positions.empty()) - return; +void _3DScene::register_action_fewer_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_action_fewer_callback(canvas, callback); +} - GLVolume* volume = new GLVolume(preview_data.unretraction.color.rgba); - if (volume != nullptr) - { - volumes.volumes.emplace_back(volume); +void _3DScene::register_action_split_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_action_split_callback(canvas, callback); +} - for (const GCodePreviewData::Retraction::Position& position : preview_data.unretraction.positions) - { - volume->print_zs.push_back(unscale(position.position.z)); - volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); - volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); +void _3DScene::register_action_cut_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_action_cut_callback(canvas, callback); +} - point3_to_verts(position.position, position.width, position.height, *volume); - } +void _3DScene::register_action_settings_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_action_settings_callback(canvas, callback); +} - // finalize volumes and sends geometry to gpu - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(use_VBOs); - } +void _3DScene::register_action_layersediting_callback(wxGLCanvas* canvas, void* callback) +{ + s_canvas_mgr.register_action_layersediting_callback(canvas, callback); } -void _3DScene::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data, GLVolumeCollection& volumes) +void _3DScene::register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback) { - unsigned int size = (unsigned int)s_gcode_preview_volume_index.first_volumes.size(); - for (unsigned int i = 0; i < size; ++i) - { - std::vector<GLVolume*>::iterator begin = volumes.volumes.begin() + s_gcode_preview_volume_index.first_volumes[i].id; - std::vector<GLVolume*>::iterator end = (i + 1 < size) ? volumes.volumes.begin() + s_gcode_preview_volume_index.first_volumes[i + 1].id : volumes.volumes.end(); + s_canvas_mgr.register_action_selectbyparts_callback(canvas, callback); +} - for (std::vector<GLVolume*>::iterator it = begin; it != end; ++it) - { - GLVolume* volume = *it; - volume->outside_printer_detection_enabled = false; +static inline int hex_digit_to_int(const char c) +{ + return + (c >= '0' && c <= '9') ? int(c - '0') : + (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : + (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; +} - switch (s_gcode_preview_volume_index.first_volumes[i].type) - { - case GCodePreviewVolumeIndex::Extrusion: - { - if ((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag == erCustom) - volume->zoom_to_volumes = false; - - volume->is_active = preview_data.extrusion.is_role_flag_set((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag); - break; - } - case GCodePreviewVolumeIndex::Travel: - { - volume->is_active = preview_data.travel.is_visible; - volume->zoom_to_volumes = false; - break; - } - case GCodePreviewVolumeIndex::Retraction: - { - volume->is_active = preview_data.retraction.is_visible; - volume->zoom_to_volumes = false; - break; - } - case GCodePreviewVolumeIndex::Unretraction: - { - volume->is_active = preview_data.unretraction.is_visible; - volume->zoom_to_volumes = false; - break; - } - case GCodePreviewVolumeIndex::Shell: - { - volume->is_active = preview_data.shell.is_visible; - volume->color[3] = 0.25f; - volume->zoom_to_volumes = false; - break; - } - default: - { - volume->is_active = false; - volume->zoom_to_volumes = false; +static inline std::vector<float> parse_colors(const std::vector<std::string> &scolors) +{ + std::vector<float> output(scolors.size() * 4, 1.f); + for (size_t i = 0; i < scolors.size(); ++ i) { + const std::string &scolor = scolors[i]; + const char *c = scolor.data() + 1; + if (scolor.size() == 7 && scolor.front() == '#') { + for (size_t j = 0; j < 3; ++j) { + int digit1 = hex_digit_to_int(*c ++); + int digit2 = hex_digit_to_int(*c ++); + if (digit1 == -1 || digit2 == -1) break; - } + output[i * 4 + j] = float(digit1 * 16 + digit2) / 255.f; } } } + return output; } -void _3DScene::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors) +std::vector<int> _3DScene::load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs) { - s_legend_texture.generate(preview_data, tool_colors); + return s_canvas_mgr.load_object(canvas, model_object, obj_idx, instance_idxs); } -void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs) +std::vector<int> _3DScene::load_object(wxGLCanvas* canvas, const Model* model, int obj_idx) { - size_t initial_volumes_count = volumes.volumes.size(); - s_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count); - - if (print.objects().empty()) - // nothing to render, return - return; - - // adds objects' volumes - unsigned int object_id = 0; - for (PrintObject* obj : print.objects()) - { - ModelObject* model_obj = obj->model_object(); + return s_canvas_mgr.load_object(canvas, model, obj_idx); +} - std::vector<int> instance_ids(model_obj->instances.size()); - for (int i = 0; i < model_obj->instances.size(); ++i) - { - instance_ids[i] = i; - } +int _3DScene::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) +{ + return s_canvas_mgr.get_first_volume_id(canvas, obj_idx); +} - for (ModelInstance* instance : model_obj->instances) - { - volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", use_VBOs); - } +void _3DScene::reload_scene(wxGLCanvas* canvas, bool force) +{ + s_canvas_mgr.reload_scene(canvas, force); +} - ++object_id; - } +void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors) +{ + s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors); +} - // adds wipe tower's volume - coordf_t max_z = print.objects().front()->model_object()->get_model()->bounding_box().max.z; - const PrintConfig& config = print.config(); - unsigned int extruders_count = config.nozzle_diameter.size(); - if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) - volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, config.wipe_tower_per_color_wipe * (extruders_count - 1), max_z, use_VBOs); +void _3DScene::load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors) +{ + s_canvas_mgr.load_preview(canvas, str_tool_colors); } +void _3DScene::reset_legend_texture() +{ + s_canvas_mgr.reset_legend_texture(); } + +} // namespace Slic3r |