diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 99119f566..baaa0497c 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -354,9 +354,19 @@ bool Print::has_brim() const return std::any_of(m_objects.begin(), m_objects.end(), [](PrintObject *object) { return object->has_brim(); }); } +#if ENABLE_SEQUENTIAL_LIMITS +bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons) +#else static inline bool sequential_print_horizontal_clearance_valid(const Print &print) +#endif // ENABLE_SEQUENTIAL_LIMITS { Polygons convex_hulls_other; +#if ENABLE_SEQUENTIAL_LIMITS + if (polygons != nullptr) + polygons->clear(); + std::vector intersecting_idxs; +#endif // ENABLE_SEQUENTIAL_LIMITS + std::map map_model_object_to_convex_hull; for (const PrintObject *print_object : print.objects()) { assert(! print_object->model_object()->instances.empty()); @@ -378,7 +388,7 @@ static inline bool sequential_print_horizontal_clearance_valid(const Print &prin // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision. float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)), - jtRound, float(scale_(0.1))).front()); + jtRound, scale_(0.1)).front()); #else it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id, offset(print_object->model_object()->convex_hull_2d( @@ -391,21 +401,47 @@ static inline bool sequential_print_horizontal_clearance_valid(const Print &prin } // Make a copy, so it may be rotated for instances. Polygon convex_hull0 = it_convex_hull->second; - double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation()); + const double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation()); if (std::abs(z_diff) > EPSILON) convex_hull0.rotate(z_diff); // Now we check that no instance of convex_hull intersects any of the previously checked object instances. for (const PrintInstance &instance : print_object->instances()) { Polygon convex_hull = convex_hull0; // instance.shift is a position of a centered object, while model object may not be centered. - // Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset. + // Convert the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset. convex_hull.translate(instance.shift - print_object->center_offset()); - if (! intersection(convex_hulls_other, (Polygons)convex_hull).empty()) - return false; - convex_hulls_other.emplace_back(std::move(convex_hull)); +#if ENABLE_SEQUENTIAL_LIMITS + // if output needed, collect indices (inside convex_hulls_other) of intersecting hulls + for (size_t i = 0; i < convex_hulls_other.size(); ++i) { + if (!intersection((Polygons)convex_hulls_other[i], (Polygons)convex_hull).empty()) { + if (polygons == nullptr) + return false; + else { + intersecting_idxs.emplace_back(i); + intersecting_idxs.emplace_back(convex_hulls_other.size()); + } + } + } +#else + if (!intersection(convex_hulls_other, (Polygons)convex_hull).empty()) + return false; +#endif // ENABLE_SEQUENTIAL_LIMITS + convex_hulls_other.emplace_back(std::move(convex_hull)); } } - return true; + +#if ENABLE_SEQUENTIAL_LIMITS + if (!intersecting_idxs.empty()) { + // use collected indices (inside convex_hulls_other) to update output + std::sort(intersecting_idxs.begin(), intersecting_idxs.end()); + intersecting_idxs.erase(std::unique(intersecting_idxs.begin(), intersecting_idxs.end()), intersecting_idxs.end()); + for (size_t i : intersecting_idxs) { + polygons->emplace_back(std::move(convex_hulls_other[i])); + } + return false; + } +#endif // ENABLE_SEQUENTIAL_LIMITS + return true; } static inline bool sequential_print_vertical_clearance_valid(const Print &print) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index edd8e19f1..ec48a607c 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -545,6 +545,10 @@ public: const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; } const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; } +#if ENABLE_SEQUENTIAL_LIMITS + static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr); +#endif // ENABLE_SEQUENTIAL_LIMITS + protected: // Invalidates the step, and its depending steps in Print. bool invalidate_step(PrintStep step); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index e00af1851..b35ccbc6d 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -66,6 +66,8 @@ // Enable to push object instances under the bed #define ENABLE_ALLOW_NEGATIVE_Z (1 && ENABLE_2_4_0_ALPHA0) #define DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA (1 && ENABLE_ALLOW_NEGATIVE_Z) +// Enable visualization of objects clearance for sequential prints +#define ENABLE_SEQUENTIAL_LIMITS (1 && ENABLE_2_4_0_ALPHA0) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index f3eec0080..77710c0de 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -98,12 +98,6 @@ const float Bed3D::Axes::DefaultStemLength = 25.0f; const float Bed3D::Axes::DefaultTipRadius = 2.5f * Bed3D::Axes::DefaultStemRadius; const float Bed3D::Axes::DefaultTipLength = 5.0f; -void Bed3D::Axes::set_stem_length(float length) -{ - m_stem_length = length; - m_arrow.reset(); -} - void Bed3D::Axes::render() const { auto render_axis = [this](const Transform3f& transform) { @@ -113,7 +107,10 @@ void Bed3D::Axes::render() const glsafe(::glPopMatrix()); }; - const_cast(&m_arrow)->init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); +#if ENABLE_SEQUENTIAL_LIMITS + if (!m_arrow.is_initialized()) +#endif // ENABLE_SEQUENTIAL_LIMITS + const_cast(&m_arrow)->init_from(stilized_arrow(16, DefaultTipRadius, DefaultTipLength, DefaultStemRadius, m_stem_length)); GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader == nullptr) @@ -125,18 +122,30 @@ void Bed3D::Axes::render() const shader->set_uniform("emission_factor", 0.0); // x axis +#if ENABLE_SEQUENTIAL_LIMITS + const_cast(&m_arrow)->set_color(-1, { 0.75f, 0.0f, 0.0f, 1.0f }); +#else std::array color = { 0.75f, 0.0f, 0.0f, 1.0f }; shader->set_uniform("uniform_color", color); - render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0f }).cast()); +#endif // ENABLE_SEQUENTIAL_LIMITS + render_axis(Geometry::assemble_transform(m_origin, { 0.0, 0.5 * M_PI, 0.0 }).cast()); // y axis +#if ENABLE_SEQUENTIAL_LIMITS + const_cast(&m_arrow)->set_color(-1, { 0.0f, 0.75f, 0.0f, 1.0f }); +#else color = { 0.0f, 0.75f, 0.0f, 1.0f }; shader->set_uniform("uniform_color", color); - render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0f }).cast()); +#endif // ENABLE_SEQUENTIAL_LIMITS + render_axis(Geometry::assemble_transform(m_origin, { -0.5 * M_PI, 0.0, 0.0 }).cast()); // z axis +#if ENABLE_SEQUENTIAL_LIMITS + const_cast(&m_arrow)->set_color(-1, { 0.0f, 0.0f, 0.75f, 1.0f }); +#else color = { 0.0f, 0.0f, 0.75f, 1.0f }; shader->set_uniform("uniform_color", color); +#endif // ENABLE_SEQUENTIAL_LIMITS render_axis(Geometry::assemble_transform(m_origin).cast()); shader->stop_using(); @@ -416,7 +425,10 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const } glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glDepthMask(GL_FALSE)); +#if ENABLE_SEQUENTIAL_LIMITS + if (bottom) +#endif // ENABLE_SEQUENTIAL_LIMITS + glsafe(::glDepthMask(GL_FALSE)); glsafe(::glEnable(GL_BLEND)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); @@ -461,7 +473,10 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const glsafe(::glFrontFace(GL_CCW)); glsafe(::glDisable(GL_BLEND)); - glsafe(::glDepthMask(GL_TRUE)); +#if ENABLE_SEQUENTIAL_LIMITS + if (bottom) +#endif // ENABLE_SEQUENTIAL_LIMITS + glsafe(::glDepthMask(GL_TRUE)); shader->stop_using(); } @@ -476,6 +491,10 @@ void Bed3D::render_model() const GLModel* model = const_cast(&m_model); if (model->get_filename() != m_model_filename && model->init_from_file(m_model_filename)) { +#if ENABLE_SEQUENTIAL_LIMITS + model->set_color(-1, m_model_color); +#endif // ENABLE_SEQUENTIAL_LIMITS + // move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad Vec3d shift = m_bounding_box.center(); shift(2) = -0.03; @@ -489,10 +508,12 @@ void Bed3D::render_model() const GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); if (shader != nullptr) { shader->start_using(); +#if !ENABLE_SEQUENTIAL_LIMITS shader->set_uniform("uniform_color", m_model_color); +#endif // !ENABLE_SEQUENTIAL_LIMITS shader->set_uniform("emission_factor", 0.0); ::glPushMatrix(); - ::glTranslated(m_model_offset(0), m_model_offset(1), m_model_offset(2)); + ::glTranslated(m_model_offset.x(), m_model_offset.y(), m_model_offset.z()); model->render(); ::glPopMatrix(); shader->stop_using(); diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index c2630b799..71382315d 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -17,8 +17,8 @@ class GeometryBuffer { struct Vertex { - Vec3f position = Vec3f::Zero(); - Vec2f tex_coords = Vec2f::Zero(); + Vec3f position{ Vec3f::Zero() }; + Vec2f tex_coords{ Vec2f::Zero() }; }; std::vector m_vertices; @@ -53,7 +53,10 @@ class Bed3D public: const Vec3d& get_origin() const { return m_origin; } void set_origin(const Vec3d& origin) { m_origin = origin; } - void set_stem_length(float length); + void set_stem_length(float length) { + m_stem_length = length; + m_arrow.reset(); + } float get_total_length() const { return m_stem_length + DefaultTipLength; } void render() const; }; diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 3949511f5..2ced8e601 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -205,6 +205,9 @@ void GCodeViewer::SequentialRangeCap::reset() { void GCodeViewer::SequentialView::Marker::init() { m_model.init_from(stilized_arrow(16, 2.0f, 4.0f, 1.0f, 8.0f)); +#if ENABLE_SEQUENTIAL_LIMITS + m_model.set_color(-1, { 1.0f, 1.0f, 1.0f, 0.5f }); +#endif // ENABLE_SEQUENTIAL_LIMITS } void GCodeViewer::SequentialView::Marker::set_world_position(const Vec3f& position) @@ -226,7 +229,9 @@ void GCodeViewer::SequentialView::Marker::render() const glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); shader->start_using(); +#if !ENABLE_SEQUENTIAL_LIMITS shader->set_uniform("uniform_color", m_color); +#endif // !ENABLE_SEQUENTIAL_LIMITS shader->set_uniform("emission_factor", 0.0); glsafe(::glPushMatrix()); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 48cc53ba0..d5c793e62 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -505,7 +505,9 @@ public: Vec3f m_world_position; Transform3f m_world_transform; float m_z_offset{ 0.5f }; +#if !ENABLE_SEQUENTIAL_LIMITS std::array m_color{ 1.0f, 1.0f, 1.0f, 0.5f }; +#endif // !ENABLE_SEQUENTIAL_LIMITS bool m_visible{ true }; public: @@ -514,7 +516,9 @@ public: const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); } void set_world_position(const Vec3f& position); +#if !ENABLE_SEQUENTIAL_LIMITS void set_color(const std::array& color) { m_color = color; } +#endif // !ENABLE_SEQUENTIAL_LIMITS bool is_visible() const { return m_visible; } void set_visible(bool visible) { m_visible = visible; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index baebbbf2d..8442194f2 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -789,6 +789,97 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas ImGui::PopStyleVar(2); } +#if ENABLE_SEQUENTIAL_LIMITS +void GLCanvas3D::SequentialPrintClearance::set(const Polygons& polygons, bool fill) +{ + m_render_fill = fill; + + m_perimeter.reset(); + m_fill.reset(); + if (polygons.empty()) + return; + + size_t triangles_count = 0; + for (const Polygon& poly : polygons) { + triangles_count += poly.points.size() - 2; + } + size_t vertices_count = 3 * triangles_count; + + if (fill) { + GLModel::InitializationData fill_data; + GLModel::InitializationData::Entity entity; + entity.type = GLModel::PrimitiveType::Triangles; + entity.color = { 0.3333f, 0.0f, 0.0f, 0.5f }; + entity.positions.reserve(vertices_count); + entity.normals.reserve(vertices_count); + entity.indices.reserve(vertices_count); + + ExPolygons polygons_union = union_ex(polygons); + for (const ExPolygon& poly : polygons_union) { + std::vector triangulation = triangulate_expolygon_3d(poly, false); + for (const Vec3d& v : triangulation) { + entity.positions.emplace_back(v.cast() + Vec3f(0.0f, 0.0f, 0.0125f)); // add a small positive z to avoid z-fighting + entity.normals.emplace_back(Vec3f::UnitZ()); + size_t positions_count = entity.positions.size(); + if (positions_count % 3 == 0) { + entity.indices.emplace_back(positions_count - 3); + entity.indices.emplace_back(positions_count - 2); + entity.indices.emplace_back(positions_count - 1); + } + } + } + + fill_data.entities.emplace_back(entity); + m_fill.init_from(fill_data); + } + + GLModel::InitializationData perimeter_data; + for (const Polygon& poly : polygons) { + GLModel::InitializationData::Entity ent; + ent.type = GLModel::PrimitiveType::LineLoop; + ent.positions.reserve(poly.points.size()); + ent.indices.reserve(poly.points.size()); + unsigned int id_count = 0; + for (const Point& p : poly.points) { + ent.positions.emplace_back(unscale(p.x()), unscale(p.y()), 0.025f); // add a small positive z to avoid z-fighting + ent.normals.emplace_back(Vec3f::UnitZ()); + ent.indices.emplace_back(id_count++); + } + + perimeter_data.entities.emplace_back(ent); + } + + m_perimeter.init_from(perimeter_data); +} + +void GLCanvas3D::SequentialPrintClearance::render() const +{ + std::array FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f }; + std::array NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f }; + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader == nullptr) + return; + + shader->start_using(); + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_CULL_FACE)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + const_cast(&m_perimeter)->set_color(-1, m_render_fill ? FILL_COLOR : NO_FILL_COLOR); + m_perimeter.render(); + m_fill.render(); + + glsafe(::glDisable(GL_BLEND)); + glsafe(::glEnable(GL_CULL_FACE)); + glsafe(::glDisable(GL_DEPTH_TEST)); + + shader->stop_using(); +} +#endif // ENABLE_SEQUENTIAL_LIMITS + wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent); @@ -1376,7 +1467,11 @@ void GLCanvas3D::render() _render_sla_slices(); _render_selection(); _render_bed(!camera.is_looking_downward(), true); - +#if ENABLE_SEQUENTIAL_LIMITS + if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined && + !m_layers_editing.is_enabled()) + _render_sequential_clearance(); +#endif // ENABLE_SEQUENTIAL_LIMITS #if ENABLE_RENDER_SELECTION_CENTER _render_selection_center(); #endif // ENABLE_RENDER_SELECTION_CENTER @@ -1917,7 +2012,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re bool wt = dynamic_cast(m_config->option("wipe_tower"))->value; bool co = dynamic_cast(m_config->option("complete_objects"))->value; - if ((extruders_count > 1) && wt && !co) { + if (extruders_count > 1 && wt && !co) { // Height of a print (Show at least a slab) double height = std::max(m_model->bounding_box().max(2), 10.0); @@ -2864,6 +2959,23 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.set_start_position_3D_as_invalid(); m_mouse.position = pos.cast(); + +#if ENABLE_SEQUENTIAL_LIMITS + if (evt.Dragging() && current_printer_technology() == ptFFF && fff_print()->config().complete_objects) { + switch (m_gizmos.get_current_type()) + { + case GLGizmosManager::EType::Move: + case GLGizmosManager::EType::Scale: + case GLGizmosManager::EType::Rotate: + { + update_sequential_clearance(); + break; + } + default: { break; } + } + } +#endif // ENABLE_SEQUENTIAL_LIMITS + return; } @@ -2985,6 +3097,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_mouse.drag.move_volume_idx = volume_idx; m_selection.start_dragging(); m_mouse.drag.start_position_3D = m_mouse.scene_position; +#if ENABLE_SEQUENTIAL_LIMITS + m_sequential_print_clearance_first_displacement = true; +#endif // ENABLE_SEQUENTIAL_LIMITS m_moving = true; } } @@ -3030,6 +3145,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } m_selection.translate(cur_pos - m_mouse.drag.start_position_3D); +#if ENABLE_SEQUENTIAL_LIMITS + if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) + update_sequential_clearance(); +#endif // ENABLE_SEQUENTIAL_LIMITS wxGetApp().obj_manipul()->set_dirty(); m_dirty = true; } @@ -3062,7 +3181,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // See GH issue #3816. Camera& camera = wxGetApp().plater()->get_camera(); camera.recover_from_free_camera(); - camera.rotate_on_sphere(rot.x(), rot.y(), wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA); + camera.rotate_on_sphere(rot.x(), rot.y(), current_printer_technology() != ptSLA); } m_dirty = true; @@ -3279,15 +3398,15 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) for (const std::pair& i : done) { ModelObject* m = m_model->objects[i.first]; #if ENABLE_ALLOW_NEGATIVE_Z - double shift_z = m->get_instance_min_z(i.second); + const double shift_z = m->get_instance_min_z(i.second); #if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA if (current_printer_technology() == ptSLA || shift_z > 0.0) { #else if (shift_z > 0.0) { #endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA - Vec3d shift(0.0, 0.0, -shift_z); + const Vec3d shift(0.0, 0.0, -shift_z); #else - Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); + const Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second)); #endif // ENABLE_ALLOW_NEGATIVE_Z m_selection.translate(i.first, i.second, shift); m->translate_instance(i.second, shift); @@ -3309,6 +3428,10 @@ void GLCanvas3D::do_move(const std::string& snapshot_type) if (wipe_tower_origin != Vec3d::Zero()) post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); +#if ENABLE_SEQUENTIAL_LIMITS + set_sequential_print_clearance(Polygons(), false); +#endif // ENABLE_SEQUENTIAL_LIMITS + m_dirty = true; } @@ -3655,6 +3778,93 @@ void GLCanvas3D::mouse_up_cleanup() m_canvas->ReleaseMouse(); } +#if ENABLE_SEQUENTIAL_LIMITS +void GLCanvas3D::update_sequential_clearance() +{ + if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects) + return; + + // collects instance transformations from volumes + // first define temporary cache + unsigned int instances_count = 0; + std::vector>> instance_transforms; + for (size_t obj = 0; obj < m_model->objects.size(); ++obj) { + instance_transforms.emplace_back(std::vector>()); + const ModelObject* model_object = m_model->objects[obj]; + for (size_t i = 0; i < model_object->instances.size(); ++i) { + instance_transforms[obj].emplace_back(std::optional()); + ++instances_count; + } + } + + if (instances_count == 1) + return; + + // second fill temporary cache with data from volumes + for (const GLVolume* v : m_volumes.volumes) { + if (v->is_modifier || v->is_wipe_tower) + continue; + + auto& transform = instance_transforms[v->object_idx()][v->instance_idx()]; + if (!transform.has_value()) + transform = v->get_instance_transformation(); + } + + // calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid()) + // this is done only the first time this method is called while moving the mouse, + // the results are then cached for following displacements + if (m_sequential_print_clearance_first_displacement) { + m_sequential_print_clearance.m_hull_2d_cache.clear(); + float shrink_factor = static_cast(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON)); + double mitter_limit = scale_(0.1); + m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size()); + for (size_t i = 0; i < m_model->objects.size(); ++i) { + ModelObject* model_object = m_model->objects[i]; + ModelInstance* model_instance0 = model_object->instances.front(); + Polygon hull_2d = offset(model_object->convex_hull_2d(Geometry::assemble_transform({ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), + model_instance0->get_scaling_factor(), model_instance0->get_mirror())), + // Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects + // exactly by satisfying the extruder_clearance_radius, this test will not trigger collision. + shrink_factor, + jtRound, mitter_limit).front(); + + Pointf3s& cache_hull_2d = m_sequential_print_clearance.m_hull_2d_cache.emplace_back(Pointf3s()); + cache_hull_2d.reserve(hull_2d.points.size()); + for (const Point& p : hull_2d.points) { + cache_hull_2d.emplace_back(unscale(p.x()), unscale(p.y()), 0.0); + } + } + m_sequential_print_clearance_first_displacement = false; + } + + // calculates instances 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid()) + Polygons polygons; + polygons.reserve(instances_count); + for (size_t i = 0; i < instance_transforms.size(); ++i) { + const auto& instances = instance_transforms[i]; + double rotation_z0 = instances.front()->get_rotation().z(); + for (const auto& instance : instances) { + Geometry::Transformation transformation; + const Vec3d& offset = instance->get_offset(); + transformation.set_offset({ offset.x(), offset.y(), 0.0 }); + transformation.set_rotation(Z, instance->get_rotation().z() - rotation_z0); + const Transform3d& trafo = transformation.get_matrix(); + const Pointf3s& hull_2d = m_sequential_print_clearance.m_hull_2d_cache[i]; + Points inst_pts; + inst_pts.reserve(hull_2d.size()); + for (size_t j = 0; j < hull_2d.size(); ++j) { + const Vec3d p = trafo * hull_2d[j]; + inst_pts.emplace_back(scaled(p.x()), scaled(p.y())); + } + polygons.emplace_back(Geometry::convex_hull(std::move(inst_pts))); + } + } + + // sends instances 2d hulls to be rendered + set_sequential_print_clearance(polygons, false); +} +#endif // ENABLE_SEQUENTIAL_LIMITS + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; @@ -4756,7 +4966,7 @@ void GLCanvas3D::_render_background() const bool use_error_color = false; if (wxGetApp().is_editor()) { use_error_color = m_dynamic_background_enabled && - (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA || !m_volumes.empty()); + (current_printer_technology() != ptSLA || !m_volumes.empty()); if (!m_volumes.empty()) use_error_color &= _is_any_volume_outside(); @@ -4902,6 +5112,13 @@ void GLCanvas3D::_render_selection() const m_selection.render(scale_factor); } +#if ENABLE_SEQUENTIAL_LIMITS +void GLCanvas3D::_render_sequential_clearance() const +{ + m_sequential_print_clearance.render(); +} +#endif // ENABLE_SEQUENTIAL_LIMITS + #if ENABLE_RENDER_SELECTION_CENTER void GLCanvas3D::_render_selection_center() const { @@ -5167,7 +5384,7 @@ void GLCanvas3D::_render_camera_target() const void GLCanvas3D::_render_sla_slices() const { - if (!m_use_clipping_planes || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA) + if (!m_use_clipping_planes || current_printer_technology() != ptSLA) return; const SLAPrint* print = this->sla_print(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index f7493f157..c0ef83b72 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -496,6 +496,26 @@ private: void load_arrange_settings(); +#if ENABLE_SEQUENTIAL_LIMITS + class SequentialPrintClearance + { + GLModel m_fill; + GLModel m_perimeter; + bool m_render_fill{ true }; + + std::vector m_hull_2d_cache; + + public: + void set(const Polygons& polygons, bool fill); + void render() const; + + friend class GLCanvas3D; + }; + + SequentialPrintClearance m_sequential_print_clearance; + bool m_sequential_print_clearance_first_displacement{ true }; +#endif // ENABLE_SEQUENTIAL_LIMITS + public: explicit GLCanvas3D(wxGLCanvas* canvas); ~GLCanvas3D(); @@ -737,6 +757,14 @@ public: #endif } +#if ENABLE_SEQUENTIAL_LIMITS + void set_sequential_print_clearance(const Polygons& polygons, bool fill) { m_sequential_print_clearance.set(polygons, fill); } + void update_sequential_clearance(); +#endif // ENABLE_SEQUENTIAL_LIMITS + + const Print* fff_print() const; + const SLAPrint* sla_print() const; + #if ENABLE_SCROLLABLE_LEGEND void reset_old_size() { m_old_size = { 0, 0 }; } #endif // ENABLE_SCROLLABLE_LEGEND @@ -767,6 +795,9 @@ private: void _render_objects() const; void _render_gcode() const; void _render_selection() const; +#if ENABLE_SEQUENTIAL_LIMITS + void _render_sequential_clearance() const; +#endif // ENABLE_SEQUENTIAL_LIMITS #if ENABLE_RENDER_SELECTION_CENTER void _render_selection_center() const; #endif // ENABLE_RENDER_SELECTION_CENTER @@ -843,10 +874,6 @@ private: float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); } static std::vector _parse_colors(const std::vector& colors); - -public: - const Print* fff_print() const; - const SLAPrint* sla_print() const; }; } // namespace GUI diff --git a/src/slic3r/GUI/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index e738aa3c4..5452a1865 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -2,6 +2,11 @@ #include "GLModel.hpp" #include "3DScene.hpp" +#if ENABLE_SEQUENTIAL_LIMITS +#include "GUI_App.hpp" +#include "GLShader.hpp" +#endif // ENABLE_SEQUENTIAL_LIMITS + #include "libslic3r/TriangleMesh.hpp" #include "libslic3r/Model.hpp" @@ -13,6 +18,45 @@ namespace Slic3r { namespace GUI { +#if ENABLE_SEQUENTIAL_LIMITS +void GLModel::init_from(const InitializationData& data) +{ + if (!m_render_data.empty()) // call reset() if you want to reuse this model + return; + + for (const InitializationData::Entity& entity : data.entities) { + if (entity.positions.empty() || entity.indices.empty()) + continue; + + assert(entity.normals.empty() || entity.normals.size() == entity.positions.size()); + + RenderData rdata; + rdata.type = entity.type; + rdata.color = entity.color; + + // vertices/normals data + std::vector vertices(6 * entity.positions.size()); + for (size_t i = 0; i < entity.positions.size(); ++i) { + const size_t offset = i * 6; + ::memcpy(static_cast(&vertices[offset]), static_cast(entity.positions[i].data()), 3 * sizeof(float)); + if (!entity.normals.empty()) + ::memcpy(static_cast(&vertices[3 + offset]), static_cast(entity.normals[i].data()), 3 * sizeof(float)); + } + + // indices data + std::vector indices = entity.indices; + + rdata.indices_count = static_cast(indices.size()); + + // update bounding box + for (size_t i = 0; i < entity.positions.size(); ++i) { + m_bounding_box.merge(entity.positions[i].cast()); + } + + send_to_gpu(rdata, vertices, indices); + m_render_data.emplace_back(rdata); + } +#else void GLModel::init_from(const GLModelInitializationData& data) { assert(!data.positions.empty() && !data.triangles.empty()); @@ -44,10 +88,41 @@ void GLModel::init_from(const GLModelInitializationData& data) } send_to_gpu(vertices, indices); +#endif // ENABLE_SEQUENTIAL_LIMITS } void GLModel::init_from(const TriangleMesh& mesh) { +#if ENABLE_SEQUENTIAL_LIMITS + if (!m_render_data.empty()) // call reset() if you want to reuse this model + return; + + RenderData data; + data.type = PrimitiveType::Triangles; + + std::vector vertices = std::vector(18 * mesh.stl.stats.number_of_facets); + std::vector indices = std::vector(3 * mesh.stl.stats.number_of_facets); + + unsigned int vertices_count = 0; + for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) { + const stl_facet& facet = mesh.stl.facet_start[i]; + for (size_t j = 0; j < 3; ++j) { + size_t offset = i * 18 + j * 6; + ::memcpy(static_cast(&vertices[offset]), static_cast(facet.vertex[j].data()), 3 * sizeof(float)); + ::memcpy(static_cast(&vertices[3 + offset]), static_cast(facet.normal.data()), 3 * sizeof(float)); + } + for (size_t j = 0; j < 3; ++j) { + indices[i * 3 + j] = vertices_count + j; + } + vertices_count += 3; + } + + data.indices_count = static_cast(indices.size()); + m_bounding_box = mesh.bounding_box(); + + send_to_gpu(data, vertices, indices); + m_render_data.emplace_back(data); +#else if (m_vbo_id > 0) // call reset() if you want to reuse this model return; @@ -72,6 +147,7 @@ void GLModel::init_from(const TriangleMesh& mesh) m_bounding_box = mesh.bounding_box(); send_to_gpu(vertices, indices); +#endif // ENABLE_SEQUENTIAL_LIMITS } bool GLModel::init_from_file(const std::string& filename) @@ -99,8 +175,29 @@ bool GLModel::init_from_file(const std::string& filename) return true; } +#if ENABLE_SEQUENTIAL_LIMITS +void GLModel::set_color(int entity_id, const std::array& color) +{ + for (size_t i = 0; i < m_render_data.size(); ++i) { + if (entity_id == -1 || static_cast(i) == entity_id) + m_render_data[i].color = color; + } +} +#endif // ENABLE_SEQUENTIAL_LIMITS + void GLModel::reset() { +#if ENABLE_SEQUENTIAL_LIMITS + for (RenderData& data : m_render_data) { + // release gpu memory + if (data.ibo_id > 0) + glsafe(::glDeleteBuffers(1, &data.ibo_id)); + if (data.vbo_id > 0) + glsafe(::glDeleteBuffers(1, &data.vbo_id)); + } + + m_render_data.clear(); +#else // release gpu memory if (m_ibo_id > 0) { glsafe(::glDeleteBuffers(1, &m_ibo_id)); @@ -113,12 +210,49 @@ void GLModel::reset() } m_indices_count = 0; +#endif // ENABLE_SEQUENTIAL_LIMITS m_bounding_box = BoundingBoxf3(); m_filename = std::string(); } void GLModel::render() const { +#if ENABLE_SEQUENTIAL_LIMITS + for (const RenderData& data : m_render_data) { + if (data.vbo_id == 0 || data.ibo_id == 0) + continue; + + GLenum mode; + switch (data.type) + { + default: + case PrimitiveType::Triangles: { mode = GL_TRIANGLES; break; } + case PrimitiveType::Lines: { mode = GL_LINES; break; } + case PrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; } + case PrimitiveType::LineLoop: { mode = GL_LINE_LOOP; break; } + } + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id)); + glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)0)); + glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + + GLShaderProgram* shader = wxGetApp().get_current_shader(); + if (shader != nullptr) + shader->set_uniform("uniform_color", data.color); + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id)); + glsafe(::glDrawElements(mode, static_cast(data.indices_count), GL_UNSIGNED_INT, (const void*)0)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + } +#else if (m_vbo_id == 0 || m_ibo_id == 0) return; @@ -137,8 +271,28 @@ void GLModel::render() const glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +#endif // ENABLE_SEQUENTIAL_LIMITS } +#if ENABLE_SEQUENTIAL_LIMITS +void GLModel::send_to_gpu(RenderData& data, const std::vector& vertices, const std::vector& indices) +{ + assert(data.vbo_id == 0); + assert(data.ibo_id == 0); + + // vertex data -> send to gpu + glsafe(::glGenBuffers(1, &data.vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + // indices data -> send to gpu + glsafe(::glGenBuffers(1, &data.ibo_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} +#else void GLModel::send_to_gpu(const std::vector& vertices, const std::vector& indices) { // vertex data -> send to gpu @@ -153,60 +307,138 @@ void GLModel::send_to_gpu(const std::vector& vertices, const std::vector< glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); } +#endif // ENABLE_SEQUENTIAL_LIMITS +#if ENABLE_SEQUENTIAL_LIMITS +GLModel::InitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) +{ + auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) { + entity.positions.emplace_back(position); + entity.normals.emplace_back(normal); + }; + auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { + entity.indices.emplace_back(v1); + entity.indices.emplace_back(v2); + entity.indices.emplace_back(v3); + }; +#else GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height) { auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { data.positions.emplace_back(position); data.normals.emplace_back(normal); }; +#endif // ENABLE_SEQUENTIAL_LIMITS resolution = std::max(4, resolution); +#if ENABLE_SEQUENTIAL_LIMITS + GLModel::InitializationData data; + GLModel::InitializationData::Entity entity; + entity.type = GLModel::PrimitiveType::Triangles; +#else GLModelInitializationData data; +#endif // ENABLE_SEQUENTIAL_LIMITS const float angle_step = 2.0f * M_PI / static_cast(resolution); std::vector cosines(resolution); std::vector sines(resolution); - for (int i = 0; i < resolution; ++i) - { - float angle = angle_step * static_cast(i); + for (int i = 0; i < resolution; ++i) { + const float angle = angle_step * static_cast(i); cosines[i] = ::cos(angle); sines[i] = -::sin(angle); } const float total_height = tip_height + stem_height; +#if ENABLE_SEQUENTIAL_LIMITS + // tip vertices/normals + append_vertex(entity, { 0.0f, 0.0f, total_height }, Vec3f::UnitZ()); + for (int i = 0; i < resolution; ++i) { + append_vertex(entity, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); + } + + // tip triangles + for (int i = 0; i < resolution; ++i) { + const int v3 = (i < resolution - 1) ? i + 2 : 1; + append_indices(entity, 0, i + 1, v3); + } + + // tip cap outer perimeter vertices + for (int i = 0; i < resolution; ++i) { + append_vertex(entity, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); + } + + // tip cap inner perimeter vertices + for (int i = 0; i < resolution; ++i) { + append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); + } + + // tip cap triangles + for (int i = 0; i < resolution; ++i) { + const int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1; + const int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1; + append_indices(entity, i + resolution + 1, v3, v2); + append_indices(entity, i + resolution + 1, i + 2 * resolution + 1, v3); + } + + // stem bottom vertices + for (int i = 0; i < resolution; ++i) { + append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); + } + + // stem top vertices + for (int i = 0; i < resolution; ++i) { + append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, { sines[i], cosines[i], 0.0f }); + } + + // stem triangles + for (int i = 0; i < resolution; ++i) { + const int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1; + const int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1; + append_indices(entity, i + 3 * resolution + 1, v3, v2); + append_indices(entity, i + 3 * resolution + 1, i + 4 * resolution + 1, v3); + } + + // stem cap vertices + append_vertex(entity, Vec3f::Zero(), -Vec3f::UnitZ()); + for (int i = 0; i < resolution; ++i) { + append_vertex(entity, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, -Vec3f::UnitZ()); + } + + // stem cap triangles + for (int i = 0; i < resolution; ++i) { + const int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2; + append_indices(entity, 5 * resolution + 1, v3, i + 5 * resolution + 2); + } + + data.entities.emplace_back(entity); +#else // tip vertices/normals append_vertex(data, { 0.0f, 0.0f, total_height }, Vec3f::UnitZ()); - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { append_vertex(data, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); } // tip triangles - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { int v3 = (i < resolution - 1) ? i + 2 : 1; data.triangles.emplace_back(0, i + 1, v3); } // tip cap outer perimeter vertices - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { append_vertex(data, { tip_radius * sines[i], tip_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); } // tip cap inner perimeter vertices - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, -Vec3f::UnitZ()); } // tip cap triangles - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { int v2 = (i < resolution - 1) ? i + resolution + 2 : resolution + 1; int v3 = (i < resolution - 1) ? i + 2 * resolution + 2 : 2 * resolution + 1; data.triangles.emplace_back(i + resolution + 1, v3, v2); @@ -214,20 +446,17 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float } // stem bottom vertices - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], stem_height }, { sines[i], cosines[i], 0.0f }); } // stem top vertices - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, { sines[i], cosines[i], 0.0f }); } // stem triangles - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { int v2 = (i < resolution - 1) ? i + 3 * resolution + 2 : 3 * resolution + 1; int v3 = (i < resolution - 1) ? i + 4 * resolution + 2 : 4 * resolution + 1; data.triangles.emplace_back(i + 3 * resolution + 1, v3, v2); @@ -236,31 +465,50 @@ GLModelInitializationData stilized_arrow(int resolution, float tip_radius, float // stem cap vertices append_vertex(data, Vec3f::Zero(), -Vec3f::UnitZ()); - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { append_vertex(data, { stem_radius * sines[i], stem_radius * cosines[i], 0.0f }, -Vec3f::UnitZ()); } // stem cap triangles - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { int v3 = (i < resolution - 1) ? i + 5 * resolution + 3 : 5 * resolution + 2; data.triangles.emplace_back(5 * resolution + 1, v3, i + 5 * resolution + 2); } +#endif // ENABLE_SEQUENTIAL_LIMITS return data; } +#if ENABLE_SEQUENTIAL_LIMITS +GLModel::InitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness) +{ + auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) { + entity.positions.emplace_back(position); + entity.normals.emplace_back(normal); + }; + auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { + entity.indices.emplace_back(v1); + entity.indices.emplace_back(v2); + entity.indices.emplace_back(v3); + }; +#else GLModelInitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness) { auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { data.positions.emplace_back(position); data.normals.emplace_back(normal); }; +#endif // ENABLE_SEQUENTIAL_LIMITS resolution = std::max(2, resolution); +#if ENABLE_SEQUENTIAL_LIMITS + GLModel::InitializationData data; + GLModel::InitializationData::Entity entity; + entity.type = GLModel::PrimitiveType::Triangles; +#else GLModelInitializationData data; +#endif // ENABLE_SEQUENTIAL_LIMITS const float half_thickness = 0.5f * thickness; const float half_stem_width = 0.5f * stem_width; @@ -270,6 +518,153 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip const float inner_radius = radius - half_stem_width; const float step_angle = 0.5f * PI / static_cast(resolution); +#if ENABLE_SEQUENTIAL_LIMITS + // tip + // top face vertices + append_vertex(entity, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitZ()); + append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitZ()); + append_vertex(entity, { -tip_height, radius, half_thickness }, Vec3f::UnitZ()); + append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitZ()); + append_vertex(entity, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitZ()); + + // top face triangles + append_indices(entity, 0, 1, 2); + append_indices(entity, 0, 2, 4); + append_indices(entity, 4, 2, 3); + + // bottom face vertices + append_vertex(entity, { 0.0f, outer_radius, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(entity, { -tip_height, radius, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(entity, { 0.0f, inner_radius, -half_thickness }, -Vec3f::UnitZ()); + + // bottom face triangles + append_indices(entity, 5, 7, 6); + append_indices(entity, 5, 9, 7); + append_indices(entity, 9, 8, 7); + + // side faces vertices + append_vertex(entity, { 0.0f, outer_radius, -half_thickness }, Vec3f::UnitX()); + append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, Vec3f::UnitX()); + append_vertex(entity, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitX()); + append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, Vec3f::UnitX()); + + Vec3f normal(-half_tip_width, tip_height, 0.0f); + normal.normalize(); + append_vertex(entity, { 0.0f, radius + half_tip_width, -half_thickness }, normal); + append_vertex(entity, { -tip_height, radius, -half_thickness }, normal); + append_vertex(entity, { 0.0f, radius + half_tip_width, half_thickness }, normal); + append_vertex(entity, { -tip_height, radius, half_thickness }, normal); + + normal = Vec3f(-half_tip_width, -tip_height, 0.0f); + normal.normalize(); + append_vertex(entity, { -tip_height, radius, -half_thickness }, normal); + append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, normal); + append_vertex(entity, { -tip_height, radius, half_thickness }, normal); + append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, normal); + + append_vertex(entity, { 0.0f, radius - half_tip_width, -half_thickness }, Vec3f::UnitX()); + append_vertex(entity, { 0.0f, inner_radius, -half_thickness }, Vec3f::UnitX()); + append_vertex(entity, { 0.0f, radius - half_tip_width, half_thickness }, Vec3f::UnitX()); + append_vertex(entity, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitX()); + + // side face triangles + for (int i = 0; i < 4; ++i) { + const int ii = i * 4; + append_indices(entity, 10 + ii, 11 + ii, 13 + ii); + append_indices(entity, 10 + ii, 13 + ii, 12 + ii); + } + + // stem + // top face vertices + for (int i = 0; i <= resolution; ++i) { + const float angle = static_cast(i) * step_angle; + append_vertex(entity, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); + } + + for (int i = 0; i <= resolution; ++i) { + const float angle = static_cast(i) * step_angle; + append_vertex(entity, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); + } + + // top face triangles + for (int i = 0; i < resolution; ++i) { + append_indices(entity, 26 + i, 27 + i, 27 + resolution + i); + append_indices(entity, 27 + i, 28 + resolution + i, 27 + resolution + i); + } + + // bottom face vertices + for (int i = 0; i <= resolution; ++i) { + const float angle = static_cast(i) * step_angle; + append_vertex(entity, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); + } + + for (int i = 0; i <= resolution; ++i) { + const float angle = static_cast(i) * step_angle; + append_vertex(entity, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); + } + + // bottom face triangles + for (int i = 0; i < resolution; ++i) { + append_indices(entity, 28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i); + append_indices(entity, 29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i); + } + + // side faces vertices and triangles + for (int i = 0; i <= resolution; ++i) { + const float angle = static_cast(i) * step_angle; + const float c = ::cos(angle); + const float s = ::sin(angle); + append_vertex(entity, { inner_radius * s, inner_radius * c, -half_thickness }, { -s, -c, 0.0f }); + } + + for (int i = 0; i <= resolution; ++i) { + const float angle = static_cast(i) * step_angle; + const float c = ::cos(angle); + const float s = ::sin(angle); + append_vertex(entity, { inner_radius * s, inner_radius * c, half_thickness }, { -s, -c, 0.0f }); + } + + int first_id = 26 + 4 * (resolution + 1); + for (int i = 0; i < resolution; ++i) { + const int ii = first_id + i; + append_indices(entity, ii, ii + 1, ii + resolution + 2); + append_indices(entity, ii, ii + resolution + 2, ii + resolution + 1); + } + + append_vertex(entity, { inner_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { outer_radius, 0.0f, -half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { inner_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { outer_radius, 0.0f, half_thickness }, -Vec3f::UnitY()); + + first_id = 26 + 6 * (resolution + 1); + append_indices(entity, first_id, first_id + 1, first_id + 3); + append_indices(entity, first_id, first_id + 3, first_id + 2); + + for (int i = resolution; i >= 0; --i) { + const float angle = static_cast(i) * step_angle; + const float c = ::cos(angle); + const float s = ::sin(angle); + append_vertex(entity, { outer_radius * s, outer_radius * c, -half_thickness }, { s, c, 0.0f }); + } + + for (int i = resolution; i >= 0; --i) { + const float angle = static_cast(i) * step_angle; + const float c = ::cos(angle); + const float s = ::sin(angle); + append_vertex(entity, { outer_radius * s, outer_radius * c, +half_thickness }, { s, c, 0.0f }); + } + + first_id = 30 + 6 * (resolution + 1); + for (int i = 0; i < resolution; ++i) { + const int ii = first_id + i; + append_indices(entity, ii, ii + 1, ii + resolution + 2); + append_indices(entity, ii, ii + resolution + 2, ii + resolution + 1); + } + + data.entities.emplace_back(entity); +#else // tip // top face vertices append_vertex(data, { 0.0f, outer_radius, half_thickness }, Vec3f::UnitZ()); @@ -321,8 +716,7 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip append_vertex(data, { 0.0f, inner_radius, half_thickness }, Vec3f::UnitX()); // side face triangles - for (int i = 0; i < 4; ++i) - { + for (int i = 0; i < 4; ++i) { int ii = i * 4; data.triangles.emplace_back(10 + ii, 11 + ii, 13 + ii); data.triangles.emplace_back(10 + ii, 13 + ii, 12 + ii); @@ -330,56 +724,48 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip // stem // top face vertices - for (int i = 0; i <= resolution; ++i) - { + for (int i = 0; i <= resolution; ++i) { float angle = static_cast(i) * step_angle; append_vertex(data, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); } - for (int i = 0; i <= resolution; ++i) - { + for (int i = 0; i <= resolution; ++i) { float angle = static_cast(i) * step_angle; append_vertex(data, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), half_thickness }, Vec3f::UnitZ()); } // top face triangles - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { data.triangles.emplace_back(26 + i, 27 + i, 27 + resolution + i); data.triangles.emplace_back(27 + i, 28 + resolution + i, 27 + resolution + i); } // bottom face vertices - for (int i = 0; i <= resolution; ++i) - { + for (int i = 0; i <= resolution; ++i) { float angle = static_cast(i) * step_angle; append_vertex(data, { inner_radius * ::sin(angle), inner_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); } - for (int i = 0; i <= resolution; ++i) - { + for (int i = 0; i <= resolution; ++i) { float angle = static_cast(i) * step_angle; append_vertex(data, { outer_radius * ::sin(angle), outer_radius * ::cos(angle), -half_thickness }, -Vec3f::UnitZ()); } // bottom face triangles - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { data.triangles.emplace_back(28 + 2 * resolution + i, 29 + 3 * resolution + i, 29 + 2 * resolution + i); data.triangles.emplace_back(29 + 2 * resolution + i, 29 + 3 * resolution + i, 30 + 3 * resolution + i); } // side faces vertices and triangles - for (int i = 0; i <= resolution; ++i) - { + for (int i = 0; i <= resolution; ++i) { float angle = static_cast(i) * step_angle; float c = ::cos(angle); float s = ::sin(angle); append_vertex(data, { inner_radius * s, inner_radius * c, -half_thickness }, { -s, -c, 0.0f }); } - for (int i = 0; i <= resolution; ++i) - { + for (int i = 0; i <= resolution; ++i) { float angle = static_cast(i) * step_angle; float c = ::cos(angle); float s = ::sin(angle); @@ -387,8 +773,7 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip } int first_id = 26 + 4 * (resolution + 1); - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { int ii = first_id + i; data.triangles.emplace_back(ii, ii + 1, ii + resolution + 2); data.triangles.emplace_back(ii, ii + resolution + 2, ii + resolution + 1); @@ -403,16 +788,14 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip data.triangles.emplace_back(first_id, first_id + 1, first_id + 3); data.triangles.emplace_back(first_id, first_id + 3, first_id + 2); - for (int i = resolution; i >= 0; --i) - { + for (int i = resolution; i >= 0; --i) { float angle = static_cast(i) * step_angle; float c = ::cos(angle); float s = ::sin(angle); append_vertex(data, { outer_radius * s, outer_radius * c, -half_thickness }, { s, c, 0.0f }); } - for (int i = resolution; i >= 0; --i) - { + for (int i = resolution; i >= 0; --i) { float angle = static_cast(i) * step_angle; float c = ::cos(angle); float s = ::sin(angle); @@ -420,16 +803,33 @@ GLModelInitializationData circular_arrow(int resolution, float radius, float tip } first_id = 30 + 6 * (resolution + 1); - for (int i = 0; i < resolution; ++i) - { + for (int i = 0; i < resolution; ++i) { int ii = first_id + i; data.triangles.emplace_back(ii, ii + 1, ii + resolution + 2); data.triangles.emplace_back(ii, ii + resolution + 2, ii + resolution + 1); } +#endif // ENABLE_SEQUENTIAL_LIMITS return data; } +#if ENABLE_SEQUENTIAL_LIMITS +GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness) +{ + auto append_vertex = [](GLModel::InitializationData::Entity& entity, const Vec3f& position, const Vec3f& normal) { + entity.positions.emplace_back(position); + entity.normals.emplace_back(normal); + }; + auto append_indices = [](GLModel::InitializationData::Entity& entity, unsigned int v1, unsigned int v2, unsigned int v3) { + entity.indices.emplace_back(v1); + entity.indices.emplace_back(v2); + entity.indices.emplace_back(v3); + }; + + GLModel::InitializationData data; + GLModel::InitializationData::Entity entity; + entity.type = GLModel::PrimitiveType::Triangles; +#else GLModelInitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness) { auto append_vertex = [](GLModelInitializationData& data, const Vec3f& position, const Vec3f& normal) { @@ -438,6 +838,7 @@ GLModelInitializationData straight_arrow(float tip_width, float tip_height, floa }; GLModelInitializationData data; +#endif // ENABLE_SEQUENTIAL_LIMITS const float half_thickness = 0.5f * thickness; const float half_stem_width = 0.5f * stem_width; @@ -445,6 +846,87 @@ GLModelInitializationData straight_arrow(float tip_width, float tip_height, floa const float total_height = tip_height + stem_height; // top face vertices +#if ENABLE_SEQUENTIAL_LIMITS + append_vertex(entity, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); + append_vertex(entity, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(entity, { half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(entity, { 0.0, total_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); + append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); + + // top face triangles + append_indices(entity, 0, 1, 6); + append_indices(entity, 6, 1, 5); + append_indices(entity, 4, 5, 3); + append_indices(entity, 5, 1, 3); + append_indices(entity, 1, 2, 3); + + // bottom face vertices + append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(entity, { 0.0, total_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitZ()); + append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitZ()); + + // bottom face triangles + append_indices(entity, 7, 13, 8); + append_indices(entity, 13, 12, 8); + append_indices(entity, 12, 11, 10); + append_indices(entity, 8, 12, 10); + append_indices(entity, 9, 8, 10); + + // side faces vertices + append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, Vec3f::UnitX()); + append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, Vec3f::UnitX()); + append_vertex(entity, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitX()); + append_vertex(entity, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitX()); + + append_vertex(entity, { half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); + + Vec3f normal(tip_height, half_tip_width, 0.0f); + normal.normalize(); + append_vertex(entity, { half_tip_width, stem_height, -half_thickness }, normal); + append_vertex(entity, { 0.0, total_height, -half_thickness }, normal); + append_vertex(entity, { half_tip_width, stem_height, half_thickness }, normal); + append_vertex(entity, { 0.0, total_height, half_thickness }, normal); + + normal = Vec3f(-tip_height, half_tip_width, 0.0f); + normal.normalize(); + append_vertex(entity, { 0.0, total_height, -half_thickness }, normal); + append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, normal); + append_vertex(entity, { 0.0, total_height, half_thickness }, normal); + append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, normal); + + append_vertex(entity, { -half_tip_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { -half_tip_width, stem_height, half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitY()); + + append_vertex(entity, { -half_stem_width, stem_height, -half_thickness }, -Vec3f::UnitX()); + append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitX()); + append_vertex(entity, { -half_stem_width, stem_height, half_thickness }, -Vec3f::UnitX()); + append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitX()); + + append_vertex(entity, { -half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { half_stem_width, 0.0, -half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { -half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); + append_vertex(entity, { half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); + + // side face triangles + for (int i = 0; i < 7; ++i) { + const int ii = i * 4; + append_indices(entity, 14 + ii, 15 + ii, 17 + ii); + append_indices(entity, 14 + ii, 17 + ii, 16 + ii); + } + + data.entities.emplace_back(entity); +#else append_vertex(data, { half_stem_width, 0.0, half_thickness }, Vec3f::UnitZ()); append_vertex(data, { half_stem_width, stem_height, half_thickness }, Vec3f::UnitZ()); append_vertex(data, { half_tip_width, stem_height, half_thickness }, Vec3f::UnitZ()); @@ -517,12 +999,12 @@ GLModelInitializationData straight_arrow(float tip_width, float tip_height, floa append_vertex(data, { half_stem_width, 0.0, half_thickness }, -Vec3f::UnitY()); // side face triangles - for (int i = 0; i < 7; ++i) - { + for (int i = 0; i < 7; ++i) { int ii = i * 4; data.triangles.emplace_back(14 + ii, 15 + ii, 17 + ii); data.triangles.emplace_back(14 + ii, 17 + ii, 16 + ii); } +#endif // ENABLE_SEQUENTIAL_LIMITS return data; } diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 0b4a69bdb..8e1e3dfd5 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -12,40 +12,115 @@ class TriangleMesh; namespace GUI { +#if !ENABLE_SEQUENTIAL_LIMITS struct GLModelInitializationData { std::vector positions; std::vector normals; std::vector triangles; }; +#endif // !ENABLE_SEQUENTIAL_LIMITS class GLModel { +#if ENABLE_SEQUENTIAL_LIMITS + public: + enum class PrimitiveType : unsigned char + { + Triangles, + Lines, + LineStrip, + LineLoop + }; + + struct RenderData + { + PrimitiveType type; + unsigned int vbo_id{ 0 }; + unsigned int ibo_id{ 0 }; + size_t indices_count{ 0 }; + std::array color{ 1.0f, 1.0f, 1.0f, 1.0f }; + }; + + struct InitializationData + { + struct Entity + { + PrimitiveType type; + std::vector positions; + std::vector normals; + std::vector indices; + std::array color{ 1.0f, 1.0f, 1.0f, 1.0f }; + }; + + std::vector entities; + }; + + private: + std::vector m_render_data; +#else unsigned int m_vbo_id{ 0 }; unsigned int m_ibo_id{ 0 }; size_t m_indices_count{ 0 }; +#endif // ENABLE_SEQUENTIAL_LIMITS BoundingBoxf3 m_bounding_box; std::string m_filename; public: + GLModel() = default; virtual ~GLModel() { reset(); } +#if ENABLE_SEQUENTIAL_LIMITS + void init_from(const InitializationData& data); +#else void init_from(const GLModelInitializationData& data); +#endif // ENABLE_SEQUENTIAL_LIMITS void init_from(const TriangleMesh& mesh); bool init_from_file(const std::string& filename); + +#if ENABLE_SEQUENTIAL_LIMITS + // if entity_id == -1 set the color of all entities + void set_color(int entity_id, const std::array& color); +#endif // ENABLE_SEQUENTIAL_LIMITS + void reset(); void render() const; - const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } +#if ENABLE_SEQUENTIAL_LIMITS + bool is_initialized() const { return !m_render_data.empty(); } +#endif // ENABLE_SEQUENTIAL_LIMITS + const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } const std::string& get_filename() const { return m_filename; } private: +#if ENABLE_SEQUENTIAL_LIMITS + void send_to_gpu(RenderData& data, const std::vector& vertices, const std::vector& indices); +#else void send_to_gpu(const std::vector& vertices, const std::vector& indices); +#endif // ENABLE_SEQUENTIAL_LIMITS }; +#if ENABLE_SEQUENTIAL_LIMITS + // 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 + // used to render bed axes and sequential marker + GLModel::InitializationData stilized_arrow(int resolution, float tip_radius, float tip_height, float stem_radius, float stem_height); + // create an arrow whose stem is a quarter of circle, with the given dimensions and resolution + // the origin of the arrow is in the center of the circle + // the arrow is contained in the 1st quadrant of the XY plane and is pointing counterclockwise + // used to render sidebar hints for rotations + GLModel::InitializationData circular_arrow(int resolution, float radius, float tip_height, float tip_width, float stem_width, float thickness); + + // create an arrow with the given dimensions + // the origin of the arrow is in the center of the stem cap + // the arrow is contained in XY plane and has its main axis along the Y axis + // used to render sidebar hints for position and scale + GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness); +#else // 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 @@ -60,6 +135,7 @@ namespace GUI { // the origin of the arrow is in the center of the stem cap // the arrow is contained in XY plane and has its main axis along the Y axis GLModelInitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness); +#endif // ENABLE_SEQUENTIAL_LIMITS } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 98729bd7f..07eb49379 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -63,15 +63,19 @@ void GLGizmoBase::Grabber::render(float size, const std::array& render GLShaderProgram* shader = picking ? nullptr : wxGetApp().get_current_shader(); if (shader != nullptr) +#if ENABLE_SEQUENTIAL_LIMITS + const_cast(&cube)->set_color(-1, render_color); +#else shader->set_uniform("uniform_color", render_color); +#endif // ENABLE_SEQUENTIAL_LIMITS else glsafe(::glColor4fv(render_color.data())); // picking glsafe(::glPushMatrix()); - glsafe(::glTranslated(center(0), center(1), center(2))); - glsafe(::glRotated(Geometry::rad2deg(angles(2)), 0.0, 0.0, 1.0)); - glsafe(::glRotated(Geometry::rad2deg(angles(1)), 0.0, 1.0, 0.0)); - glsafe(::glRotated(Geometry::rad2deg(angles(0)), 1.0, 0.0, 0.0)); + glsafe(::glTranslated(center.x(), center.y(), center.z())); + glsafe(::glRotated(Geometry::rad2deg(angles.z()), 0.0, 0.0, 1.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.y()), 0.0, 1.0, 0.0)); + glsafe(::glRotated(Geometry::rad2deg(angles.x()), 1.0, 0.0, 0.0)); glsafe(::glScaled(fullsize, fullsize, fullsize)); cube.render(); glsafe(::glPopMatrix()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 486d945ba..b4c0b7a52 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -150,7 +150,11 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons } if (shader && ! picking) +#if ENABLE_SEQUENTIAL_LIMITS + const_cast(&m_vbo_cylinder)->set_color(-1 , render_color); +#else shader->set_uniform("uniform_color", render_color); +#endif // ENABLE_SEQUENTIAL_LIMITS else // picking glsafe(::glColor4fv(render_color.data())); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 84282f0a8..d92f339d7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -80,11 +80,11 @@ void GLGizmoMove3D::on_stop_dragging() void GLGizmoMove3D::on_update(const UpdateData& data) { if (m_hover_id == 0) - m_displacement(0) = calc_projection(data); + m_displacement.x() = calc_projection(data); else if (m_hover_id == 1) - m_displacement(1) = calc_projection(data); + m_displacement.y() = calc_projection(data); else if (m_hover_id == 2) - m_displacement(2) = calc_projection(data); + m_displacement.z() = calc_projection(data); } void GLGizmoMove3D::on_render() const @@ -98,15 +98,15 @@ void GLGizmoMove3D::on_render() const const Vec3d& center = box.center(); // x axis - m_grabbers[0].center = Vec3d(box.max.x() + Offset, center.y(), center.z()); + m_grabbers[0].center = { box.max.x() + Offset, center.y(), center.z() }; m_grabbers[0].color = AXES_COLOR[0]; // y axis - m_grabbers[1].center = Vec3d(center.x(), box.max.y() + Offset, center.z()); + m_grabbers[1].center = { center.x(), box.max.y() + Offset, center.z() }; m_grabbers[1].color = AXES_COLOR[1]; // z axis - m_grabbers[2].center = Vec3d(center.x(), center.y(), box.max.z() + Offset); + m_grabbers[2].center = { center.x(), center.y(), box.max.z() + Offset }; m_grabbers[2].color = AXES_COLOR[2]; glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f)); @@ -138,9 +138,15 @@ void GLGizmoMove3D::on_render() const ::glVertex3dv(m_grabbers[m_hover_id].center.data()); glsafe(::glEnd()); - // draw grabber - float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); - m_grabbers[m_hover_id].render(true, mean_size); + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1); + // draw grabber + float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0); + m_grabbers[m_hover_id].render(true, mean_size); + shader->stop_using(); + } render_grabber_extension((Axis)m_hover_id, box, false); } } @@ -202,7 +208,11 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box if (! picking) { shader->start_using(); shader->set_uniform("emission_factor", 0.1); +#if ENABLE_SEQUENTIAL_LIMITS + const_cast(&m_vbo_cone)->set_color(-1, color); +#else shader->set_uniform("uniform_color", color); +#endif // ENABLE_SEQUENTIAL_LIMITS } else glsafe(::glColor4fv(color.data())); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index f72ea014d..0f0c74652 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -339,7 +339,11 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick if (! picking) { shader->start_using(); shader->set_uniform("emission_factor", 0.1); +#if ENABLE_SEQUENTIAL_LIMITS + const_cast(&m_cone)->set_color(-1, color); +#else shader->set_uniform("uniform_color", color); +#endif // ENABLE_SEQUENTIAL_LIMITS } else glsafe(::glColor4fv(color.data())); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 88a5ba169..3fc347222 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -120,16 +120,14 @@ protected: void on_stop_dragging() override; void on_update(const UpdateData& data) override { - for (GLGizmoRotate& g : m_gizmos) - { + for (GLGizmoRotate& g : m_gizmos) { g.update(data); } } void on_render() const override; void on_render_for_picking() const override { - for (const GLGizmoRotate& g : m_gizmos) - { + for (const GLGizmoRotate& g : m_gizmos) { g.render_for_picking(); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index d088b4ba3..fb1b148b4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -1,6 +1,7 @@ // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoScale.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" #include @@ -75,7 +76,7 @@ bool GLGizmoScale3D::on_init() std::string GLGizmoScale3D::on_get_name() const { - return (_(L("Scale")) + " [S]").ToUTF8().data(); + return (_L("Scale") + " [S]").ToUTF8().data(); } bool GLGizmoScale3D::on_is_activable() const @@ -131,12 +132,10 @@ void GLGizmoScale3D::on_render() const m_offsets_transform = Transform3d::Identity(); Vec3d angles = Vec3d::Zero(); - if (single_instance) - { + if (single_instance) { // calculate bounding box in instance local reference system const Selection::IndicesList& idxs = selection.get_volume_idxs(); - for (unsigned int idx : idxs) - { + for (unsigned int idx : idxs) { const GLVolume* vol = selection.get_volume(idx); m_box.merge(vol->bounding_box().transformed(vol->get_volume_transformation().get_matrix())); } @@ -150,8 +149,7 @@ void GLGizmoScale3D::on_render() const offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); m_offsets_transform = offsets_transform; } - else if (single_volume) - { + else if (single_volume) { const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); m_box = v->bounding_box(); m_transform = v->world_matrix(); @@ -172,20 +170,20 @@ void GLGizmoScale3D::on_render() const // x axis m_grabbers[0].center = m_transform * Vec3d(m_box.min(0), center(1), center(2)) - offset_x; - m_grabbers[1].center = m_transform * Vec3d(m_box.max(0), center(1), center(2)) + offset_x; m_grabbers[0].color = (ctrl_down && (m_hover_id == 1)) ? CONSTRAINED_COLOR : AXES_COLOR[0]; + m_grabbers[1].center = m_transform * Vec3d(m_box.max(0), center(1), center(2)) + offset_x; m_grabbers[1].color = (ctrl_down && (m_hover_id == 0)) ? CONSTRAINED_COLOR : AXES_COLOR[0]; // y axis m_grabbers[2].center = m_transform * Vec3d(center(0), m_box.min(1), center(2)) - offset_y; - m_grabbers[3].center = m_transform * Vec3d(center(0), m_box.max(1), center(2)) + offset_y; m_grabbers[2].color = (ctrl_down && (m_hover_id == 3)) ? CONSTRAINED_COLOR : AXES_COLOR[1]; + m_grabbers[3].center = m_transform * Vec3d(center(0), m_box.max(1), center(2)) + offset_y; m_grabbers[3].color = (ctrl_down && (m_hover_id == 2)) ? CONSTRAINED_COLOR : AXES_COLOR[1]; // z axis m_grabbers[4].center = m_transform * Vec3d(center(0), center(1), m_box.min(2)) - offset_z; - m_grabbers[5].center = m_transform * Vec3d(center(0), center(1), m_box.max(2)) + offset_z; m_grabbers[4].color = (ctrl_down && (m_hover_id == 5)) ? CONSTRAINED_COLOR : AXES_COLOR[2]; + m_grabbers[5].center = m_transform * Vec3d(center(0), center(1), m_box.max(2)) + offset_z; m_grabbers[5].color = (ctrl_down && (m_hover_id == 4)) ? CONSTRAINED_COLOR : AXES_COLOR[2]; // uniform @@ -234,25 +232,46 @@ void GLGizmoScale3D::on_render() const // draw connection glsafe(::glColor4fv(m_grabbers[0].color.data())); render_grabbers_connection(0, 1); - // draw grabbers - m_grabbers[0].render(true, grabber_mean_size); - m_grabbers[1].render(true, grabber_mean_size); + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1); + // draw grabbers + m_grabbers[0].render(true, grabber_mean_size); + m_grabbers[1].render(true, grabber_mean_size); + shader->stop_using(); + } } else if (m_hover_id == 2 || m_hover_id == 3) { // draw connection glsafe(::glColor4fv(m_grabbers[2].color.data())); render_grabbers_connection(2, 3); - // draw grabbers - m_grabbers[2].render(true, grabber_mean_size); - m_grabbers[3].render(true, grabber_mean_size); + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1); + // draw grabbers + m_grabbers[2].render(true, grabber_mean_size); + m_grabbers[3].render(true, grabber_mean_size); + shader->stop_using(); + } } else if (m_hover_id == 4 || m_hover_id == 5) { // draw connection glsafe(::glColor4fv(m_grabbers[4].color.data())); render_grabbers_connection(4, 5); - // draw grabbers - m_grabbers[4].render(true, grabber_mean_size); - m_grabbers[5].render(true, grabber_mean_size); + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1); + // draw grabbers + m_grabbers[4].render(true, grabber_mean_size); + m_grabbers[5].render(true, grabber_mean_size); + shader->stop_using(); + } } else if (m_hover_id >= 6) { // draw connection @@ -261,9 +280,16 @@ void GLGizmoScale3D::on_render() const render_grabbers_connection(7, 8); render_grabbers_connection(8, 9); render_grabbers_connection(9, 6); - // draw grabbers - for (int i = 6; i < 10; ++i) { - m_grabbers[i].render(true, grabber_mean_size); + + GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light"); + if (shader != nullptr) { + shader->start_using(); + shader->set_uniform("emission_factor", 0.1); + // draw grabbers + for (int i = 6; i < 10; ++i) { + m_grabbers[i].render(true, grabber_mean_size); + } + shader->stop_using(); } } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index f0818a2d7..10da6b85c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -166,7 +166,12 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) } } if (shader && ! picking) { +#if ENABLE_SEQUENTIAL_LIMITS + const_cast(&m_cone)->set_color(-1, render_color); + const_cast(&m_sphere)->set_color(-1, render_color); +#else shader->set_uniform("uniform_color", render_color); +#endif // ENABLE_SEQUENTIAL_LIMITS shader->set_uniform("emission_factor", 0.5); } else // picking @@ -225,7 +230,11 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) render_color[2] = 0.7f; render_color[3] = 0.7f; if (shader) { +#if ENABLE_SEQUENTIAL_LIMITS + const_cast(&m_cylinder)->set_color(-1, render_color); +#else shader->set_uniform("uniform_color", render_color); +#endif // ENABLE_SEQUENTIAL_LIMITS shader->set_uniform("emission_factor", 0.5); } for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3aff82fb4..63aca0df2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2730,6 +2730,10 @@ void Plater::priv::reset() reset_gcode_toolpaths(); gcode_result.reset(); +#if ENABLE_SEQUENTIAL_LIMITS + view3D->get_canvas3d()->set_sequential_print_clearance(Polygons(), false); +#endif // ENABLE_SEQUENTIAL_LIMITS + // Stop and reset the Print content. this->background_process.reset(); model.clear_objects(); @@ -2894,12 +2898,12 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool // If the update_background_process() was not called by the timer, kill the timer, // so the update_restart_background_process() will not be called again in vain. - this->background_process_timer.Stop(); + background_process_timer.Stop(); // Update the "out of print bed" state of ModelInstances. - this->update_print_volume_state(); + update_print_volume_state(); // Apply new config to the possibly running background task. - bool was_running = this->background_process.running(); - Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config()); + bool was_running = background_process.running(); + Print::ApplyStatus invalidated = background_process.apply(q->model(), wxGetApp().preset_bundle->full_config()); // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. if (view3D->is_layers_editing_enabled()) @@ -2908,40 +2912,56 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool if (invalidated == Print::APPLY_STATUS_INVALIDATED) { // Some previously calculated data on the Print was invalidated. // Hide the slicing results, as the current slicing status is no more valid. - this->sidebar->show_sliced_info_sizer(false); + sidebar->show_sliced_info_sizer(false); // Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. // Otherwise they will be just refreshed. - if (this->preview != nullptr) { + if (preview != nullptr) { // If the preview is not visible, the following line just invalidates the preview, // but the G-code paths or SLA preview are calculated first once the preview is made visible. reset_gcode_toolpaths(); - this->preview->reload_print(); + preview->reload_print(); } // In FDM mode, we need to reload the 3D scene because of the wipe tower preview box. // In SLA mode, we need to reload the 3D scene every time to show the support structures. - if (this->printer_technology == ptSLA || (this->printer_technology == ptFFF && this->config->opt_bool("wipe_tower"))) + if (printer_technology == ptSLA || (printer_technology == ptFFF && config->opt_bool("wipe_tower"))) return_state |= UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE; } - if ((invalidated != Print::APPLY_STATUS_UNCHANGED || force_validation) && ! this->background_process.empty()) { + if ((invalidated != Print::APPLY_STATUS_UNCHANGED || force_validation) && ! background_process.empty()) { // The delayed error message is no more valid. - this->delayed_error_message.clear(); + delayed_error_message.clear(); // The state of the Print changed, and it is non-zero. Let's validate it and give the user feedback on errors. std::string warning; - std::string err = this->background_process.validate(&warning); + std::string err = background_process.validate(&warning); if (err.empty()) { notification_manager->set_all_slicing_errors_gray(true); - if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->background_processing_enabled()) + if (invalidated != Print::APPLY_STATUS_UNCHANGED && background_processing_enabled()) return_state |= UPDATE_BACKGROUND_PROCESS_RESTART; // Pass a warning from validation and either show a notification, // or hide the old one. process_validation_warning(warning); +#if ENABLE_SEQUENTIAL_LIMITS + if (printer_technology == ptFFF) { + view3D->get_canvas3d()->set_sequential_print_clearance(Polygons(), false); + view3D->get_canvas3d()->set_as_dirty(); + view3D->get_canvas3d()->request_extra_frame(); + } +#endif // ENABLE_SEQUENTIAL_LIMITS } else { // The print is not valid. // Show error as notification. notification_manager->push_slicing_error_notification(err); return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; +#if ENABLE_SEQUENTIAL_LIMITS + if (printer_technology == ptFFF) { + const Print* print = background_process.fff_print(); + Polygons polygons; + if (print->config().complete_objects) + Print::sequential_print_horizontal_clearance_valid(*print, &polygons); + view3D->get_canvas3d()->set_sequential_print_clearance(polygons, true); + } +#endif // ENABLE_SEQUENTIAL_LIMITS } } else if (! this->delayed_error_message.empty()) { @@ -3077,8 +3097,7 @@ void Plater::priv::update_fff_scene() if (this->preview != nullptr) this->preview->reload_print(); // In case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: - view3D->reload_scene(true); - + view3D->reload_scene(true); } void Plater::priv::update_sla_scene() diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 479b0fa77..ca1530683 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1818,8 +1818,31 @@ void Selection::render_bounding_box(const BoundingBoxf3& box, float* color) cons glsafe(::glEnd()); } +#if ENABLE_SEQUENTIAL_LIMITS +static std::array get_color(Axis axis) +{ + return { AXES_COLOR[axis][0], AXES_COLOR[axis][1], AXES_COLOR[axis][2], AXES_COLOR[axis][3] }; +}; +#endif // ENABLE_SEQUENTIAL_LIMITS + void Selection::render_sidebar_position_hints(const std::string& sidebar_field) const { +#if ENABLE_SEQUENTIAL_LIMITS + if (boost::ends_with(sidebar_field, "x")) { + glsafe(::glRotated(-90.0, 0.0, 0.0, 1.0)); + const_cast(&m_arrow)->set_color(-1, get_color(X)); + m_arrow.render(); + } + else if (boost::ends_with(sidebar_field, "y")) { + const_cast(&m_arrow)->set_color(-1, get_color(Y)); + m_arrow.render(); + } + else if (boost::ends_with(sidebar_field, "z")) { + glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); + const_cast(&m_arrow)->set_color(-1, get_color(Z)); + m_arrow.render(); + } +#else auto set_color = [](Axis axis) { GLShaderProgram* shader = wxGetApp().get_current_shader(); if (shader != nullptr) { @@ -1840,10 +1863,33 @@ void Selection::render_sidebar_position_hints(const std::string& sidebar_field) glsafe(::glRotated(90.0, 1.0, 0.0, 0.0)); m_arrow.render(); } +#endif // ENABLE_SEQUENTIAL_LIMITS } void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) const { +#if ENABLE_SEQUENTIAL_LIMITS + auto render_sidebar_rotation_hint = [this]() { + m_curved_arrow.render(); + glsafe(::glRotated(180.0, 0.0, 0.0, 1.0)); + m_curved_arrow.render(); + }; + + if (boost::ends_with(sidebar_field, "x")) { + glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); + const_cast(&m_curved_arrow)->set_color(-1, get_color(X)); + render_sidebar_rotation_hint(); + } + else if (boost::ends_with(sidebar_field, "y")) { + glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); + const_cast(&m_curved_arrow)->set_color(-1, get_color(Y)); + render_sidebar_rotation_hint(); + } + else if (boost::ends_with(sidebar_field, "z")) { + const_cast(&m_curved_arrow)->set_color(-1, get_color(Z)); + render_sidebar_rotation_hint(); + } +#else auto set_color = [](Axis axis) { GLShaderProgram* shader = wxGetApp().get_current_shader(); if (shader != nullptr) { @@ -1870,6 +1916,7 @@ void Selection::render_sidebar_rotation_hints(const std::string& sidebar_field) set_color(Z); render_sidebar_rotation_hint(); } +#endif // ENABLE_SEQUENTIAL_LIMITS } void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) const @@ -1877,11 +1924,12 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con bool uniform_scale = requires_uniform_scale() || wxGetApp().obj_manipul()->get_uniform_scaling(); auto render_sidebar_scale_hint = [this, uniform_scale](Axis axis) { +#if ENABLE_SEQUENTIAL_LIMITS + const_cast(&m_arrow)->set_color(-1, uniform_scale ? UNIFORM_SCALE_COLOR : get_color(axis)); +#endif // ENABLE_SEQUENTIAL_LIMITS GLShaderProgram* shader = wxGetApp().get_current_shader(); - if (shader != nullptr) { - shader->set_uniform("uniform_color", uniform_scale ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]); + if (shader != nullptr) shader->set_uniform("emission_factor", 0.0); - } glsafe(::glTranslated(0.0, 5.0, 0.0)); m_arrow.render();