From 75df722fee97bcb6e0d7e0b90f93cfb0819d3ee0 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 19 Feb 2019 15:15:27 +0100 Subject: [PATCH] Bed and Axes classes moved from GLCanva3d to Plater to have a unique instance of them shared by all views --- src/libslic3r/Technologies.hpp | 9 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/3DBed.cpp | 517 +++++++++++++++++++++++++++++++++ src/slic3r/GUI/3DBed.hpp | 105 +++++++ src/slic3r/GUI/GLCanvas3D.cpp | 68 +++++ src/slic3r/GUI/GLCanvas3D.hpp | 24 ++ src/slic3r/GUI/GUI_Preview.cpp | 34 ++- src/slic3r/GUI/GUI_Preview.hpp | 17 ++ src/slic3r/GUI/Plater.cpp | 41 +++ src/slic3r/GUI/Plater.hpp | 5 + 10 files changed, 821 insertions(+), 1 deletion(-) create mode 100644 src/slic3r/GUI/3DBed.cpp create mode 100644 src/slic3r/GUI/3DBed.hpp diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index fe7606041..b26534c54 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -54,4 +54,13 @@ // Toolbar items hidden/shown in dependence of the user mode #define ENABLE_MODE_AWARE_TOOLBAR_ITEMS (1 && ENABLE_1_42_0_ALPHA5) + +//==================== +// 1.42.0.alpha7 techs +//==================== +#define ENABLE_1_42_0_ALPHA7 1 + +// Moves bed from GLCanva3d to Plater to have a unique instance of it +#define ENABLE_UNIQUE_BED (1 && ENABLE_1_42_0_ALPHA7) + #endif // _technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 9d65a479f..157fc9011 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -74,6 +74,8 @@ set(SLIC3R_GUI_SOURCES GUI/BedShapeDialog.hpp GUI/2DBed.cpp GUI/2DBed.hpp + GUI/3DBed.cpp + GUI/3DBed.hpp GUI/wxExtensions.cpp GUI/wxExtensions.hpp GUI/WipeTowerDialog.cpp diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp new file mode 100644 index 000000000..7ad89776b --- /dev/null +++ b/src/slic3r/GUI/3DBed.cpp @@ -0,0 +1,517 @@ +#include "libslic3r/libslic3r.h" + +#include "3DBed.hpp" + +#include "libslic3r/Polygon.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/BoundingBox.hpp" + +#include "GUI_App.hpp" +#include "PresetBundle.hpp" + +#include + +#include + +static const float GROUND_Z = -0.02f; + +namespace Slic3r { +namespace GUI { + +#if ENABLE_UNIQUE_BED +bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords) +{ + m_vertices.clear(); + m_tex_coords.clear(); + + unsigned int v_size = 9 * (unsigned int)triangles.size(); + unsigned int t_size = 6 * (unsigned int)triangles.size(); + if (v_size == 0) + return false; + + m_vertices = std::vector(v_size, 0.0f); + if (generate_tex_coords) + m_tex_coords = std::vector(t_size, 0.0f); + + float min_x = unscale(triangles[0].points[0](0)); + float min_y = unscale(triangles[0].points[0](1)); + float max_x = min_x; + float max_y = min_y; + + unsigned int v_coord = 0; + unsigned int t_coord = 0; + for (const Polygon& t : triangles) + { + for (unsigned int v = 0; v < 3; ++v) + { + const Point& p = t.points[v]; + float x = unscale(p(0)); + float y = unscale(p(1)); + + m_vertices[v_coord++] = x; + m_vertices[v_coord++] = y; + m_vertices[v_coord++] = z; + + if (generate_tex_coords) + { + m_tex_coords[t_coord++] = x; + m_tex_coords[t_coord++] = y; + + min_x = std::min(min_x, x); + max_x = std::max(max_x, x); + min_y = std::min(min_y, y); + max_y = std::max(max_y, y); + } + } + } + + if (generate_tex_coords) + { + float size_x = max_x - min_x; + float size_y = max_y - min_y; + + if ((size_x != 0.0f) && (size_y != 0.0f)) + { + float inv_size_x = 1.0f / size_x; + float inv_size_y = -1.0f / size_y; + for (unsigned int i = 0; i < m_tex_coords.size(); i += 2) + { + m_tex_coords[i] = (m_tex_coords[i] - min_x) * inv_size_x; + m_tex_coords[i + 1] = (m_tex_coords[i + 1] - min_y) * inv_size_y; + } + } + } + + return true; +} + +bool GeometryBuffer::set_from_lines(const Lines& lines, float z) +{ + m_vertices.clear(); + m_tex_coords.clear(); + + unsigned int size = 6 * (unsigned int)lines.size(); + if (size == 0) + return false; + + m_vertices = std::vector(size, 0.0f); + + unsigned int coord = 0; + for (const Line& l : lines) + { + m_vertices[coord++] = unscale(l.a(0)); + m_vertices[coord++] = unscale(l.a(1)); + m_vertices[coord++] = z; + m_vertices[coord++] = unscale(l.b(0)); + m_vertices[coord++] = unscale(l.b(1)); + m_vertices[coord++] = z; + } + + return true; +} + +const double Bed3D::Axes::Radius = 0.5; +const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius; +const double Bed3D::Axes::ArrowLength = 5.0; + +Bed3D::Axes::Axes() +: origin(Vec3d::Zero()) +, length(Vec3d::Zero()) +{ + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); +} + +Bed3D::Axes::~Axes() +{ + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} + +void Bed3D::Axes::render() const +{ + if (m_quadric == nullptr) + return; + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glEnable(GL_LIGHTING)); + + // x axis + glsafe(::glColor3f(1.0f, 0.0f, 0.0f)); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(origin(0), origin(1), origin(2))); + glsafe(::glRotated(90.0, 0.0, 1.0, 0.0)); + render_axis(length(0)); + glsafe(::glPopMatrix()); + + // y axis + glsafe(::glColor3f(0.0f, 1.0f, 0.0f)); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(origin(0), origin(1), origin(2))); + glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0)); + render_axis(length(1)); + glsafe(::glPopMatrix()); + + // z axis + glsafe(::glColor3f(0.0f, 0.0f, 1.0f)); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(origin(0), origin(1), origin(2))); + render_axis(length(2)); + glsafe(::glPopMatrix()); + + glsafe(::glDisable(GL_LIGHTING)); +} + +void Bed3D::Axes::render_axis(double length) const +{ + ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); + ::gluCylinder(m_quadric, Radius, Radius, length, 32, 1); + ::gluQuadricOrientation(m_quadric, GLU_INSIDE); + ::gluDisk(m_quadric, 0.0, Radius, 32, 1); + glsafe(::glTranslated(0.0, 0.0, length)); + ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); + ::gluCylinder(m_quadric, ArrowBaseRadius, 0.0, ArrowLength, 32, 1); + ::gluQuadricOrientation(m_quadric, GLU_INSIDE); + ::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1); +} + +Bed3D::Bed3D() +: m_type(Custom) +, m_scale_factor(1.0f) +{ +} + +bool Bed3D::set_shape(const Pointfs& shape) +{ + EType new_type = detect_type(shape); + if (m_shape == shape && m_type == new_type) + // No change, no need to update the UI. + return false; + + m_shape = shape; + m_type = new_type; + + calc_bounding_box(); + + ExPolygon poly; + for (const Vec2d& p : m_shape) + { + poly.contour.append(Point(scale_(p(0)), scale_(p(1)))); + } + + calc_triangles(poly); + + const BoundingBox& bed_bbox = poly.contour.bounding_box(); + calc_gridlines(poly, bed_bbox); + + m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour; + + // Set the origin and size for painting of the coordinate system axes. + m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); + m_axes.length = 0.1 * get_bounding_box().max_size() * Vec3d::Ones(); + + // Let the calee to update the UI. + return true; +} + +bool Bed3D::contains(const Point& point) const +{ + return m_polygon.contains(point); +} + +Point Bed3D::point_projection(const Point& point) const +{ + return m_polygon.point_projection(point); +} + +void Bed3D::render(float theta, bool useVBOs, float scale_factor) const +{ + m_scale_factor = scale_factor; + + if (m_shape.empty()) + return; + + switch (m_type) + { + case MK2: + { + render_prusa("mk2", theta, useVBOs); + break; + } + case MK3: + { + render_prusa("mk3", theta, useVBOs); + break; + } + case SL1: + { + render_prusa("sl1", theta, useVBOs); + break; + } + default: + case Custom: + { + render_custom(); + break; + } + } +} + +void Bed3D::render_axes() const +{ + if (!m_shape.empty()) + m_axes.render(); +} + +void Bed3D::calc_bounding_box() +{ + m_bounding_box = BoundingBoxf3(); + for (const Vec2d& p : m_shape) + { + m_bounding_box.merge(Vec3d(p(0), p(1), 0.0)); + } +} + +void Bed3D::calc_triangles(const ExPolygon& poly) +{ + Polygons triangles; + poly.triangulate(&triangles); + + if (!m_triangles.set_from_triangles(triangles, GROUND_Z, m_type != Custom)) + printf("Unable to create bed triangles\n"); +} + +void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) +{ + Polylines axes_lines; + for (coord_t x = bed_bbox.min(0); x <= bed_bbox.max(0); x += scale_(10.0)) + { + Polyline line; + line.append(Point(x, bed_bbox.min(1))); + line.append(Point(x, bed_bbox.max(1))); + axes_lines.push_back(line); + } + for (coord_t y = bed_bbox.min(1); y <= bed_bbox.max(1); y += scale_(10.0)) + { + Polyline line; + line.append(Point(bed_bbox.min(0), y)); + line.append(Point(bed_bbox.max(0), y)); + axes_lines.push_back(line); + } + + // clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped + Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, (float)SCALED_EPSILON))); + + // append bed contours + Lines contour_lines = to_lines(poly); + std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines)); + + if (!m_gridlines.set_from_lines(gridlines, GROUND_Z)) + printf("Unable to create bed grid lines\n"); +} + +Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const +{ + EType type = Custom; + + auto bundle = wxGetApp().preset_bundle; + if (bundle != nullptr) + { + const Preset* curr = &bundle->printers.get_selected_preset(); + while (curr != nullptr) + { + if (curr->config.has("bed_shape")) + { + if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research") && (shape == dynamic_cast(curr->config.option("bed_shape"))->values)) + { + if (boost::contains(curr->name, "SL1")) + { + type = SL1; + break; + } + else if (boost::contains(curr->name, "MK3") || boost::contains(curr->name, "MK2.5")) + { + type = MK3; + break; + } + else if (boost::contains(curr->name, "MK2")) + { + type = MK2; + break; + } + } + } + + curr = bundle->printers.get_preset_parent(*curr); + } + } + + return type; +} + +void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) const +{ + std::string tex_path = resources_dir() + "/icons/bed/" + key; + + // use higher resolution images if graphic card allows + GLint max_tex_size; + ::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size); + + // temporary set to lowest resolution + max_tex_size = 2048; + + if (max_tex_size >= 8192) + tex_path += "_8192"; + else if (max_tex_size >= 4096) + tex_path += "_4096"; + + std::string model_path = resources_dir() + "/models/" + key; + +#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + // use anisotropic filter if graphic card allows + GLfloat max_anisotropy = 0.0f; + if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) + ::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy); +#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + + std::string filename = tex_path + "_top.png"; + if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) + { + if (!m_top_texture.load_from_file(filename, true)) + { + render_custom(); + return; + } +#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + if (max_anisotropy > 0.0f) + { + glsafe(::glBindTexture(GL_TEXTURE_2D, m_top_texture.get_id())); + glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy)); + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + } +#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + } + + filename = tex_path + "_bottom.png"; + if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename)) + { + if (!m_bottom_texture.load_from_file(filename, true)) + { + render_custom(); + return; + } +#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + if (max_anisotropy > 0.0f) + { + glsafe(::glBindTexture(GL_TEXTURE_2D, m_bottom_texture.get_id())); + glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy)); + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + } +#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES + } + + if (theta <= 90.0f) + { + filename = model_path + "_bed.stl"; + if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs)) { + Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2)); + if (key == "mk2") + // hardcoded value to match the stl model + offset += Vec3d(0.0, 7.5, -0.03); + else if (key == "mk3") + // hardcoded value to match the stl model + offset += Vec3d(0.0, 5.5, 2.43); + else if (key == "sl1") + // hardcoded value to match the stl model + offset += Vec3d(0.0, 0.0, -0.03); + + m_model.center_around(offset); + } + + if (!m_model.get_filename().empty()) + { + glsafe(::glEnable(GL_LIGHTING)); + m_model.render(); + glsafe(::glDisable(GL_LIGHTING)); + } + } + + unsigned int triangles_vcount = m_triangles.get_vertices_count(); + if (triangles_vcount > 0) + { + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glDepthMask(GL_FALSE)); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + glsafe(::glEnable(GL_TEXTURE_2D)); + glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + + if (theta > 90.0f) + glsafe(::glFrontFace(GL_CW)); + + glsafe(::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id())); + glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices())); + glsafe(::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords())); + glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); + + if (theta > 90.0f) + glsafe(::glFrontFace(GL_CCW)); + + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); + glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY)); + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glDisable(GL_TEXTURE_2D)); + + glsafe(::glDisable(GL_BLEND)); + glsafe(::glDepthMask(GL_TRUE)); + } +} + +void Bed3D::render_custom() const +{ + m_top_texture.reset(); + m_bottom_texture.reset(); + + unsigned int triangles_vcount = m_triangles.get_vertices_count(); + if (triangles_vcount > 0) + { + glsafe(::glEnable(GL_LIGHTING)); + glsafe(::glDisable(GL_DEPTH_TEST)); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f)); + glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); + glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices())); + glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); + + // draw grid + unsigned int gridlines_vcount = m_gridlines.get_vertices_count(); + + // we need depth test for grid, otherwise it would disappear when looking the object from below + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glLineWidth(3.0f * m_scale_factor)); + glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f)); + glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices())); + glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount)); + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glDisable(GL_BLEND)); + glsafe(::glDisable(GL_LIGHTING)); + } +} +#endif // ENABLE_UNIQUE_BED + +} // GUI +} // Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp new file mode 100644 index 000000000..51d787c03 --- /dev/null +++ b/src/slic3r/GUI/3DBed.hpp @@ -0,0 +1,105 @@ +#ifndef slic3r_3DBed_hpp_ +#define slic3r_3DBed_hpp_ + +#include "GLTexture.hpp" +#include "3DScene.hpp" + +class GLUquadric; +typedef class GLUquadric GLUquadricObj; + +namespace Slic3r { +namespace GUI { + +#if ENABLE_UNIQUE_BED +class GeometryBuffer +{ + std::vector m_vertices; + std::vector m_tex_coords; + +public: + bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords); + bool set_from_lines(const Lines& lines, float z); + + const float* get_vertices() const { return m_vertices.data(); } + const float* get_tex_coords() const { return m_tex_coords.data(); } + + unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size() / 3; } +}; + +class Bed3D +{ + struct Axes + { + static const double Radius; + static const double ArrowBaseRadius; + static const double ArrowLength; + Vec3d origin; + Vec3d length; + GLUquadricObj* m_quadric; + + Axes(); + ~Axes(); + + void render() const; + + private: + void render_axis(double length) const; + }; + +public: + enum EType : unsigned char + { + MK2, + MK3, + SL1, + Custom, + Num_Types + }; + +private: + EType m_type; + Pointfs m_shape; + BoundingBoxf3 m_bounding_box; + Polygon m_polygon; + GeometryBuffer m_triangles; + GeometryBuffer m_gridlines; + mutable GLTexture m_top_texture; + mutable GLTexture m_bottom_texture; + mutable GLBed m_model; + Axes m_axes; + + mutable float m_scale_factor; + +public: + Bed3D(); + + EType get_type() const { return m_type; } + + bool is_prusa() const { return (m_type == MK2) || (m_type == MK3) || (m_type == SL1); } + bool is_custom() const { return m_type == Custom; } + + const Pointfs& get_shape() const { return m_shape; } + // Return true if the bed shape changed, so the calee will update the UI. + bool set_shape(const Pointfs& shape); + + const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } + bool contains(const Point& point) const; + Point point_projection(const Point& point) const; + + void render(float theta, bool useVBOs, float scale_factor) const; + void render_axes() const; + +private: + void calc_bounding_box(); + void calc_triangles(const ExPolygon& poly); + void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); + EType detect_type(const Pointfs& shape) const; + void render_prusa(const std::string &key, float theta, bool useVBOs) const; + void render_custom() const; +}; +#endif // ENABLE_UNIQUE_BED + +} // GUI +} // Slic3r + +#endif // slic3r_3DBed_hpp_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 8024e6a46..e1c5138b4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -84,6 +84,7 @@ static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f namespace Slic3r { namespace GUI { +#if !ENABLE_UNIQUE_BED bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords) { m_vertices.clear(); @@ -189,6 +190,7 @@ unsigned int GeometryBuffer::get_vertices_count() const { return (unsigned int)m_vertices.size() / 3; } +#endif // !ENABLE_UNIQUE_BED Size::Size() : m_width(0) @@ -344,6 +346,7 @@ void GLCanvas3D::Camera::set_scene_box(const BoundingBoxf3& box, GLCanvas3D& can } } +#if !ENABLE_UNIQUE_BED GLCanvas3D::Bed::Bed() : m_type(Custom) , m_scale_factor(1.0f) @@ -751,6 +754,7 @@ void GLCanvas3D::Axes::render_axis(double length) const ::gluQuadricOrientation(m_quadric, GLU_INSIDE); ::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1); } +#endif // !ENABLE_UNIQUE_BED GLCanvas3D::Shader::Shader() : m_shader(nullptr) @@ -3973,6 +3977,9 @@ wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); +#if ENABLE_UNIQUE_BED +wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent); +#endif // ENABLE_UNIQUE_BED GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) : m_canvas(canvas) @@ -3981,6 +3988,9 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_retina_helper(nullptr) #endif , m_in_render(false) +#if ENABLE_UNIQUE_BED + , m_bed(nullptr) +#endif // ENABLE_UNIQUE_BED , m_toolbar(GLToolbar::Normal) , m_view_toolbar(nullptr) , m_use_clipping_planes(false) @@ -4180,6 +4190,15 @@ void GLCanvas3D::set_model(Model* model) m_selection.set_model(m_model); } +#if ENABLE_UNIQUE_BED +void GLCanvas3D::bed_shape_changed() +{ + m_camera.set_scene_box(scene_bounding_box(), *this); + m_requires_zoom_to_bed = true; + + m_dirty = true; +} +#else void GLCanvas3D::set_bed_shape(const Pointfs& shape) { bool new_shape = m_bed.set_shape(shape); @@ -4200,6 +4219,7 @@ void GLCanvas3D::set_bed_axes_length(double length) { m_axes.length = length * Vec3d::Ones(); } +#endif // ENABLE_UNIQUE_BED void GLCanvas3D::set_color_by(const std::string& value) { @@ -4225,7 +4245,12 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); +#if ENABLE_UNIQUE_BED + if (m_bed != nullptr) + bb.merge(m_bed->get_bounding_box()); +#else bb.merge(m_bed.get_bounding_box()); +#endif // ENABLE_UNIQUE_BED if (m_config != nullptr) { double h = m_config->opt_float("max_print_height"); @@ -4318,7 +4343,12 @@ bool GLCanvas3D::is_toolbar_item_pressed(const std::string& name) const void GLCanvas3D::zoom_to_bed() { +#if ENABLE_UNIQUE_BED + if (m_bed != nullptr) + _zoom_to_bounding_box(m_bed->get_bounding_box()); +#else _zoom_to_bounding_box(m_bed.get_bounding_box()); +#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::zoom_to_volumes() @@ -4431,12 +4461,20 @@ void GLCanvas3D::render() if (!_set_current() || !_3DScene::init(m_canvas)) return; +#if ENABLE_UNIQUE_BED + if ((m_bed != nullptr) && m_bed->get_shape().empty()) + { + // this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE + post_event(SimpleEvent(EVT_GLCANVAS_UPDATE_BED_SHAPE)); + } +#else if (m_bed.get_shape().empty()) { // this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE if (m_config != nullptr) set_bed_shape(m_config->opt("bed_shape")->values); } +#endif // ENABLE_UNIQUE_BED if (m_requires_zoom_to_bed) { @@ -4458,7 +4496,11 @@ void GLCanvas3D::render() // absolute value of the rotation theta = 360.f - theta; +#if ENABLE_UNIQUE_BED + bool is_custom_bed = (m_bed == nullptr) || m_bed->is_custom(); +#else bool is_custom_bed = m_bed.is_custom(); +#endif // ENABLE_UNIQUE_BED #if ENABLE_IMGUI wxGetApp().imgui()->new_frame(); @@ -6188,9 +6230,16 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) BoundingBoxf3 GLCanvas3D::_max_bounding_box() const { +#if ENABLE_UNIQUE_BED + BoundingBoxf3 bb = volumes_bounding_box(); + if (m_bed != nullptr) + bb.merge(m_bed->get_bounding_box()); + return bb; +#else BoundingBoxf3 bb = m_bed.get_bounding_box(); bb.merge(volumes_bounding_box()); return bb; +#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) @@ -6394,12 +6443,22 @@ void GLCanvas3D::_render_bed(float theta) const scale_factor = m_retina_helper->get_scale_factor(); #endif +#if ENABLE_UNIQUE_BED + if (m_bed != nullptr) + m_bed->render(theta, m_use_VBOs, scale_factor); +#else m_bed.render(theta, m_use_VBOs, scale_factor); +#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::_render_axes() const { +#if ENABLE_UNIQUE_BED + if (m_bed != nullptr) + m_bed->render_axes(); +#else m_axes.render(); +#endif // ENABLE_UNIQUE_BED } void GLCanvas3D::_render_objects() const @@ -6417,12 +6476,21 @@ void GLCanvas3D::_render_objects() const // Update the layer editing selection to the first object selected, update the current object maximum Z. const_cast(m_layers_editing).select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1); +#if ENABLE_UNIQUE_BED + if ((m_config != nullptr) && (m_bed != nullptr)) + { + const BoundingBoxf3& bed_bb = m_bed->get_bounding_box(); + m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); + m_volumes.check_outside_state(m_config, nullptr); + } +#else if (m_config != nullptr) { const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(); m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); m_volumes.check_outside_state(m_config, nullptr); } +#endif // ENABLE_UNIQUE_BED } if (m_use_clipping_planes) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 44e8ee0b2..2136adca7 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -8,6 +8,9 @@ #include "3DScene.hpp" #include "GLToolbar.hpp" #include "Event.hpp" +#if ENABLE_UNIQUE_BED +#include "3DBed.hpp" +#endif // ENABLE_UNIQUE_BED #include @@ -25,8 +28,10 @@ class wxGLCanvas; // Support for Retina OpenGL on Mac OS #define ENABLE_RETINA_GL __APPLE__ +#if !ENABLE_UNIQUE_BED class GLUquadric; typedef class GLUquadric GLUquadricObj; +#endif // !ENABLE_UNIQUE_BED namespace Slic3r { @@ -45,6 +50,7 @@ class GLGizmoBase; class RetinaHelper; #endif +#if !ENABLE_UNIQUE_BED class GeometryBuffer { std::vector m_vertices; @@ -59,6 +65,7 @@ public: unsigned int get_vertices_count() const; }; +#endif // !ENABLE_UNIQUE_BED class Size { @@ -131,6 +138,9 @@ wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); +#if ENABLE_UNIQUE_BED +wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent); +#endif // ENABLE_UNIQUE_BED class GLCanvas3D { @@ -196,6 +206,7 @@ class GLCanvas3D void set_scene_box(const BoundingBoxf3& box, GLCanvas3D& canvas); }; +#if !ENABLE_UNIQUE_BED class Bed { public: @@ -265,6 +276,7 @@ class GLCanvas3D private: void render_axis(double length) const; }; +#endif // !ENABLE_UNIQUE_BED class Shader { @@ -865,8 +877,12 @@ private: WarningTexture m_warning_texture; wxTimer m_timer; Camera m_camera; +#if ENABLE_UNIQUE_BED + Bed3D* m_bed; +#else Bed m_bed; Axes m_axes; +#endif // ENABLE_UNIQUE_BED LayersEditing m_layers_editing; Shader m_shader; Mouse m_mouse; @@ -920,6 +936,10 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas; } const wxGLCanvas* get_wxglcanvas() const { return m_canvas; } +#if ENABLE_UNIQUE_BED + void set_bed(Bed3D* bed) { m_bed = bed; } +#endif // ENABLE_UNIQUE_BED + void set_view_toolbar(GLToolbar* toolbar) { m_view_toolbar = toolbar; } bool init(bool useVBOs, bool use_legacy_opengl); @@ -938,12 +958,16 @@ public: const Selection& get_selection() const { return m_selection; } Selection& get_selection() { return m_selection; } +#if ENABLE_UNIQUE_BED + void bed_shape_changed(); +#else // Set the bed shape to a single closed 2D polygon(array of two element arrays), // triangulate the bed and store the triangles into m_bed.m_triangles, // fills the m_bed.m_grid_lines and sets m_bed.m_origin. // Sets m_bed.m_polygon to limit the object placement. void set_bed_shape(const Pointfs& shape); void set_bed_axes_length(double length); +#endif // ENABLE_UNIQUE_BED void set_clipping_plane(unsigned int id, const ClippingPlane& plane) { diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 6f758f375..0c3dc7025 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -89,6 +89,14 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba return true; } +#if ENABLE_UNIQUE_BED +void View3D::set_bed(Bed3D* bed) +{ + if (m_canvas != nullptr) + m_canvas->set_bed(bed); +} +#endif // ENABLE_UNIQUE_BED + void View3D::set_view_toolbar(GLToolbar* toolbar) { if (m_canvas != nullptr) @@ -101,11 +109,19 @@ void View3D::set_as_dirty() m_canvas->set_as_dirty(); } +#if ENABLE_UNIQUE_BED +void View3D::bed_shape_changed() +{ + if (m_canvas != nullptr) + m_canvas->bed_shape_changed(); +} +#else void View3D::set_bed_shape(const Pointfs& shape) { if (m_canvas != nullptr) m_canvas->set_bed_shape(shape); } +#endif // ENABLE_UNIQUE_BED void View3D::select_view(const std::string& direction) { @@ -337,6 +353,14 @@ Preview::~Preview() } } +#if ENABLE_UNIQUE_BED +void Preview::set_bed(Bed3D* bed) +{ + if (m_canvas != nullptr) + m_canvas->set_bed(bed); +} +#endif // ENABLE_UNIQUE_BED + void Preview::set_view_toolbar(GLToolbar* toolbar) { if (m_canvas != nullptr) @@ -368,10 +392,18 @@ void Preview::set_enabled(bool enabled) m_enabled = enabled; } -void Preview::set_bed_shape(const Pointfs& shape) +#if ENABLE_UNIQUE_BED +void Preview::bed_shape_changed() +{ + if (m_canvas != nullptr) + m_canvas->bed_shape_changed(); +} +#else +vvoid Preview::set_bed_shape(const Pointfs& shape) { m_canvas->set_bed_shape(shape); } +#endif // ENABLE_UNIQUE_BED void Preview::select_view(const std::string& direction) { diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 49dbed44d..3dec8bcbc 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -27,6 +27,9 @@ namespace GUI { class GLCanvas3D; class GLToolbar; +#if ENABLE_UNIQUE_BED +class Bed3D; +#endif // ENABLE_UNIQUE_BED class View3D : public wxPanel { @@ -48,10 +51,17 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } GLCanvas3D* get_canvas3d() { return m_canvas; } +#if ENABLE_UNIQUE_BED + void set_bed(Bed3D* bed); +#endif // ENABLE_UNIQUE_BED void set_view_toolbar(GLToolbar* toolbar); void set_as_dirty(); +#if ENABLE_UNIQUE_BED + void bed_shape_changed(); +#else void set_bed_shape(const Pointfs& shape); +#endif // ENABLE_UNIQUE_BED void select_view(const std::string& direction); void select_all(); @@ -114,12 +124,19 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } GLCanvas3D* get_canvas3d() { return m_canvas; } +#if ENABLE_UNIQUE_BED + void set_bed(Bed3D* bed); +#endif // ENABLE_UNIQUE_BED void set_view_toolbar(GLToolbar* toolbar); void set_number_extruders(unsigned int number_extruders); void set_canvas_as_dirty(); void set_enabled(bool enabled); +#if ENABLE_UNIQUE_BED + void bed_shape_changed(); +#else void set_bed_shape(const Pointfs& shape); +#endif // ENABLE_UNIQUE_BED void select_view(const std::string& direction); void set_viewport_from_scene(GLCanvas3D* canvas); void set_viewport_into_scene(GLCanvas3D* canvas); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e970a30dc..860d63ef8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -49,6 +49,9 @@ #include "GLCanvas3D.hpp" #include "GLToolbar.hpp" #include "GUI_Preview.hpp" +#if ENABLE_UNIQUE_BED +#include "3DBed.hpp" +#endif // ENABLE_UNIQUE_BED #include "Tab.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" @@ -952,6 +955,9 @@ struct Plater::priv wxPanel* current_panel; std::vector panels; Sidebar *sidebar; +#if ENABLE_UNIQUE_BED + Bed3D bed; +#endif // ENABLE_UNIQUE_BED View3D* view3D; GLToolbar view_toolbar; Preview *preview; @@ -1055,6 +1061,14 @@ struct Plater::priv void update_object_menu(); +#if ENABLE_UNIQUE_BED + // Set the bed shape to a single closed 2D polygon(array of two element arrays), + // triangulate the bed and store the triangles into m_bed.m_triangles, + // fills the m_bed.m_grid_lines and sets m_bed.m_origin. + // Sets m_bed.m_polygon to limit the object placement. + void set_bed_shape(const Pointfs& shape); +#endif // ENABLE_UNIQUE_BED + private: bool init_object_menu(); bool init_common_menu(wxMenu* menu, const bool is_part = false); @@ -1130,6 +1144,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); }); preview->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); }); +#if ENABLE_UNIQUE_BED + view3D->set_bed(&bed); + preview->set_bed(&bed); +#endif // ENABLE_UNIQUE_BED + panels.push_back(view3D); panels.push_back(preview); @@ -1186,10 +1205,16 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this); view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); +#if ENABLE_UNIQUE_BED + view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option("bed_shape")->values); }); +#endif // ENABLE_UNIQUE_BED // Preview events: preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); +#if ENABLE_UNIQUE_BED + preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option("bed_shape")->values); }); +#endif // ENABLE_UNIQUE_BED view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); @@ -2624,6 +2649,18 @@ bool Plater::priv::can_mirror() const return get_selection().is_from_single_instance(); } +#if ENABLE_UNIQUE_BED +void Plater::priv::set_bed_shape(const Pointfs& shape) +{ + bool new_shape = bed.set_shape(shape); + if (new_shape) + { + if (view3D) view3D->bed_shape_changed(); + if (preview) preview->bed_shape_changed(); + } +} +#endif // ENABLE_UNIQUE_BED + void Plater::priv::update_object_menu() { sidebar->obj_list()->append_menu_items_add_volume(&object_menu); @@ -3064,8 +3101,12 @@ void Plater::on_config_change(const DynamicPrintConfig &config) if (bed_shape_changed) { +#if ENABLE_UNIQUE_BED + p->set_bed_shape(p->config->option("bed_shape")->values); +#else if (p->view3D) p->view3D->set_bed_shape(p->config->option("bed_shape")->values); if (p->preview) p->preview->set_bed_shape(p->config->option("bed_shape")->values); +#endif // ENABLE_UNIQUE_BED } if (update_scheduled) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index e3601b65c..5f921008f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -10,6 +10,11 @@ #include "Preset.hpp" +#if ENABLE_UNIQUE_BED +#include "3DScene.hpp" +#include "GLTexture.hpp" +#endif // ENABLE_UNIQUE_BED + class wxButton; class wxBoxSizer; class wxGLCanvas;