diff --git a/src/libslic3r/BuildVolume.hpp b/src/libslic3r/BuildVolume.hpp index 6b928d48b..be8d224c3 100644 --- a/src/libslic3r/BuildVolume.hpp +++ b/src/libslic3r/BuildVolume.hpp @@ -94,6 +94,12 @@ public: // Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices. bool all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& bbox, bool ignore_bottom = true) const; + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_scene() const { return m_top_bottom_convex_hull_decomposition_scene; } + const std::pair<std::vector<Vec2d>, std::vector<Vec2d>>& top_bottom_convex_hull_decomposition_bed() const { return m_top_bottom_convex_hull_decomposition_bed; } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + private: // Source definition of the print bed geometry (PrintConfig::bed_shape) std::vector<Vec2d> m_bed_shape; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 23ca974dd..da09a236f 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1,10 +1,12 @@ #include <GL/glew.h> +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #if ENABLE_SMOOTH_NORMALS #include <igl/per_face_normals.h> #include <igl/per_corner_normals.h> #include <igl/per_vertex_normals.h> #endif // ENABLE_SMOOTH_NORMALS +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #include "3DScene.hpp" #include "GLShader.hpp" @@ -69,6 +71,7 @@ void glAssertRecentCallImpl(const char* file_name, unsigned int line, const char namespace Slic3r { +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #if ENABLE_SMOOTH_NORMALS static void smooth_normals_corner(TriangleMesh& mesh, std::vector<stl_normal>& normals) { @@ -287,6 +290,7 @@ void GLIndexedVertexArray::render( glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL const float GLVolume::SinkingContours::HalfWidth = 0.25f; @@ -483,7 +487,9 @@ GLVolume::GLVolume(float r, float g, float b, float a) , force_neutral_color(false) , force_sinking_contours(false) , tverts_range(0, size_t(-1)) +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL , qverts_range(0, size_t(-1)) +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { color = { r, g, b, a }; set_render_color(color); @@ -599,6 +605,36 @@ const BoundingBoxf3& GLVolume::transformed_non_sinking_bounding_box() const return *m_transformed_non_sinking_bounding_box; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GLVolume::set_range(double min_z, double max_z) +{ + this->tverts_range.first = 0; + this->tverts_range.second = this->model.indices_count(); + + if (!this->print_zs.empty()) { + // The Z layer range is specified. + // First test whether the Z span of this object is not out of (min_z, max_z) completely. + if (this->print_zs.front() > max_z || this->print_zs.back() < min_z) + this->tverts_range.second = 0; + else { + // Then find the lowest layer to be displayed. + size_t i = 0; + for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++i); + if (i == this->print_zs.size()) + // This shall not happen. + this->tverts_range.second = 0; + else { + // Remember start of the layer. + this->tverts_range.first = this->offsets[i]; + // Some layers are above $min_z. Which? + for (; i < this->print_zs.size() && this->print_zs[i] <= max_z; ++i); + if (i < this->print_zs.size()) + this->tverts_range.second = this->offsets[i]; + } + } + } +} +#else void GLVolume::set_range(double min_z, double max_z) { this->qverts_range.first = 0; @@ -611,7 +647,8 @@ void GLVolume::set_range(double min_z, double max_z) if (this->print_zs.front() > max_z || this->print_zs.back() < min_z) { this->qverts_range.second = 0; this->tverts_range.second = 0; - } else { + } + else { // Then find the lowest layer to be displayed. size_t i = 0; for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++ i); @@ -619,7 +656,8 @@ void GLVolume::set_range(double min_z, double max_z) // This shall not happen. this->qverts_range.second = 0; this->tverts_range.second = 0; - } else { + } + else { // Remember start of the layer. this->qverts_range.first = this->offsets[i * 2]; this->tverts_range.first = this->offsets[i * 2 + 1]; @@ -633,8 +671,9 @@ void GLVolume::set_range(double min_z, double max_z) } } } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL -void GLVolume::render() const +void GLVolume::render() { if (!is_active) return; @@ -645,7 +684,14 @@ void GLVolume::render() const glsafe(::glPushMatrix()); glsafe(::glMultMatrixd(world_matrix().data())); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (tverts_range == std::make_pair<size_t, size_t>(0, -1)) + model.render(); + else + model.render(this->tverts_range); +#else this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL glsafe(::glPopMatrix()); if (this->is_left_handed()) @@ -680,25 +726,44 @@ void GLVolume::render_non_manifold_edges() } #endif // ENABLE_SHOW_NON_MANIFOLD_EDGES +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +std::vector<int> GLVolumeCollection::load_object( + const ModelObject* model_object, + int obj_idx, + const std::vector<int>& instance_idxs) +#else std::vector<int> GLVolumeCollection::load_object( const ModelObject *model_object, int obj_idx, const std::vector<int> &instance_idxs, bool opengl_initialized) +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { std::vector<int> volumes_idx; for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx) for (int instance_idx : instance_idxs) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx)); +#else volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, opengl_initialized)); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL return volumes_idx; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +int GLVolumeCollection::load_object_volume( + const ModelObject* model_object, + int obj_idx, + int volume_idx, + int instance_idx) +#else int GLVolumeCollection::load_object_volume( const ModelObject *model_object, int obj_idx, int volume_idx, int instance_idx, bool opengl_initialized) +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { const ModelVolume *model_volume = model_object->volumes[volume_idx]; const int extruder_id = model_volume->extruder_id(); @@ -706,6 +771,14 @@ int GLVolumeCollection::load_object_volume( const TriangleMesh &mesh = model_volume->mesh(); this->volumes.emplace_back(new GLVolume()); GLVolume& v = *this->volumes.back(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_SMOOTH_NORMALS + v.model.init_from(mesh, true); +#else + v.model.init_from(mesh); +#endif // ENABLE_SMOOTH_NORMALS + v.model.set_color(color_from_model_volume(*model_volume)); +#else v.set_color(color_from_model_volume(*model_volume)); #if ENABLE_SMOOTH_NORMALS v.indexed_vertex_array.load_mesh(mesh, true); @@ -713,9 +786,9 @@ int GLVolumeCollection::load_object_volume( v.indexed_vertex_array.load_mesh(mesh); #endif // ENABLE_SMOOTH_NORMALS v.indexed_vertex_array.finalize_geometry(opengl_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx); - if (model_volume->is_model_part()) - { + if (model_volume->is_model_part()) { // GLVolume will reference a convex hull from model_volume! v.set_convex_hull(model_volume->get_convex_hull_shared_ptr()); if (extruder_id != -1) @@ -732,6 +805,16 @@ int GLVolumeCollection::load_object_volume( // Load SLA auxiliary GLVolumes (for support trees or pad). // This function produces volumes for multiple instances in a single shot, // as some object specific mesh conversions may be expensive. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GLVolumeCollection::load_object_auxiliary( + const SLAPrintObject* print_object, + int obj_idx, + // pairs of <instance_idx, print_instance_idx> + const std::vector<std::pair<size_t, size_t>>& instances, + SLAPrintObjectStep milestone, + // Timestamp of the last change of the milestone + size_t timestamp) +#else void GLVolumeCollection::load_object_auxiliary( const SLAPrintObject *print_object, int obj_idx, @@ -741,6 +824,7 @@ void GLVolumeCollection::load_object_auxiliary( // Timestamp of the last change of the milestone size_t timestamp, bool opengl_initialized) +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { assert(print_object->is_step_done(milestone)); Transform3d mesh_trafo_inv = print_object->trafo().inverse(); @@ -753,12 +837,21 @@ void GLVolumeCollection::load_object_auxiliary( const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; this->volumes.emplace_back(new GLVolume((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); GLVolume& v = *this->volumes.back(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_SMOOTH_NORMALS + v.model.init_from(mesh, true); +#else + v.model.init_from(mesh); +#endif // ENABLE_SMOOTH_NORMALS + v.model.set_color((milestone == slaposPad) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR); +#else #if ENABLE_SMOOTH_NORMALS v.indexed_vertex_array.load_mesh(mesh, true); #else v.indexed_vertex_array.load_mesh(mesh); #endif // ENABLE_SMOOTH_NORMALS v.indexed_vertex_array.finalize_geometry(opengl_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first); v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id); // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. @@ -774,6 +867,17 @@ void GLVolumeCollection::load_object_auxiliary( } } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +int GLVolumeCollection::load_wipe_tower_preview( + float pos_x, float pos_y, float width, float depth, float height, + float rotation_angle, bool size_unknown, float brim_width) +#else +int GLVolumeCollection::load_wipe_tower_preview( + int obj_idx, float pos_x, float pos_y, float width, float depth, float height, + float rotation_angle, bool size_unknown, float brim_width) +#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#else #if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL int GLVolumeCollection::load_wipe_tower_preview( float pos_x, float pos_y, float width, float depth, float height, @@ -783,6 +887,7 @@ int GLVolumeCollection::load_wipe_tower_preview( int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized) #endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { if (depth < 0.01f) return int(this->volumes.size() - 1); @@ -839,9 +944,16 @@ int GLVolumeCollection::load_wipe_tower_preview( volumes.emplace_back(new GLVolume(color)); GLVolume& v = *volumes.back(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + v.model.init_from(mesh); + v.model.set_color(color); +#else v.indexed_vertex_array.load_mesh(mesh); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.set_convex_hull(mesh.convex_hull_3d()); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.indexed_vertex_array.finalize_geometry(opengl_initialized); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle)); #if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL @@ -856,6 +968,22 @@ int GLVolumeCollection::load_wipe_tower_preview( return int(volumes.size() - 1); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +GLVolume* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba) +{ + GLVolume* out = new_nontoolpath_volume(rgba); + out->is_extrusion_path = true; + return out; +} + +GLVolume* GLVolumeCollection::new_nontoolpath_volume(const ColorRGBA& rgba) +{ + GLVolume* out = new GLVolume(rgba); + out->is_extrusion_path = false; + this->volumes.emplace_back(out); + return out; +} +#else GLVolume* GLVolumeCollection::new_toolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats) { GLVolume *out = new_nontoolpath_volume(rgba, reserve_vbo_floats); @@ -872,6 +1000,7 @@ GLVolume* GLVolumeCollection::new_nontoolpath_volume(const ColorRGBA& rgba, size this->volumes.emplace_back(out); return out; } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCollection::ERenderType type, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) { @@ -960,7 +1089,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); - shader->set_uniform("uniform_color", volume.first->render_color); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (!volume.first->model.is_initialized()) +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + shader->set_uniform("uniform_color", volume.first->render_color); shader->set_uniform("z_range", m_z_range, 2); shader->set_uniform("clipping_plane", m_clipping_plane, 4); shader->set_uniform("print_volume.type", static_cast<int>(m_print_volume.type)); @@ -980,6 +1112,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab #endif // ENABLE_ENVIRONMENT_MAP glcheck(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (volume.first->model.is_initialized()) + volume.first->model.set_color(volume.first->render_color); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL volume.first->render(); #if ENABLE_ENVIRONMENT_MAP @@ -1215,6 +1351,466 @@ std::string GLVolumeCollection::log_memory_info() const return " (GLVolumeCollection RAM: " + format_memsize_MB(this->cpu_memory_used()) + " GPU: " + format_memsize_MB(this->gpu_memory_used()) + " Both: " + format_memsize_MB(this->gpu_memory_used()) + ")"; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +static void thick_lines_to_geometry( + const Lines& lines, + const std::vector<double>& widths, + const std::vector<double>& heights, + bool closed, + double top_z, + GUI::GLModel::Geometry& geometry) +{ + assert(!lines.empty()); + if (lines.empty()) + return; + + enum Direction : unsigned char + { + Left, + Right, + Top, + Bottom + }; + + // right, left, top, bottom + std::array<int, 4> idx_prev = { -1, -1, -1, -1 }; + std::array<int, 4> idx_initial = { -1, -1, -1, -1 }; + + double bottom_z_prev = 0.0; + Vec2d b1_prev(Vec2d::Zero()); + Vec2d v_prev(Vec2d::Zero()); + double len_prev = 0.0; + double width_initial = 0.0; + double bottom_z_initial = 0.0; + + // loop once more in case of closed loops + const size_t lines_end = closed ? (lines.size() + 1) : lines.size(); + for (size_t ii = 0; ii < lines_end; ++ii) { + const size_t i = (ii == lines.size()) ? 0 : ii; + const Line& line = lines[i]; + const double bottom_z = top_z - heights[i]; + const double middle_z = 0.5 * (top_z + bottom_z); + const double width = widths[i]; + + const bool is_first = (ii == 0); + const bool is_last = (ii == lines_end - 1); + const bool is_closing = closed && is_last; + + const Vec2d v = unscale(line.vector()).normalized(); + const double len = unscale<double>(line.length()); + + const Vec2d a = unscale(line.a); + const Vec2d b = unscale(line.b); + Vec2d a1 = a; + Vec2d a2 = a; + Vec2d b1 = b; + Vec2d b2 = b; + { + const double dist = 0.5 * width; // scaled + const double dx = dist * v.x(); + const double dy = dist * v.y(); + a1 += Vec2d(+dy, -dx); + a2 += Vec2d(-dy, +dx); + b1 += Vec2d(+dy, -dx); + b2 += Vec2d(-dy, +dx); + } + + // calculate new XY normals + const Vec2d xy_right_normal = unscale(line.normal()).normalized(); + + std::array<int, 4> idx_a = { 0, 0, 0, 0 }; + std::array<int, 4> idx_b = { 0, 0, 0, 0 }; + int idx_last = int(geometry.vertices_count()); + + const 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. + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]); + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]); + } + + // Share top / bottom vertices if possible. + if (is_first) { + idx_a[Top] = idx_last++; + geometry.add_vertex(Vec3f(a.x(), a.y(), top_z), Vec3f(0.0f, 0.0f, 1.0f)); + } + else + idx_a[Top] = idx_prev[Top]; + + 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++; + geometry.add_vertex(Vec3f(a.x(), a.y(), bottom_z), Vec3f(0.0f, 0.0f, -1.0f)); + idx_a[Left] = idx_last++; + geometry.add_vertex(Vec3f(a2.x(), a2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f)); + idx_a[Right] = idx_last++; + geometry.add_vertex(Vec3f(a1.x(), a1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f)); + } + else + idx_a[Bottom] = idx_prev[Bottom]; + + if (is_first) { + // Start of the 1st line segment. + width_initial = width; + bottom_z_initial = bottom_z; + idx_initial = idx_a; + } + else { + // Continuing a previous segment. + // Share left / right vertices if possible. + const double v_dot = v_prev.dot(v); + // To reduce gpu memory usage, we try to reuse vertices + // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges + // is longer than a fixed threshold. + // The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts + const double len_threshold = 2.5; + + // Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met + const bool sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold); + if (sharp) { + 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++; + geometry.add_vertex(Vec3f(a1.x(), a1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f)); + idx_a[Left] = idx_last++; + geometry.add_vertex(Vec3f(a2.x(), a2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f)); + if (cross2(v_prev, v) > 0.0) { + // Right turn. Fill in the right turn wedge. + geometry.add_uint_triangle(idx_prev[Right], idx_a[Right], idx_prev[Top]); + geometry.add_uint_triangle(idx_prev[Right], idx_prev[Bottom], idx_a[Right]); + } + else { + // Left turn. Fill in the left turn wedge. + geometry.add_uint_triangle(idx_prev[Left], idx_prev[Top], idx_a[Left]); + geometry.add_uint_triangle(idx_prev[Left], idx_a[Left], idx_prev[Bottom]); + } + } + } + else { + if (!bottom_z_different) { + // The two successive segments are nearly collinear. + idx_a[Left] = idx_prev[Left]; + idx_a[Right] = idx_prev[Right]; + } + } + if (is_closing) { + if (!sharp) { + if (!bottom_z_different) { + // Closing a loop with smooth transition. Unify the closing left / right vertices. + geometry.set_vertex(idx_initial[Left], geometry.extract_position_3(idx_prev[Left]), geometry.extract_normal_3(idx_prev[Left])); + geometry.set_vertex(idx_initial[Right], geometry.extract_position_3(idx_prev[Right]), geometry.extract_normal_3(idx_prev[Right])); + geometry.remove_vertex(geometry.vertices_count() - 1); + geometry.remove_vertex(geometry.vertices_count() - 1); + // Replace the left / right vertex indices to point to the start of the loop. + const size_t indices_count = geometry.indices_count(); + for (size_t u = indices_count - 24; u < indices_count; ++u) { + const unsigned int id = geometry.extract_uint_index(u); + if (id == (unsigned int)idx_prev[Left]) + geometry.set_uint_index(u, (unsigned int)idx_initial[Left]); + else if (id == (unsigned int)idx_prev[Right]) + geometry.set_uint_index(u, (unsigned int)idx_initial[Right]); + } + } + } + // This is the last iteration, only required to solve the transition. + break; + } + } + + // Only new allocate top / bottom vertices, if not closing a loop. + if (is_closing) + idx_b[Top] = idx_initial[Top]; + else { + idx_b[Top] = idx_last++; + geometry.add_vertex(Vec3f(b.x(), b.y(), top_z), Vec3f(0.0f, 0.0f, 1.0f)); + } + + if (is_closing && width == width_initial && bottom_z == bottom_z_initial) + idx_b[Bottom] = idx_initial[Bottom]; + else { + idx_b[Bottom] = idx_last++; + geometry.add_vertex(Vec3f(b.x(), b.y(), bottom_z), Vec3f(0.0f, 0.0f, -1.0f)); + } + // Generate new vertices for the end of this line segment. + idx_b[Left] = idx_last++; + geometry.add_vertex(Vec3f(b2.x(), b2.y(), middle_z), Vec3f(-xy_right_normal.x(), -xy_right_normal.y(), 0.0f)); + idx_b[Right] = idx_last++; + geometry.add_vertex(Vec3f(b1.x(), b1.y(), middle_z), Vec3f(xy_right_normal.x(), xy_right_normal.y(), 0.0f)); + + idx_prev = idx_b; + bottom_z_prev = bottom_z; + b1_prev = b1; + v_prev = v; + len_prev = len; + + 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. + geometry.add_uint_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]); + geometry.add_uint_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]); + } + + if (!closed) { + // Terminate open paths with caps. + if (is_first) { + geometry.add_uint_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]); + geometry.add_uint_triangle(idx_a[Bottom], idx_a[Top], idx_a[Left]); + } + // We don't use 'else' because both cases are true if we have only one line. + if (is_last) { + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]); + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]); + } + } + + // Add quads for a straight hollow tube-like segment. + // bottom-right face + geometry.add_uint_triangle(idx_a[Bottom], idx_b[Bottom], idx_b[Right]); + geometry.add_uint_triangle(idx_a[Bottom], idx_b[Right], idx_a[Right]); + // top-right face + geometry.add_uint_triangle(idx_a[Right], idx_b[Right], idx_b[Top]); + geometry.add_uint_triangle(idx_a[Right], idx_b[Top], idx_a[Top]); + // top-left face + geometry.add_uint_triangle(idx_a[Top], idx_b[Top], idx_b[Left]); + geometry.add_uint_triangle(idx_a[Top], idx_b[Left], idx_a[Left]); + // bottom-left face + geometry.add_uint_triangle(idx_a[Left], idx_b[Left], idx_b[Bottom]); + geometry.add_uint_triangle(idx_a[Left], idx_b[Bottom], idx_a[Bottom]); + } +} + +// caller is responsible for supplying NO lines with zero length +static void thick_lines_to_geometry( + const Lines3& lines, + const std::vector<double>& widths, + const std::vector<double>& heights, + bool closed, + GUI::GLModel::Geometry& geometry) +{ + assert(!lines.empty()); + if (lines.empty()) + return; + + enum Direction : unsigned char + { + Left, + Right, + Top, + Bottom + }; + + // left, right, top, bottom + std::array<int, 4> idx_prev = { -1, -1, -1, -1 }; + std::array<int, 4> idx_initial = { -1, -1, -1, -1 }; + + double z_prev = 0.0; + double len_prev = 0.0; + 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 + std::array<Vec3d, 4> a = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; + std::array<Vec3d, 4> b = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; + + // loop once more in case of closed loops + const size_t lines_end = closed ? (lines.size() + 1) : lines.size(); + for (size_t ii = 0; ii < lines_end; ++ii) { + const size_t i = (ii == lines.size()) ? 0 : ii; + + const Line3& line = lines[i]; + const double height = heights[i]; + const double width = widths[i]; + + const Vec3d unit_v = unscale(line.vector()).normalized(); + const double len = unscale<double>(line.length()); + + Vec3d n_top = Vec3d::Zero(); + Vec3d n_right = Vec3d::Zero(); + + if (line.a.x() == line.b.x() && line.a.y() == line.b.y()) { + // vertical segment + n_top = Vec3d::UnitY(); + n_right = Vec3d::UnitX(); + if (line.a.z() < line.b.z()) + n_right = -n_right; + } + else { + // horizontal segment + n_right = unit_v.cross(Vec3d::UnitZ()).normalized(); + n_top = n_right.cross(unit_v).normalized(); + } + + const Vec3d rl_displacement = 0.5 * width * n_right; + const Vec3d tb_displacement = 0.5 * height * n_top; + const Vec3d l_a = unscale(line.a); + const Vec3d l_b = unscale(line.b); + + a[Right] = l_a + rl_displacement; + a[Left] = l_a - rl_displacement; + a[Top] = l_a + tb_displacement; + a[Bottom] = l_a - tb_displacement; + b[Right] = l_b + rl_displacement; + b[Left] = l_b - rl_displacement; + b[Top] = l_b + tb_displacement; + b[Bottom] = l_b - tb_displacement; + + const Vec3d n_bottom = -n_top; + const Vec3d n_left = -n_right; + + std::array<int, 4> idx_a = { 0, 0, 0, 0}; + std::array<int, 4> idx_b = { 0, 0, 0, 0 }; + int idx_last = int(geometry.vertices_count()); + + const bool z_different = (z_prev != l_a.z()); + z_prev = l_b.z(); + + // Share top / bottom vertices if possible. + if (ii == 0) { + idx_a[Top] = idx_last++; + geometry.add_vertex((Vec3f)a[Top].cast<float>(), (Vec3f)n_top.cast<float>()); + } + else + idx_a[Top] = idx_prev[Top]; + + if (ii == 0 || 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++; + geometry.add_vertex((Vec3f)a[Bottom].cast<float>(), (Vec3f)n_bottom.cast<float>()); + idx_a[Left] = idx_last++; + geometry.add_vertex((Vec3f)a[Left].cast<float>(), (Vec3f)n_left.cast<float>()); + idx_a[Right] = idx_last++; + geometry.add_vertex((Vec3f)a[Right].cast<float>(), (Vec3f)n_right.cast<float>()); + } + else + idx_a[Bottom] = idx_prev[Bottom]; + + if (ii == 0) { + // Start of the 1st line segment. + width_initial = width; + idx_initial = idx_a; + } + else { + // Continuing a previous segment. + // Share left / right vertices if possible. + const double v_dot = unit_v_prev.dot(unit_v); + const bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0; + + // To reduce gpu memory usage, we try to reuse vertices + // To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges + // is longer than a fixed threshold. + // The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts + const double len_threshold = 2.5; + + // Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met + const bool is_sharp = v_dot < 0.707 || len_prev > len_threshold || len > len_threshold; + if (is_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++; + geometry.add_vertex((Vec3f)a[Right].cast<float>(), (Vec3f)n_right.cast<float>()); + idx_a[Left] = idx_last++; + geometry.add_vertex((Vec3f)a[Left].cast<float>(), (Vec3f)n_left.cast<float>()); + + if (is_right_turn) { + // Right turn. Fill in the right turn wedge. + geometry.add_uint_triangle(idx_prev[Right], idx_a[Right], idx_prev[Top]); + geometry.add_uint_triangle(idx_prev[Right], idx_prev[Bottom], idx_a[Right]); + } + else { + // Left turn. Fill in the left turn wedge. + geometry.add_uint_triangle(idx_prev[Left], idx_prev[Top], idx_a[Left]); + geometry.add_uint_triangle(idx_prev[Left], idx_a[Left], idx_prev[Bottom]); + } + } + else { + // The two successive segments are nearly collinear. + idx_a[Left] = idx_prev[Left]; + idx_a[Right] = idx_prev[Right]; + } + + if (ii == lines.size()) { + if (!is_sharp) { + // Closing a loop with smooth transition. Unify the closing left / right vertices. + geometry.set_vertex(idx_initial[Left], geometry.extract_position_3(idx_prev[Left]), geometry.extract_normal_3(idx_prev[Left])); + geometry.set_vertex(idx_initial[Right], geometry.extract_position_3(idx_prev[Right]), geometry.extract_normal_3(idx_prev[Right])); + geometry.remove_vertex(geometry.vertices_count() - 1); + geometry.remove_vertex(geometry.vertices_count() - 1); + // Replace the left / right vertex indices to point to the start of the loop. + const size_t indices_count = geometry.indices_count(); + for (size_t u = indices_count - 24; u < indices_count; ++u) { + const unsigned int id = geometry.extract_uint_index(u); + if (id == (unsigned int)idx_prev[Left]) + geometry.set_uint_index(u, (unsigned int)idx_initial[Left]); + else if (id == (unsigned int)idx_prev[Right]) + geometry.set_uint_index(u, (unsigned int)idx_initial[Right]); + } + } + + // This is the last iteration, only required to solve the transition. + break; + } + } + + // Only new allocate top / bottom vertices, if not closing a loop. + if (closed && ii + 1 == lines.size()) + idx_b[Top] = idx_initial[Top]; + else { + idx_b[Top] = idx_last++; + geometry.add_vertex((Vec3f)b[Top].cast<float>(), (Vec3f)n_top.cast<float>()); + } + + if (closed && ii + 1 == lines.size() && width == width_initial) + idx_b[Bottom] = idx_initial[Bottom]; + else { + idx_b[Bottom] = idx_last++; + geometry.add_vertex((Vec3f)b[Bottom].cast<float>(), (Vec3f)n_bottom.cast<float>()); + } + + // Generate new vertices for the end of this line segment. + idx_b[Left] = idx_last++; + geometry.add_vertex((Vec3f)b[Left].cast<float>(), (Vec3f)n_left.cast<float>()); + idx_b[Right] = idx_last++; + geometry.add_vertex((Vec3f)b[Right].cast<float>(), (Vec3f)n_right.cast<float>()); + + idx_prev = idx_b; + n_right_prev = n_right; + n_top_prev = n_top; + unit_v_prev = unit_v; + len_prev = len; + + if (!closed) { + // Terminate open paths with caps. + if (i == 0) { + geometry.add_uint_triangle(idx_a[Bottom], idx_a[Right], idx_a[Top]); + geometry.add_uint_triangle(idx_a[Bottom], 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()) { + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Left], idx_b[Top]); + geometry.add_uint_triangle(idx_b[Bottom], idx_b[Top], idx_b[Right]); + } + } + + // Add quads for a straight hollow tube-like segment. + // bottom-right face + geometry.add_uint_triangle(idx_a[Bottom], idx_b[Bottom], idx_b[Right]); + geometry.add_uint_triangle(idx_a[Bottom], idx_b[Right], idx_a[Right]); + // top-right face + geometry.add_uint_triangle(idx_a[Right], idx_b[Right], idx_b[Top]); + geometry.add_uint_triangle(idx_a[Right], idx_b[Top], idx_a[Top]); + // top-left face + geometry.add_uint_triangle(idx_a[Top], idx_b[Top], idx_b[Left]); + geometry.add_uint_triangle(idx_a[Top], idx_b[Left], idx_a[Left]); + // bottom-left face + geometry.add_uint_triangle(idx_a[Left], idx_b[Left], idx_b[Bottom]); + geometry.add_uint_triangle(idx_a[Left], idx_b[Bottom], idx_a[Bottom]); + } +} +#else // caller is responsible for supplying NO lines with zero length static void thick_lines_to_indexed_vertex_array( const Lines &lines, @@ -1724,7 +2320,30 @@ static void point_to_indexed_vertex_array(const Vec3crd& point, volume.push_triangle(idxs[3], idxs[1], idxs[4]); volume.push_triangle(idxs[0], idxs[3], idxs[4]); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::thick_lines_to_verts( + const Lines& lines, + const std::vector<double>& widths, + const std::vector<double>& heights, + bool closed, + double top_z, + GUI::GLModel::Geometry& geometry) +{ + thick_lines_to_geometry(lines, widths, heights, closed, top_z, geometry); +} + +void _3DScene::thick_lines_to_verts( + const Lines3& lines, + const std::vector<double>& widths, + const std::vector<double>& heights, + bool closed, + GUI::GLModel::Geometry& geometry) +{ + thick_lines_to_geometry(lines, widths, heights, closed, geometry); +} +#else void _3DScene::thick_lines_to_verts( const Lines &lines, const std::vector<double> &widths, @@ -1766,8 +2385,21 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, flo { extrusionentity_to_verts(extrusion_path.polyline, extrusion_path.width, extrusion_path.height, print_z, volume); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Fill in the qverts and tverts with quads and triangles for the extrusion_path. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) +{ + Polyline polyline = extrusion_path.polyline; + polyline.remove_duplicate_points(); + polyline.translate(copy); + const Lines lines = polyline.lines(); + std::vector<double> widths(lines.size(), extrusion_path.width); + std::vector<double> heights(lines.size(), extrusion_path.height); + thick_lines_to_verts(lines, widths, heights, false, print_z, geometry); +} +#else void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) { Polyline polyline = extrusion_path.polyline; @@ -1778,8 +2410,27 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, flo std::vector<double> heights(lines.size(), extrusion_path.height); thick_lines_to_verts(lines, widths, heights, false, print_z, volume); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Fill in the qverts and tverts with quads and triangles for the extrusion_loop. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) +{ + Lines lines; + std::vector<double> widths; + std::vector<double> heights; + for (const ExtrusionPath& extrusion_path : extrusion_loop.paths) { + Polyline polyline = extrusion_path.polyline; + polyline.remove_duplicate_points(); + polyline.translate(copy); + const Lines lines_this = polyline.lines(); + append(lines, lines_this); + widths.insert(widths.end(), lines_this.size(), extrusion_path.width); + heights.insert(heights.end(), lines_this.size(), extrusion_path.height); + } + thick_lines_to_verts(lines, widths, heights, true, print_z, geometry); +} +#else void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) { Lines lines; @@ -1796,8 +2447,27 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, flo } thick_lines_to_verts(lines, widths, heights, true, print_z, volume); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) +{ + Lines lines; + std::vector<double> widths; + std::vector<double> heights; + for (const ExtrusionPath& extrusion_path : extrusion_multi_path.paths) { + Polyline polyline = extrusion_path.polyline; + polyline.remove_duplicate_points(); + polyline.translate(copy); + const Lines lines_this = polyline.lines(); + append(lines, lines_this); + widths.insert(widths.end(), lines_this.size(), extrusion_path.width); + heights.insert(heights.end(), lines_this.size(), extrusion_path.height); + } + thick_lines_to_verts(lines, widths, heights, false, print_z, geometry); +} +#else void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) { Lines lines; @@ -1814,13 +2484,49 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_mult } thick_lines_to_verts(lines, widths, heights, false, print_z, volume); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) +{ + for (const ExtrusionEntity* extrusion_entity : extrusion_entity_collection.entities) + extrusionentity_to_verts(extrusion_entity, print_z, copy, geometry); +} +#else 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); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void _3DScene::extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) +{ + if (extrusion_entity != nullptr) { + auto* extrusion_path = dynamic_cast<const ExtrusionPath*>(extrusion_entity); + if (extrusion_path != nullptr) + extrusionentity_to_verts(*extrusion_path, print_z, copy, geometry); + else { + auto* extrusion_loop = dynamic_cast<const ExtrusionLoop*>(extrusion_entity); + if (extrusion_loop != nullptr) + extrusionentity_to_verts(*extrusion_loop, print_z, copy, geometry); + else { + auto* extrusion_multi_path = dynamic_cast<const ExtrusionMultiPath*>(extrusion_entity); + if (extrusion_multi_path != nullptr) + extrusionentity_to_verts(*extrusion_multi_path, print_z, copy, geometry); + else { + auto* extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity); + if (extrusion_entity_collection != nullptr) + extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, geometry); + else + throw Slic3r::RuntimeError("Unexpected extrusion_entity type in to_verts()"); + } + } + } + } +} +#else void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) { if (extrusion_entity != nullptr) { @@ -1839,9 +2545,8 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, auto *extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity); if (extrusion_entity_collection != nullptr) extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume); - else { + else throw Slic3r::RuntimeError("Unexpected extrusion_entity type in to_verts()"); - } } } } @@ -1860,5 +2565,6 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height { thick_point_to_verts(point, width, height, volume); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } // namespace Slic3r diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index c2e4e587c..ed2aa804e 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -46,6 +46,7 @@ enum ModelInstanceEPrintVolumeState : unsigned char; // Return appropriate color based on the ModelVolume. extern ColorRGBA color_from_model_volume(const ModelVolume& model_volume); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // A container for interleaved arrays of 3D vertices and normals, // possibly indexed by triangles and / or quads. class GLIndexedVertexArray { @@ -246,6 +247,7 @@ public: private: BoundingBox m_bounding_box; }; +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL class GLVolume { public: @@ -388,11 +390,17 @@ public: // Is mouse or rectangle selection over this object to select/deselect it ? EHoverState hover; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GUI::GLModel model; +#else // Interleaved triangles & normals with indexed triangles & quads. GLIndexedVertexArray indexed_vertex_array; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Ranges of triangle and quad indices to be rendered. std::pair<size_t, size_t> tverts_range; +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL std::pair<size_t, size_t> qverts_range; +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts // of the extrusions per layer. @@ -402,13 +410,17 @@ public: // Bounding box of this volume, in unscaled coordinates. BoundingBoxf3 bounding_box() const { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + return this->model.get_bounding_box(); +#else BoundingBoxf3 out; - if (! this->indexed_vertex_array.bounding_box().isEmpty()) { + if (!this->indexed_vertex_array.bounding_box().isEmpty()) { out.min = this->indexed_vertex_array.bounding_box().min().cast<double>(); out.max = this->indexed_vertex_array.bounding_box().max().cast<double>(); out.defined = true; - }; + } return out; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } void set_color(const ColorRGBA& rgba) { color = rgba; } @@ -498,14 +510,20 @@ public: // convex hull const TriangleMesh* convex_hull() const { return m_convex_hull.get(); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + bool empty() const { return this->model.is_empty(); } +#else bool empty() const { return this->indexed_vertex_array.empty(); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void set_range(double low, double high); - void render() const; + void render(); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); } void release_geometry() { this->indexed_vertex_array.release_geometry(); } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void set_bounding_boxes_as_dirty() { m_transformed_bounding_box.reset(); @@ -524,12 +542,20 @@ public: #endif // ENABLE_SHOW_NON_MANIFOLD_EDGES // Return an estimate of the memory consumed by this class. - size_t cpu_memory_used() const { - //FIXME what to do wih m_convex_hull? + size_t cpu_memory_used() const { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + return sizeof(*this) + this->model.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + + this->offsets.capacity() * sizeof(size_t); + } + // Return an estimate of the memory held by GPU vertex buffers. + size_t gpu_memory_used() const { return this->model.gpu_memory_used(); } +#else + //FIXME what to do wih m_convex_hull? return sizeof(*this) - sizeof(this->indexed_vertex_array) + this->indexed_vertex_array.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + this->offsets.capacity() * sizeof(size_t); } // Return an estimate of the memory held by GPU vertex buffers. size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); } }; @@ -589,6 +615,36 @@ public: GLVolumeCollection() { set_default_slope_normal_z(); } ~GLVolumeCollection() { clear(); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + std::vector<int> load_object( + const ModelObject* model_object, + int obj_idx, + const std::vector<int>& instance_idxs); + + int load_object_volume( + const ModelObject* model_object, + int obj_idx, + int volume_idx, + int instance_idx); + + // Load SLA auxiliary GLVolumes (for support trees or pad). + void load_object_auxiliary( + const SLAPrintObject* print_object, + int obj_idx, + // pairs of <instance_idx, print_instance_idx> + const std::vector<std::pair<size_t, size_t>>& instances, + SLAPrintObjectStep milestone, + // Timestamp of the last change of the milestone + size_t timestamp); + +#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL + int load_wipe_tower_preview( + float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width); +#else + int load_wipe_tower_preview( + int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width); +#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#else std::vector<int> load_object( const ModelObject *model_object, int obj_idx, @@ -620,13 +676,20 @@ public: int load_wipe_tower_preview( int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized); #endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLVolume* new_toolpath_volume(const ColorRGBA& rgba); + GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba); +#else GLVolume* new_toolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats = 0); GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba, size_t reserve_vbo_floats = 0); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Render the volumes by OpenGL. void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const; +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Finalize the initialization of the geometry & indices, // upload the geometry and indices to OpenGL VBO objects // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs. @@ -634,11 +697,12 @@ public: // Release the geometry data assigned to the volumes. // If OpenGL VBOs were allocated, an OpenGL context has to be active to release them. void release_geometry() { for (auto *v : volumes) v->release_geometry(); } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Clear the geometry void clear() { for (auto *v : volumes) delete v; volumes.clear(); } bool empty() const { return volumes.empty(); } - void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } + void set_range(double low, double high) { for (GLVolume* vol : this->volumes) vol->set_range(low, high); } void set_print_volume(const PrintVolume& print_volume) { m_print_volume = print_volume; } @@ -683,9 +747,18 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo struct _3DScene { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GUI::GLModel::Geometry& geometry); + static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); + static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry); +#else static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume); static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume); - static void extrusionentity_to_verts(const Polyline &polyline, float width, float height, float print_z, GLVolume& volume); + static void extrusionentity_to_verts(const Polyline& polyline, float width, float height, float print_z, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume); static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume); @@ -694,6 +767,7 @@ struct _3DScene static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume); static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume); static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL }; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index c86d16a1f..cfe6fe418 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -711,7 +711,11 @@ void GCodeViewer::init() m_gl_data_initialized = true; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& print) +#else void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized) +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { // avoid processing if called with the same gcode_result #if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC @@ -750,7 +754,11 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr m_filament_densities = gcode_result.filament_densities; if (wxGetApp().is_editor()) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + load_shells(print); +#else load_shells(print, initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL else { Pointfs bed_shape; std::string texture; @@ -2289,7 +2297,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) progress_dialog->Destroy(); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GCodeViewer::load_shells(const Print& print) +#else void GCodeViewer::load_shells(const Print& print, bool initialized) +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL { if (print.objects().empty()) // no shells, return @@ -2306,7 +2318,11 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) } size_t current_volumes_count = m_shells.volumes.volumes.size(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_shells.volumes.load_object(model_obj, object_id, instance_ids); +#else m_shells.volumes.load_object(model_obj, object_id, instance_ids, initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // adjust shells' z if raft is present const SlicingParameters& slicing_parameters = obj->slicing_parameters(); @@ -2330,6 +2346,15 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) const float depth = print.wipe_tower_data(extruders_count).depth; const float brim_width = print.wipe_tower_data(extruders_count).brim_width; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL + m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, + !print.is_step_done(psWipeTower), brim_width); +#else + m_shells.volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, + !print.is_step_done(psWipeTower), brim_width); +#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#else #if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, !print.is_step_done(psWipeTower), brim_width, initialized); @@ -2337,6 +2362,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized) m_shells.volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, !print.is_step_done(psWipeTower), brim_width, initialized); #endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } @@ -3199,6 +3225,7 @@ void GCodeViewer::render_shells() if (shader == nullptr) return; +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // when the background processing is enabled, it may happen that the shells data have been loaded // before opengl has been initialized for the preview canvas. // when this happens, the volumes' data have not been sent to gpu yet. @@ -3206,6 +3233,7 @@ void GCodeViewer::render_shells() if (!v->indexed_vertex_array.has_VBOs()) v->finalize_geometry(true); } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // glsafe(::glDepthMask(GL_FALSE)); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index b2b0626d5..bdb3ed983 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -823,7 +823,11 @@ public: void init(); // extract rendering data from the given parameters +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void load(const GCodeProcessorResult& gcode_result, const Print& print); +#else void load(const GCodeProcessorResult& gcode_result, const Print& print, bool initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // recalculate ranges in dependence of what is visible and sets tool/print colors void refresh(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors); #if ENABLE_PREVIEW_LAYOUT @@ -883,7 +887,11 @@ public: private: void load_toolpaths(const GCodeProcessorResult& gcode_result); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void load_shells(const Print& print); +#else void load_shells(const Print& print, bool initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #if !ENABLE_PREVIEW_LAYOUT void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; #endif // !ENABLE_PREVIEW_LAYOUT diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index edd62ce0a..93c3c848f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -526,7 +526,7 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data())); glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4)); - for (const GLVolume* glvolume : volumes.volumes) { + for (GLVolume* glvolume : volumes.volumes) { // Render the object using the layer editing shader and texture. if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier) continue; @@ -1192,9 +1192,11 @@ bool GLCanvas3D::init() if (m_main_toolbar.is_enabled()) m_layers_editing.init(); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // on linux the gl context is not valid until the canvas is not shown on screen // we defer the geometry finalization of volumes until the first call to render() m_volumes.finalize_geometry(true); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (m_gizmos.is_enabled() && !m_gizmos.init()) std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl; @@ -1799,7 +1801,11 @@ std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int ob instance_idxs.emplace_back(i); } } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + return m_volumes.load_object(&model_object, obj_idx, instance_idxs); +#else return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx) @@ -2024,7 +2030,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh // later in this function. it->volume_idx = m_volumes.volumes.size(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx); +#else m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL m_volumes.volumes.back()->geometry_id = key.geometry_id; update_object_list = true; } else { @@ -2081,31 +2091,55 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re GLVolume &volume = *m_volumes.volumes[it->volume_idx]; if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { // The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.model.reset(); +#else volume.indexed_vertex_array.release_geometry(); - if (state.step[istep].state == PrintStateBase::DONE) { +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (state.step[istep].state == PrintStateBase::DONE) { TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); assert(! mesh.empty()); mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse()); #if ENABLE_SMOOTH_NORMALS +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.model.init_from(mesh, true); +#else volume.indexed_vertex_array.load_mesh(mesh, true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#else +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.model.init_from(mesh); #else volume.indexed_vertex_array.load_mesh(mesh); -#endif // ENABLE_SMOOTH_NORMALS - } else { - // Reload the original volume. -#if ENABLE_SMOOTH_NORMALS - volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); -#else - volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #endif // ENABLE_SMOOTH_NORMALS } + else { + // Reload the original volume. +#if ENABLE_SMOOTH_NORMALS +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); +#else + volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh(), true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#else +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.model.init_from(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); +#else + volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#endif // ENABLE_SMOOTH_NORMALS + } +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL volume.finalize_geometry(true); - } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable // to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables // of various concenrs (model vs. 3D print path). volume.offsets = { state.step[istep].timestamp }; - } else if (state.step[istep].state == PrintStateBase::DONE) { + } + else if (state.step[istep].state == PrintStateBase::DONE) { // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created. ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); @@ -2117,7 +2151,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re instances[istep].emplace_back(std::pair<size_t, size_t>(instance_idx, print_instance_idx)); else shift_zs[object_idx] = 0.; - } else { + } + else { // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); @@ -2127,7 +2162,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re for (size_t istep = 0; istep < sla_steps.size(); ++istep) if (!instances[istep].empty()) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp); +#else m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed @@ -2157,6 +2196,17 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re float depth = print->wipe_tower_data(extruders_count).depth; float brim_width = print->wipe_tower_data(extruders_count).brim_width; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL + int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( + x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), + brim_width); +#else + int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( + 1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), + brim_width); +#endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#else #if ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), @@ -2166,6 +2216,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re 1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower), brim_width, m_initialized); #endif // ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (volume_idx_wipe_tower_old != -1) map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new; } @@ -2225,9 +2276,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_dirty = true; } +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& vol_old, bool gl_initialized, size_t prealloc_size = VERTEX_BUFFER_RESERVE_SIZE) { - // Assign the large pre-allocated buffers to the new GLVolume. + // Assign the large pre-allocated buffers to the new GLVolume. vol_new.indexed_vertex_array = std::move(vol_old.indexed_vertex_array); // Copy the content back to the old GLVolume. vol_old.indexed_vertex_array = vol_new.indexed_vertex_array; @@ -2239,10 +2291,15 @@ static void reserve_new_volume_finalize_old_volume(GLVolume& vol_new, GLVolume& // Finalize the old geometry, possibly move data to the graphics card. vol_old.finalize_geometry(gl_initialized); } +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, const std::vector<std::string>& str_tool_colors) { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + m_gcode_viewer.load(gcode_result, *this->fff_print()); +#else m_gcode_viewer.load(gcode_result, *this->fff_print(), m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (wxGetApp().is_editor()) { m_gcode_viewer.update_shells_color_by_extruder(m_config); @@ -4356,7 +4413,11 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const shader->set_uniform("emission_factor", 0.0f); for (GLVolume* vol : visible_volumes) { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + vol->model.set_color((vol->printable && !vol->is_outside) ? (current_printer_technology() == ptSLA ? vol->color : ColorRGBA::ORANGE()) : ColorRGBA::GRAY()); +#else shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? (current_printer_technology() == ptSLA ? vol->color : ColorRGBA::ORANGE()) : ColorRGBA::GRAY()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // the volume may have been deactivated by an active gizmo bool is_active = vol->is_active; vol->is_active = true; @@ -5562,6 +5623,12 @@ void GLCanvas3D::_render_overlays() void GLCanvas3D::_render_volumes_for_picking() const { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLShaderProgram* shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) + return; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + // do not cull backfaces to show broken geometry, if any glsafe(::glDisable(GL_CULL_FACE)); @@ -5577,9 +5644,17 @@ void GLCanvas3D::_render_volumes_for_picking() const // we reserve color = (0,0,0) for occluders (as the printbed) // so we shift volumes' id by 1 to get the proper color const unsigned int id = 1 + volume.second.first; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume.first->model.set_color(picking_decode(id)); + shader->start_using(); +#else glsafe(::glColor4fv(picking_decode(id).data())); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL volume.first->render(); - } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + shader->stop_using(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } } glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); @@ -6165,23 +6240,48 @@ void GLCanvas3D::_load_print_toolpaths(const BuildVolume &build_volume) skirt_height = std::min(skirt_height, print_zs.size()); print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLVolume* volume = m_volumes.new_toolpath_volume(color); + GLModel::Geometry init_data; + init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; +#else GLVolume *volume = m_volumes.new_toolpath_volume(color, VERTEX_BUFFER_RESERVE_SIZE); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL for (size_t i = 0; i < skirt_height; ++ i) { volume->print_zs.emplace_back(print_zs[i]); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume->offsets.emplace_back(init_data.indices_count()); + if (i == 0) + _3DScene::extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), init_data); + _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), init_data); +#else volume->offsets.emplace_back(volume->indexed_vertex_array.quad_indices.size()); volume->offsets.emplace_back(volume->indexed_vertex_array.triangle_indices.size()); if (i == 0) _3DScene::extrusionentity_to_verts(print->brim(), print_zs[i], Point(0, 0), *volume); _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), *volume); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (init_data.vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { + volume->model.init_from(std::move(init_data)); +#else if (volume->indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { - GLVolume &vol = *volume; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLVolume &vol = *volume; volume = m_volumes.new_toolpath_volume(vol.color); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL reserve_new_volume_finalize_old_volume(*volume, vol, m_initialized); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume->model.init_from(std::move(init_data)); + volume->is_outside = !contains(build_volume, volume->model); +#else volume->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(volume->indexed_vertex_array.vertices_and_normals_interleaved, volume->indexed_vertex_array.bounding_box()); volume->indexed_vertex_array.finalize_geometry(m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const BuildVolume& build_volume, const std::vector<std::string>& str_tool_colors, const std::vector<CustomGCode::Item>& color_print_values) @@ -6353,7 +6453,12 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c // Allocate the volume before locking. GLVolume *volume = new GLVolume(color); volume->is_extrusion_path = true; - tbb::spin_mutex::scoped_lock lock; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + // to prevent sending data to gpu (in the main thread) while + // editing the model geometry + volume->model.disable_render(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + tbb::spin_mutex::scoped_lock lock; // Lock by ROII, so if the emplace_back() fails, the lock will be released. lock.acquire(new_volume_mutex); m_volumes.volumes.emplace_back(volume); @@ -6365,31 +6470,57 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size), [&ctxt, &new_volume, is_selected_separate_extruder, this](const tbb::blocked_range<size_t>& range) { GLVolumePtrs vols; - auto volume = [&ctxt, &vols](size_t layer_idx, int extruder, int feature) -> GLVolume& { - return *vols[ctxt.color_by_color_print()? +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + std::vector<GLModel::Geometry> geometries; + auto select_geometry = [&ctxt, &geometries](size_t layer_idx, int extruder, int feature) -> GLModel::Geometry& { + return geometries[ctxt.color_by_color_print() ? ctxt.color_print_color_idx_by_layer_idx_and_extruder(layer_idx, extruder) : - ctxt.color_by_tool() ? - std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) : - feature - ]; + ctxt.color_by_tool() ? + std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) : + feature + ]; }; +#else + auto volume = [&ctxt, &vols](size_t layer_idx, int extruder, int feature) -> GLVolume& { + return *vols[ctxt.color_by_color_print() ? + ctxt.color_print_color_idx_by_layer_idx_and_extruder(layer_idx, extruder) : + ctxt.color_by_tool() ? + std::min<int>(ctxt.number_tools() - 1, std::max<int>(extruder - 1, 0)) : + feature + ]; + }; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (ctxt.color_by_color_print() || ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++i) + for (size_t i = 0; i < ctxt.number_tools(); ++i) { vols.emplace_back(new_volume(ctxt.color_tool(i))); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + geometries.emplace_back(GLModel::Geometry()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } } - else + else { vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + geometries = { GLModel::Geometry(), GLModel::Geometry(), GLModel::Geometry() }; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + assert(vols.size() == geometries.size()); + for (GLModel::Geometry& g : geometries) { + g.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; + } +#else for (GLVolume *vol : vols) // Reserving number of vertices (3x position + 3x color) vol->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { const Layer *layer = ctxt.layers[idx_layer]; - if (is_selected_separate_extruder) - { + if (is_selected_separate_extruder) { bool at_least_one_has_correct_extruder = false; - for (const LayerRegion* layerm : layer->regions()) - { + for (const LayerRegion* layerm : layer->regions()) { if (layerm->slices.surfaces.empty()) continue; const PrintRegionConfig& cfg = layerm->region().config(); @@ -6404,17 +6535,27 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c continue; } - for (GLVolume *vol : vols) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + 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.emplace_back(layer->print_z); + vol->offsets.emplace_back(geometries[i].indices_count()); + } + } +#else + for (GLVolume* vol : vols) if (vol->print_zs.empty() || vol->print_zs.back() != layer->print_z) { vol->print_zs.emplace_back(layer->print_z); vol->offsets.emplace_back(vol->indexed_vertex_array.quad_indices.size()); vol->offsets.emplace_back(vol->indexed_vertex_array.triangle_indices.size()); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (const PrintInstance &instance : *ctxt.shifted_copies) { const Point © = instance.shift; for (const LayerRegion *layerm : layer->regions()) { - if (is_selected_separate_extruder) - { + if (is_selected_separate_extruder) { const PrintRegionConfig& cfg = layerm->region().config(); if (cfg.perimeter_extruder.value != m_selected_extruder || cfg.infill_extruder.value != m_selected_extruder || @@ -6422,19 +6563,31 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c continue; } if (ctxt.has_perimeters) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, + select_geometry(idx_layer, layerm->region().config().perimeter_extruder.value, 0)); +#else _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, volume(idx_layer, layerm->region().config().perimeter_extruder.value, 0)); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL 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()) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, + select_geometry(idx_layer, is_solid_infill(fill->entities.front()->role()) ? + layerm->region().config().solid_infill_extruder : + layerm->region().config().infill_extruder, 1)); +#else _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, volume(idx_layer, is_solid_infill(fill->entities.front()->role()) ? layerm->region().config().solid_infill_extruder : layerm->region().config().infill_extruder, 1)); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } } @@ -6442,28 +6595,50 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer); if (support_layer) { for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, + select_geometry(idx_layer, (extrusion_entity->role() == erSupportMaterial) ? + support_layer->object()->config().support_material_extruder : + support_layer->object()->config().support_material_interface_extruder, 2)); +#else _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, volume(idx_layer, (extrusion_entity->role() == erSupportMaterial) ? support_layer->object()->config().support_material_extruder : support_layer->object()->config().support_material_interface_extruder, 2)); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } } // Ensure that no volume grows over the limits. If the volume is too large, allocate a new one. for (size_t i = 0; i < vols.size(); ++i) { GLVolume &vol = *vols[i]; - if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { - vols[i] = new_volume(vol.color); - reserve_new_volume_finalize_old_volume(*vols[i], vol, false); - } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (geometries[i].vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { + vol.model.init_from(std::move(geometries[i])); +#else + if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + vols[i] = new_volume(vol.color); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + reserve_new_volume_finalize_old_volume(*vols[i], vol, false); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } } } + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (size_t i = 0; i < vols.size(); ++i) { + if (!geometries[i].is_empty()) + vols[i]->model.init_from(std::move(geometries[i])); + } +#else for (GLVolume *vol : vols) // Ideally one would call vol->indexed_vertex_array.finalize() here to move the buffers to the OpenGL driver, // but this code runs in parallel and the OpenGL driver is not thread safe. vol->indexed_vertex_array.shrink_to_fit(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL }); BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info(); @@ -6478,8 +6653,14 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c } for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) { GLVolume* v = m_volumes.volumes[i]; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + v->is_outside = !contains(build_volume, v->model); + // We are done editinig the model, now it can be sent to gpu + v->model.enable_render(); +#else v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box()); v->indexed_vertex_array.finalize_geometry(m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); @@ -6499,10 +6680,10 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con struct Ctxt { - const Print *print; - const std::vector<ColorRGBA>* tool_colors; - Vec2f wipe_tower_pos; - float wipe_tower_angle; + const Print *print; + const std::vector<ColorRGBA> *tool_colors; + Vec2f wipe_tower_pos; + float wipe_tower_angle; static ColorRGBA color_support() { return ColorRGBA::GREENISH(); } @@ -6544,6 +6725,11 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con auto new_volume = [this, &new_volume_mutex](const ColorRGBA& color) { auto *volume = new GLVolume(color); volume->is_extrusion_path = true; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + // to prevent sending data to gpu (in the main thread) while + // editing the model geometry + volume->model.disable_render(); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL tbb::spin_mutex::scoped_lock lock; lock.acquire(new_volume_mutex); m_volumes.volumes.emplace_back(volume); @@ -6557,23 +6743,46 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) { // Bounding box of this slab of a wipe tower. GLVolumePtrs vols; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + std::vector<GLModel::Geometry> geometries; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL if (ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++i) + for (size_t i = 0; i < ctxt.number_tools(); ++i) { vols.emplace_back(new_volume(ctxt.color_tool(i))); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + geometries.emplace_back(GLModel::Geometry()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } } - else + else { vols = { new_volume(ctxt.color_support()) }; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + geometries = { GLModel::Geometry() }; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + } + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + assert(vols.size() == geometries.size()); + for (GLModel::Geometry& g : geometries) { + g.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3, GLModel::Geometry::EIndexType::UINT }; + } +#else for (GLVolume *volume : vols) // Reserving number of vertices (3x position + 3x color) volume->indexed_vertex_array.reserve(VERTEX_BUFFER_RESERVE_SIZE / 6); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL 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.emplace_back(layer.front().print_z); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + vol.offsets.emplace_back(geometries[i].indices_count()); +#else vol.offsets.emplace_back(vol.indexed_vertex_array.quad_indices.size()); vol.offsets.emplace_back(vol.indexed_vertex_array.triangle_indices.size()); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } for (const WipeTower::ToolChangeResult &extrusions : layer) { @@ -6615,21 +6824,42 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con e_prev = e; } + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, + geometries[ctxt.volume_idx(e.tool, 0)]); +#else _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, *vols[ctxt.volume_idx(e.tool, 0)]); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } } for (size_t i = 0; i < vols.size(); ++i) { GLVolume &vol = *vols[i]; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + if (geometries[i].vertices_size_bytes() > MAX_VERTEX_BUFFER_SIZE) { + vol.model.init_from(std::move(geometries[i])); +#else if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() > MAX_VERTEX_BUFFER_SIZE) { +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL vols[i] = new_volume(vol.color); +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL reserve_new_volume_finalize_old_volume(*vols[i], vol, false); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } } + +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + for (size_t i = 0; i < vols.size(); ++i) { + if (!geometries[i].is_empty()) + vols[i]->model.init_from(std::move(geometries[i])); + } +#else for (GLVolume *vol : vols) vol->indexed_vertex_array.shrink_to_fit(); - }); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + }); BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results" << m_volumes.log_memory_info() << log_memory_info(); // Remove empty volumes from the newly added volumes. @@ -6643,8 +6873,14 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const BuildVolume& build_volume, con } for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) { GLVolume* v = m_volumes.volumes[i]; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + v->is_outside = !contains(build_volume, v->model); + // We are done editinig the model, now it can be sent to gpu + v->model.enable_render(); +#else v->is_outside = ! build_volume.all_paths_inside_vertices_and_normals_interleaved(v->indexed_vertex_array.vertices_and_normals_interleaved, v->indexed_vertex_array.bounding_box()); v->indexed_vertex_array.finalize_geometry(m_initialized); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end" << m_volumes.log_memory_info() << log_memory_info(); @@ -6668,11 +6904,21 @@ void GLCanvas3D::_load_sla_shells() m_volumes.volumes.emplace_back(new GLVolume(color)); GLVolume& v = *m_volumes.volumes.back(); #if ENABLE_SMOOTH_NORMALS +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + v.model.init_from(mesh, true); +#else v.indexed_vertex_array.load_mesh(mesh, true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#else +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + v.model.init_from(mesh); #else v.indexed_vertex_array.load_mesh(mesh); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #endif // ENABLE_SMOOTH_NORMALS +#if !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.indexed_vertex_array.finalize_geometry(m_initialized); +#endif // !ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled; v.composite_id.volume_id = volume_id; v.set_instance_offset(unscale(instance.shift.x(), instance.shift.y(), 0.0)); diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 28a6a1ce7..8620c7eaf 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -8,15 +8,56 @@ #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Polygon.hpp" +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#include "libslic3r/BuildVolume.hpp" +#include "libslic3r/Geometry/ConvexHull.hpp" +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #include <boost/filesystem/operations.hpp> #include <boost/algorithm/string/predicate.hpp> +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_SMOOTH_NORMALS +#include <igl/per_face_normals.h> +#include <igl/per_corner_normals.h> +#include <igl/per_vertex_normals.h> +#endif // ENABLE_SMOOTH_NORMALS +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + #include <GL/glew.h> namespace Slic3r { namespace GUI { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +#if ENABLE_SMOOTH_NORMALS +static void smooth_normals_corner(const TriangleMesh& mesh, std::vector<stl_normal>& normals) +{ + using MapMatrixXfUnaligned = Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>; + using MapMatrixXiUnaligned = Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>>; + + std::vector<Vec3f> face_normals = its_face_normals(mesh.its); + + Eigen::MatrixXd vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), + Eigen::Index(mesh.its.vertices.size()), 3).cast<double>(); + Eigen::MatrixXi indices = MapMatrixXiUnaligned(mesh.its.indices.front().data(), + Eigen::Index(mesh.its.indices.size()), 3); + Eigen::MatrixXd in_normals = MapMatrixXfUnaligned(face_normals.front().data(), + Eigen::Index(face_normals.size()), 3).cast<double>(); + Eigen::MatrixXd out_normals; + + igl::per_corner_normals(vertices, indices, in_normals, 1.0, out_normals); + + normals = std::vector<stl_normal>(mesh.its.vertices.size()); + for (size_t i = 0; i < mesh.its.indices.size(); ++i) { + for (size_t j = 0; j < 3; ++j) { + normals[mesh.its.indices[i][j]] = out_normals.row(i * 3 + j).cast<float>(); + } + } +} +#endif // ENABLE_SMOOTH_NORMALS +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + #if ENABLE_GLBEGIN_GLEND_REMOVAL void GLModel::Geometry::reserve_vertices(size_t vertices_count) { @@ -207,6 +248,37 @@ Vec2f GLModel::Geometry::extract_tex_coord_2(size_t id) const return { *(start + 0), *(start + 1) }; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GLModel::Geometry::set_vertex(size_t id, const Vec3f& position, const Vec3f& normal) +{ + assert(format.vertex_layout == EVertexLayout::P3N3); + assert(id < vertices_count()); + if (id < vertices_count()) { + float* start = &vertices[id * vertex_stride_floats(format)]; + *(start + 0) = position.x(); + *(start + 1) = position.y(); + *(start + 2) = position.z(); + *(start + 3) = normal.x(); + *(start + 4) = normal.y(); + *(start + 5) = normal.z(); + } +} + +void GLModel::Geometry::set_ushort_index(size_t id, unsigned short index) +{ + assert(id < indices_count()); + if (id < indices_count()) + ::memcpy(indices.data() + id * sizeof(unsigned short), &index, sizeof(unsigned short)); +} + +void GLModel::Geometry::set_uint_index(size_t id, unsigned int index) +{ + assert(id < indices_count()); + if (id < indices_count()) + ::memcpy(indices.data() + id * sizeof(unsigned int), &index, sizeof(unsigned int)); +} +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + unsigned int GLModel::Geometry::extract_uint_index(size_t id) const { if (format.index_type != EIndexType::UINT) { @@ -219,7 +291,7 @@ unsigned int GLModel::Geometry::extract_uint_index(size_t id) const return -1; } - unsigned int ret = -1; + unsigned int ret = (unsigned int)-1; ::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned int)); return ret; } @@ -236,11 +308,23 @@ unsigned short GLModel::Geometry::extract_ushort_index(size_t id) const return -1; } - unsigned short ret = -1; + unsigned short ret = (unsigned short)-1; ::memcpy(&ret, indices.data() + id * index_stride_bytes(format), sizeof(unsigned short)); return ret; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GLModel::Geometry::remove_vertex(size_t id) +{ + assert(id < vertices_count()); + if (id < vertices_count()) { + size_t stride = vertex_stride_floats(format); + std::vector<float>::iterator it = vertices.begin() + id * stride; + vertices.erase(it, it + stride); + } +} +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + size_t GLModel::Geometry::vertex_stride_floats(const Format& format) { switch (format.vertex_layout) @@ -461,10 +545,58 @@ void GLModel::init_from(const Geometry& data) } #if ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_SMOOTH_NORMALS +void GLModel::init_from(const TriangleMesh& mesh, bool smooth_normals) +{ + if (smooth_normals) { + if (is_initialized()) { + // call reset() if you want to reuse this model + assert(false); + return; + } + + if (mesh.its.vertices.empty() || mesh.its.indices.empty()) { + assert(false); + return; + } + + std::vector<stl_normal> normals; + smooth_normals_corner(mesh, normals); + + const indexed_triangle_set& its = mesh.its; + Geometry& data = m_render_data.geometry; + data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(3 * its.indices.size()) }; + data.reserve_vertices(3 * its.indices.size()); + data.reserve_indices(3 * its.indices.size()); + + // vertices + for (size_t i = 0; i < its.vertices.size(); ++i) { + data.add_vertex(its.vertices[i], normals[i]); + } + + // indices + for (size_t i = 0; i < its.indices.size(); ++i) { + const stl_triangle_vertex_indices& idx = its.indices[i]; + if (data.format.index_type == GLModel::Geometry::EIndexType::USHORT) + data.add_ushort_triangle((unsigned short)idx(0), (unsigned short)idx(1), (unsigned short)idx(2)); + else + data.add_uint_triangle((unsigned int)idx(0), (unsigned int)idx(1), (unsigned int)idx(2)); + } + + // update bounding box + for (size_t i = 0; i < vertices_count(); ++i) { + m_bounding_box.merge(m_render_data.geometry.extract_position_3(i).cast<double>()); + } + } + else + init_from(mesh.its); +} +#else void GLModel::init_from(const TriangleMesh& mesh) { init_from(mesh.its); } +#endif // ENABLE_SMOOTH_NORMALS void GLModel::init_from(const indexed_triangle_set& its) #else @@ -484,21 +616,24 @@ void GLModel::init_from(const indexed_triangle_set& its, const BoundingBoxf3 &bb } Geometry& data = m_render_data.geometry; - data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3, Geometry::EIndexType::UINT }; + data.format = { Geometry::EPrimitiveType::Triangles, Geometry::EVertexLayout::P3N3, GLModel::Geometry::index_type(3 * its.indices.size()) }; data.reserve_vertices(3 * its.indices.size()); data.reserve_indices(3 * its.indices.size()); // vertices + indices unsigned int vertices_counter = 0; for (uint32_t i = 0; i < its.indices.size(); ++i) { - stl_triangle_vertex_indices face = its.indices[i]; - stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; - stl_vertex n = face_normal_normalized(vertex); + const stl_triangle_vertex_indices face = its.indices[i]; + const stl_vertex vertex[3] = { its.vertices[face[0]], its.vertices[face[1]], its.vertices[face[2]] }; + const stl_vertex n = face_normal_normalized(vertex); for (size_t j = 0; j < 3; ++j) { data.add_vertex(vertex[j], n); } vertices_counter += 3; - data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); + if (data.format.index_type == GLModel::Geometry::EIndexType::USHORT) + data.add_ushort_triangle((unsigned short)vertices_counter - 3, (unsigned short)vertices_counter - 2, (unsigned short)vertices_counter - 1); + else + data.add_uint_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1); } // update bounding box @@ -721,6 +856,9 @@ void GLModel::render() void GLModel::render() const #endif // ENABLE_GLBEGIN_GLEND_REMOVAL { +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + render(std::make_pair<size_t, size_t>(0, indices_count())); +#else GLShaderProgram* shader = wxGetApp().get_current_shader(); #if ENABLE_GLBEGIN_GLEND_REMOVAL @@ -809,8 +947,71 @@ void GLModel::render() const glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); } #endif // ENABLE_GLBEGIN_GLEND_REMOVAL +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +void GLModel::render(const std::pair<size_t, size_t>& range) +{ + if (m_render_disabled) + return; + + if (range.second == range.first) + return; + + GLShaderProgram* shader = wxGetApp().get_current_shader(); + + if (shader == nullptr) + return; + + // sends data to gpu if not done yet + if (m_render_data.vbo_id == 0 || m_render_data.ibo_id == 0) { + if (m_render_data.geometry.vertices_count() > 0 && m_render_data.geometry.indices_count() > 0 && !send_to_gpu()) + return; + } + + const Geometry& data = m_render_data.geometry; + + const GLenum mode = get_primitive_mode(data.format); + const GLenum index_type = get_index_type(data.format); + + const size_t vertex_stride_bytes = Geometry::vertex_stride_bytes(data.format); + const bool position = Geometry::has_position(data.format); + const bool normal = Geometry::has_normal(data.format); + const bool tex_coord = Geometry::has_tex_coord(data.format); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_render_data.vbo_id)); + + if (position) { + glsafe(::glVertexPointer(Geometry::position_stride_floats(data.format), GL_FLOAT, vertex_stride_bytes, (const void*)Geometry::position_offset_bytes(data.format))); + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + } + if (normal) { + glsafe(::glNormalPointer(GL_FLOAT, vertex_stride_bytes, (const void*)Geometry::normal_offset_bytes(data.format))); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + } + if (tex_coord) { + glsafe(::glTexCoordPointer(Geometry::tex_coord_stride_floats(data.format), GL_FLOAT, vertex_stride_bytes, (const void*)Geometry::tex_coord_offset_bytes(data.format))); + glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + } + + shader->set_uniform("uniform_color", data.color); + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_render_data.ibo_id)); + glsafe(::glDrawElements(mode, range.second - range.first + 1, index_type, (const void*)(range.first * Geometry::index_stride_bytes(data.format)))); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + if (tex_coord) + glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY)); + if (normal) + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + if (position) + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +} +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + #if ENABLE_GLBEGIN_GLEND_REMOVAL void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) #else @@ -1027,6 +1228,62 @@ static void append_triangle(GLModel::Geometry& data, unsigned short v1, unsigned } #endif // ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +template<typename Fn> +inline bool all_vertices_inside(const GLModel::Geometry& geometry, Fn fn) +{ + const size_t position_stride_floats = geometry.position_stride_floats(geometry.format); + const size_t position_offset_floats = geometry.position_offset_floats(geometry.format); + assert(position_stride_floats == 3); + if (geometry.vertices.empty() || position_stride_floats != 3) + return false; + + for (auto it = geometry.vertices.begin(); it != geometry.vertices.end(); ) { + it += position_offset_floats; + if (!fn({ *it, *(it + 1), *(it + 2) })) + return false; + it += (geometry.vertex_stride_floats(geometry.format) - position_offset_floats - position_stride_floats); + } + return true; +} + +bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom) +{ + static constexpr const double epsilon = BuildVolume::BedEpsilon; + switch (volume.type()) { + case BuildVolume::Type::Rectangle: + { + BoundingBox3Base<Vec3d> build_volume = volume.bounding_volume().inflated(epsilon); + if (volume.max_print_height() == 0.0) + build_volume.max.z() = std::numeric_limits<double>::max(); + if (ignore_bottom) + build_volume.min.z() = -std::numeric_limits<double>::max(); + const BoundingBoxf3& model_box = model.get_bounding_box(); + return build_volume.contains(model_box.min) && build_volume.contains(model_box.max); + } + case BuildVolume::Type::Circle: + { + const Geometry::Circled& circle = volume.circle(); + const Vec2f c = unscaled<float>(circle.center); + const float r = unscaled<double>(circle.radius) + float(epsilon); + const float r2 = sqr(r); + return volume.max_print_height() == 0.0 ? + all_vertices_inside(model.get_geometry(), [c, r2](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2; }) : + + all_vertices_inside(model.get_geometry(), [c, r2, z = volume.max_print_height() + epsilon](const Vec3f& p) { return (to_2d(p) - c).squaredNorm() <= r2 && p.z() <= z; }); + } + case BuildVolume::Type::Convex: + //FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently. + case BuildVolume::Type::Custom: + return volume.max_print_height() == 0.0 ? + all_vertices_inside(model.get_geometry(), [&volume](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast<double>()); }) : + all_vertices_inside(model.get_geometry(), [&volume, z = volume.max_print_height() + epsilon](const Vec3f& p) { return Geometry::inside_convex_polygon(volume.top_bottom_convex_hull_decomposition_bed(), to_2d(p).cast<double>()) && p.z() <= z; }); + default: + return true; + } +} +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + GLModel::Geometry stilized_arrow(unsigned short resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) { #if !ENABLE_GLBEGIN_GLEND_REMOVAL diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 72c50ee11..3b268dab7 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -14,6 +14,9 @@ namespace Slic3r { class TriangleMesh; class Polygon; using Polygons = std::vector<Polygon>; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL +class BuildVolume; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL namespace GUI { @@ -89,6 +92,13 @@ namespace GUI { void add_vertex(const Vec3f& position, const Vec2f& tex_coord); // EVertexLayout::P3T2 void add_vertex(const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3 +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void set_vertex(size_t id, const Vec3f& position, const Vec3f& normal); // EVertexLayout::P3N3 + + void set_ushort_index(size_t id, unsigned short index); + void set_uint_index(size_t id, unsigned int index); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void add_ushort_index(unsigned short id); void add_uint_index(unsigned int id); @@ -106,7 +116,11 @@ namespace GUI { unsigned int extract_uint_index(size_t id) const; unsigned short extract_ushort_index(size_t id) const; - bool is_empty() const { return vertices.empty() || indices.empty(); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void remove_vertex(size_t id); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + + bool is_empty() const { return vertices_count() == 0 || indices_count() == 0; } size_t vertices_count() const { return vertices.size() / vertex_stride_floats(format); } size_t indices_count() const { return indices.size() / index_stride_bytes(format); } @@ -179,6 +193,16 @@ namespace GUI { std::vector<RenderData> m_render_data; #endif // ENABLE_GLBEGIN_GLEND_REMOVAL +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + // By default the vertex and index buffers data are sent to gpu at the first call to render() method. + // If you need to initialize a model from outside the main thread, so that a call to render() may happen + // before the initialization is complete, use the methods: + // disable_render() + // ... do your initialization ... + // enable_render() + // to keep the data on cpu side until needed. + bool m_render_disabled{ false }; +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL BoundingBoxf3 m_bounding_box; std::string m_filename; @@ -197,8 +221,16 @@ namespace GUI { size_t indices_size_bytes() const { return indices_count() * Geometry::index_stride_bytes(m_render_data.geometry.format); } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + const Geometry& get_geometry() const { return m_render_data.geometry; } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void init_from(Geometry&& data); +#if ENABLE_SMOOTH_NORMALS + void init_from(const TriangleMesh& mesh, bool smooth_normals = false); +#else void init_from(const TriangleMesh& mesh); +#endif // ENABLE_SMOOTH_NORMALS #else void init_from(const Geometry& data); void init_from(const indexed_triangle_set& its, const BoundingBoxf3& bbox); @@ -219,9 +251,15 @@ namespace GUI { void reset(); #if ENABLE_GLBEGIN_GLEND_REMOVAL void render(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + void render(const std::pair<size_t, size_t>& range); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL void render_instanced(unsigned int instances_vbo, unsigned int instances_count); bool is_initialized() const { return vertices_count() > 0 && indices_count() > 0; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + bool is_empty() const { return m_render_data.geometry.is_empty(); } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL #else void render() const; void render_instanced(unsigned int instances_vbo, unsigned int instances_count) const; @@ -232,6 +270,29 @@ namespace GUI { const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } const std::string& get_filename() const { return m_filename; } +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + bool is_render_disabled() const { return m_render_disabled; } + void enable_render() { m_render_disabled = false; } + void disable_render() { m_render_disabled = true; } + + size_t cpu_memory_used() const { + size_t ret = 0; + if (!m_render_data.geometry.vertices.empty()) + ret += vertices_size_bytes(); + if (!m_render_data.geometry.indices.empty()) + ret += indices_size_bytes(); + return ret; + } + size_t gpu_memory_used() const { + size_t ret = 0; + if (m_render_data.geometry.vertices.empty()) + ret += vertices_size_bytes(); + if (m_render_data.geometry.indices.empty()) + ret += indices_size_bytes(); + return ret; + } +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + private: #if ENABLE_GLBEGIN_GLEND_REMOVAL bool send_to_gpu(); @@ -240,6 +301,10 @@ namespace GUI { #endif // ENABLE_GLBEGIN_GLEND_REMOVAL }; +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + bool contains(const BuildVolume& volume, const GLModel& model, bool ignore_bottom = true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + // create an arrow with cylindrical stem and conical tip, with the given dimensions and resolution // the origin of the arrow is in the center of the stem cap // the arrow has its axis of symmetry along the Z axis and is pointing upward diff --git a/src/slic3r/GUI/GLSelectionRectangle.cpp b/src/slic3r/GUI/GLSelectionRectangle.cpp index 515da6de3..8cf3247cb 100644 --- a/src/slic3r/GUI/GLSelectionRectangle.cpp +++ b/src/slic3r/GUI/GLSelectionRectangle.cpp @@ -98,7 +98,7 @@ namespace GUI { color[1] = (m_state == Select) ? 1.0f : 0.3f; color[2] = 0.3f; glsafe(::glColor3fv(color)); -#endif // ENABLE_GLBEGIN_GLEND_REMOVAL +#endif // !ENABLE_GLBEGIN_GLEND_REMOVAL glsafe(::glDisable(GL_DEPTH_TEST)); diff --git a/src/slic3r/GUI/GalleryDialog.cpp b/src/slic3r/GUI/GalleryDialog.cpp index 1191e5c2e..0bc741c96 100644 --- a/src/slic3r/GUI/GalleryDialog.cpp +++ b/src/slic3r/GUI/GalleryDialog.cpp @@ -274,9 +274,13 @@ static void generate_thumbnail_from_model(const std::string& filename) GLVolumeCollection volumes; volumes.volumes.push_back(new GLVolume()); - GLVolume* volume = volumes.volumes[0]; + GLVolume* volume = volumes.volumes.back(); +#if ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL + volume->model.init_from(model.mesh()); +#else volume->indexed_vertex_array.load_mesh(model.mesh()); volume->indexed_vertex_array.finalize_geometry(true); +#endif // ENABLE_GLINDEXEDVERTEXARRAY_REMOVAL volume->set_instance_transformation(model.objects[0]->instances[0]->get_transformation()); volume->set_volume_transformation(model.objects[0]->volumes[0]->get_transformation()); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index e70c1111b..4b75befb6 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1444,8 +1444,8 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); + const int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); + const int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); if (fb_width == 0 || fb_height == 0) return; draw_data->ScaleClipRects(io.DisplayFramebufferScale); @@ -1488,8 +1488,7 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) // Render command lists ImVec2 pos = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { + for (int n = 0; n < draw_data->CmdListsCount; ++n) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; @@ -1497,19 +1496,14 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) glsafe(::glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)))); glsafe(::glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)))); - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; ++cmd_i) { const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; if (pcmd->UserCallback) - { // User callback (registered via ImDrawList::AddCallback) pcmd->UserCallback(cmd_list, pcmd); - } - else - { + else { ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) { // Apply scissor/clipping rectangle glsafe(::glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)));