diff --git a/resources/models/mk2_bed.stl b/resources/models/mk2_bed.stl index 07b7655ea6..8a74a23495 100644 Binary files a/resources/models/mk2_bed.stl and b/resources/models/mk2_bed.stl differ diff --git a/resources/models/mk3_bed.stl b/resources/models/mk3_bed.stl index 1bd1a8faac..6aff36f0bc 100644 Binary files a/resources/models/mk3_bed.stl and b/resources/models/mk3_bed.stl differ diff --git a/resources/models/sl1_bed.stl b/resources/models/sl1_bed.stl index b2cadde4bd..4a6980b09f 100644 Binary files a/resources/models/sl1_bed.stl and b/resources/models/sl1_bed.stl differ diff --git a/resources/shaders/printbed.fs b/resources/shaders/printbed.fs index be14347c21..d1316ca2fe 100644 --- a/resources/shaders/printbed.fs +++ b/resources/shaders/printbed.fs @@ -5,16 +5,30 @@ const vec3 back_color_light = vec3(0.365, 0.365, 0.365); uniform sampler2D texture; uniform bool transparent_background; +uniform bool svg_source; varying vec2 tex_coords; -void main() +vec4 svg_color() { + // takes foreground from texture + vec4 fore_color = texture2D(texture, tex_coords); + // calculates radial gradient vec3 back_color = vec3(mix(back_color_light, back_color_dark, smoothstep(0.0, 0.5, length(abs(tex_coords.xy) - vec2(0.5))))); - vec4 fore_color = texture2D(texture, tex_coords); - // blends foreground with background - gl_FragColor = vec4(mix(back_color, fore_color.rgb, fore_color.a), transparent_background ? fore_color.a : 1.0); + return vec4(mix(back_color, fore_color.rgb, fore_color.a), transparent_background ? fore_color.a : 1.0); +} + +vec4 non_svg_color() +{ + // takes foreground from texture + vec4 color = texture2D(texture, tex_coords); + return vec4(color.rgb, transparent_background ? color.a * 0.25 : color.a); +} + +void main() +{ + gl_FragColor = svg_source ? svg_color() : non_svg_color(); } \ No newline at end of file diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1c0fa914ec..8e091fb806 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -51,6 +51,16 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionPoints{ Vec2d(0, 0), Vec2d(200, 0), Vec2d(200, 200), Vec2d(0, 200) }); + def = this->add("bed_custom_texture", coString); + def->label = L("Bed custom texture"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + + def = this->add("bed_custom_model", coString); + def->label = L("Bed custom model"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + def = this->add("layer_height", coFloat); def->label = L("Layer height"); def->category = L("Layers and Perimeters"); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index ae43369d22..51d0920946 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -32,13 +32,4 @@ #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1) -//==================== -// 1.42.0.alpha7 techs -//==================== -#define ENABLE_1_42_0_ALPHA7 1 - -// Printbed textures generated from svg files -#define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7) - - #endif // _technologies_h_ diff --git a/src/slic3r/GUI/2DBed.cpp b/src/slic3r/GUI/2DBed.cpp index a339f3e66a..d2075f673d 100644 --- a/src/slic3r/GUI/2DBed.cpp +++ b/src/slic3r/GUI/2DBed.cpp @@ -18,10 +18,9 @@ wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), - #ifdef __APPLE__ m_user_drawn_background = false; #endif /*__APPLE__*/ - Bind(wxEVT_PAINT, ([this](wxPaintEvent &/* e */) { repaint(); })); - Bind(wxEVT_SIZE, ([this](wxSizeEvent & /* e */) { Refresh(); })); } -void Bed_2D::repaint() + +void Bed_2D::repaint(const std::vector& shape) { wxAutoBufferedPaintDC dc(this); auto cw = GetSize().GetWidth(); @@ -41,7 +40,7 @@ void Bed_2D::repaint() dc.DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight()); } - if (m_bed_shape.empty()) + if (shape.empty()) return; // reduce size to have some space around the drawn shape @@ -52,10 +51,9 @@ void Bed_2D::repaint() auto ccenter = cbb.center(); // get bounding box of bed shape in G - code coordinates - auto bed_shape = m_bed_shape; - auto bed_polygon = Polygon::new_scale(m_bed_shape); - auto bb = BoundingBoxf(m_bed_shape); - bb.merge(Vec2d(0, 0)); // origin needs to be in the visible area + auto bed_polygon = Polygon::new_scale(shape); + auto bb = BoundingBoxf(shape); + bb.merge(Vec2d(0, 0)); // origin needs to be in the visible area auto bw = bb.size()(0); auto bh = bb.size()(1); auto bcenter = bb.center(); @@ -73,8 +71,8 @@ void Bed_2D::repaint() // draw bed fill dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxBRUSHSTYLE_SOLID)); wxPointList pt_list; - for (auto pt: m_bed_shape) - { + for (auto pt : shape) + { Point pt_pix = to_pixels(pt, ch); pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1))); } @@ -155,13 +153,13 @@ void Bed_2D::repaint() // convert G - code coordinates into pixels -Point Bed_2D::to_pixels(Vec2d point, int height) +Point Bed_2D::to_pixels(const Vec2d& point, int height) { auto p = point * m_scale_factor + m_shift; return Point(p(0) + Border, height - p(1) + Border); } -void Bed_2D::set_pos(Vec2d pos) +void Bed_2D::set_pos(const Vec2d& pos) { m_pos = pos; Refresh(); diff --git a/src/slic3r/GUI/2DBed.hpp b/src/slic3r/GUI/2DBed.hpp index a61fb313d4..80926bea72 100644 --- a/src/slic3r/GUI/2DBed.hpp +++ b/src/slic3r/GUI/2DBed.hpp @@ -17,16 +17,13 @@ class Bed_2D : public wxPanel Vec2d m_shift = Vec2d::Zero(); Vec2d m_pos = Vec2d::Zero(); - Point to_pixels(Vec2d point, int height); - void repaint(); - void set_pos(Vec2d pos); + Point to_pixels(const Vec2d& point, int height); + void set_pos(const Vec2d& pos); public: - Bed_2D(wxWindow* parent); - ~Bed_2D() {} + explicit Bed_2D(wxWindow* parent); - std::vector m_bed_shape; - + void repaint(const std::vector& shape); }; diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 482cd58adc..97e200b38e 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -14,6 +14,7 @@ #include #include +#include static const float GROUND_Z = -0.02f; @@ -22,7 +23,6 @@ namespace GUI { bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords) { -#if ENABLE_TEXTURES_FROM_SVG m_vertices.clear(); unsigned int v_size = 3 * (unsigned int)triangles.size(); @@ -82,75 +82,12 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool } } } -#else - 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; - } - } - } -#endif // ENABLE_TEXTURES_FROM_SVG return true; } bool GeometryBuffer::set_from_lines(const Lines& lines, float z) { -#if ENABLE_TEXTURES_FROM_SVG m_vertices.clear(); unsigned int v_size = 2 * (unsigned int)lines.size(); @@ -174,37 +111,14 @@ bool GeometryBuffer::set_from_lines(const Lines& lines, float z) v2.position[2] = z; ++v_count; } -#else - 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; - } -#endif // ENABLE_TEXTURES_FROM_SVG return true; } -#if ENABLE_TEXTURES_FROM_SVG const float* GeometryBuffer::get_vertices_data() const { return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr; } -#endif // ENABLE_TEXTURES_FROM_SVG const double Bed3D::Axes::Radius = 0.5; const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius; @@ -274,22 +188,41 @@ void Bed3D::Axes::render_axis(double length) const Bed3D::Bed3D() : m_type(Custom) -#if ENABLE_TEXTURES_FROM_SVG + , m_custom_texture("") + , m_custom_model("") , m_requires_canvas_update(false) , m_vbo_id(0) -#endif // ENABLE_TEXTURES_FROM_SVG , m_scale_factor(1.0f) { } -bool Bed3D::set_shape(const Pointfs& shape) +bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model) { EType new_type = detect_type(shape); - if (m_shape == shape && m_type == new_type) + + // check that the passed custom texture filename is valid + std::string cst_texture(custom_texture); + if (!cst_texture.empty()) + { + if ((!boost::algorithm::iends_with(custom_texture, ".png") && !boost::algorithm::iends_with(custom_texture, ".svg")) || !boost::filesystem::exists(custom_texture)) + cst_texture = ""; + } + + // check that the passed custom texture filename is valid + std::string cst_model(custom_model); + if (!cst_model.empty()) + { + if (!boost::algorithm::iends_with(custom_model, ".stl") || !boost::filesystem::exists(custom_model)) + cst_model = ""; + } + + if ((m_shape == shape) && (m_type == new_type) && (m_custom_texture == cst_texture) && (m_custom_model == cst_model)) // No change, no need to update the UI. return false; m_shape = shape; + m_custom_texture = cst_texture; + m_custom_model = cst_model; m_type = new_type; calc_bounding_boxes(); @@ -307,9 +240,9 @@ bool Bed3D::set_shape(const Pointfs& shape) m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour; -#if ENABLE_TEXTURES_FROM_SVG reset(); -#endif // ENABLE_TEXTURES_FROM_SVG + m_texture.reset(); + m_model.reset(); // Set the origin and size for painting of the coordinate system axes. m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); @@ -329,11 +262,12 @@ Point Bed3D::point_projection(const Point& point) const return m_polygon.point_projection(point); } -#if ENABLE_TEXTURES_FROM_SVG -void Bed3D::render(GLCanvas3D* canvas, float theta, float scale_factor) const +void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor) const { m_scale_factor = scale_factor; + render_axes(); + switch (m_type) { case MK2: @@ -354,51 +288,11 @@ void Bed3D::render(GLCanvas3D* canvas, float theta, float scale_factor) const default: case Custom: { - render_custom(); + render_custom(canvas, theta > 90.0f); break; } } } -#else -void Bed3D::render(float theta, float scale_factor) const -{ - m_scale_factor = scale_factor; - - if (m_shape.empty()) - return; - - switch (m_type) - { - case MK2: - { - render_prusa("mk2", theta); - break; - } - case MK3: - { - render_prusa("mk3", theta); - break; - } - case SL1: - { - render_prusa("sl1", theta); - break; - } - default: - case Custom: - { - render_custom(); - break; - } - } -} -#endif // ENABLE_TEXTURES_FROM_SVG - -void Bed3D::render_axes() const -{ - if (!m_shape.empty()) - m_axes.render(); -} void Bed3D::calc_bounding_boxes() const { @@ -423,7 +317,7 @@ void Bed3D::calc_triangles(const ExPolygon& poly) Polygons triangles; poly.triangulate(&triangles); - if (!m_triangles.set_from_triangles(triangles, GROUND_Z, m_type != Custom)) + if (!m_triangles.set_from_triangles(triangles, GROUND_Z, true)) printf("Unable to create bed triangles\n"); } @@ -495,31 +389,76 @@ Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const return type; } -#if ENABLE_TEXTURES_FROM_SVG -void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom) const +void Bed3D::render_axes() const { - std::string tex_path = resources_dir() + "/icons/bed/" + key; + if (!m_shape.empty()) + m_axes.render(); +} - std::string model_path = resources_dir() + "/models/" + key; +void Bed3D::render_prusa(GLCanvas3D& canvas, const std::string& key, bool bottom) const +{ + if (!bottom) + render_model(m_custom_model.empty() ? resources_dir() + "/models/" + key + "_bed.stl" : m_custom_model); - // use higher resolution images if graphic card and opengl version allow - GLint max_tex_size = GLCanvas3DManager::get_gl_info().get_max_tex_size(); + render_texture(m_custom_texture.empty() ? resources_dir() + "/icons/bed/" + key + ".svg" : m_custom_texture, bottom, canvas); +} - std::string filename = tex_path + ".svg"; +void Bed3D::render_texture(const std::string& filename, bool bottom, GLCanvas3D& canvas) const +{ + if (filename.empty()) + { + m_texture.reset(); + render_default(bottom); + return; + } if ((m_texture.get_id() == 0) || (m_texture.get_source() != filename)) { - // generate a temporary lower resolution texture to show while no main texture levels have been compressed - if (!m_temp_texture.load_from_svg_file(filename, false, false, false, max_tex_size / 8)) - { - render_custom(); - return; - } + m_texture.reset(); - // starts generating the main texture, compression will run asynchronously - if (!m_texture.load_from_svg_file(filename, true, true, true, max_tex_size)) + if (boost::algorithm::iends_with(filename, ".svg")) { - render_custom(); + // use higher resolution images if graphic card and opengl version allow + GLint max_tex_size = GLCanvas3DManager::get_gl_info().get_max_tex_size(); + if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != filename)) + { + // generate a temporary lower resolution texture to show while no main texture levels have been compressed + if (!m_temp_texture.load_from_svg_file(filename, false, false, false, max_tex_size / 8)) + { + render_default(bottom); + return; + } + } + + // starts generating the main texture, compression will run asynchronously + if (!m_texture.load_from_svg_file(filename, true, true, true, max_tex_size)) + { + render_default(bottom); + return; + } + } + else if (boost::algorithm::iends_with(filename, ".png")) + { + // generate a temporary lower resolution texture to show while no main texture levels have been compressed + if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != filename)) + { + if (!m_temp_texture.load_from_file(filename, false, GLTexture::None, false)) + { + render_default(bottom); + return; + } + } + + // starts generating the main texture, compression will run asynchronously + if (!m_texture.load_from_file(filename, true, GLTexture::MultiThreaded, true)) + { + render_default(bottom); + return; + } + } + else + { + render_default(bottom); return; } } @@ -536,292 +475,161 @@ void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom } else if (m_requires_canvas_update && m_texture.all_compressed_data_sent_to_gpu()) { - if (canvas != nullptr) - canvas->stop_keeping_dirty(); - + canvas.stop_keeping_dirty(); m_requires_canvas_update = false; } - if (!bottom) + if (m_triangles.get_vertices_count() > 0) { - filename = model_path + "_bed.stl"; - if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) { - 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); + if (m_shader.get_shader_program_id() == 0) + m_shader.init("printbed.vs", "printbed.fs"); - m_model.center_around(offset); - - // update extended bounding box - calc_bounding_boxes(); - } - - if (!m_model.get_filename().empty()) + if (m_shader.is_initialized()) { - glsafe(::glEnable(GL_LIGHTING)); - m_model.render(); - glsafe(::glDisable(GL_LIGHTING)); - } - } + m_shader.start_using(); + m_shader.set_uniform("transparent_background", bottom); + m_shader.set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); - unsigned int triangles_vcount = m_triangles.get_vertices_count(); - if (triangles_vcount > 0) - { - if (m_vbo_id == 0) - { - glsafe(::glGenBuffers(1, &m_vbo_id)); + if (m_vbo_id == 0) + { + glsafe(::glGenBuffers(1, &m_vbo_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); + glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + } + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glDepthMask(GL_FALSE)); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + if (bottom) + glsafe(::glFrontFace(GL_CW)); + + unsigned int stride = m_triangles.get_vertex_data_size(); + + GLint position_id = m_shader.get_attrib_location("v_position"); + GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords"); + + // show the temporary texture while no compressed data is available + GLuint tex_id = (GLuint)m_temp_texture.get_id(); + if (tex_id == 0) + tex_id = (GLuint)m_texture.get_id(); + + glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); - glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW)); + + if (position_id != -1) + { + glsafe(::glEnableVertexAttribArray(position_id)); + glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset())); + } + if (tex_coords_id != -1) + { + glsafe(::glEnableVertexAttribArray(tex_coords_id)); + glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset())); + } + + glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count())); + + if (tex_coords_id != -1) + glsafe(::glDisableVertexAttribArray(tex_coords_id)); + + if (position_id != -1) + glsafe(::glDisableVertexAttribArray(position_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - } - - glsafe(::glEnable(GL_DEPTH_TEST)); - glsafe(::glDepthMask(GL_FALSE)); - - glsafe(::glEnable(GL_BLEND)); - glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - - if (bottom) - glsafe(::glFrontFace(GL_CW)); - - render_prusa_shader(bottom); - - if (bottom) - glsafe(::glFrontFace(GL_CCW)); - - glsafe(::glDisable(GL_BLEND)); - glsafe(::glDepthMask(GL_TRUE)); - } -} - -void Bed3D::render_prusa_shader(bool transparent) const -{ - if (m_shader.get_shader_program_id() == 0) - m_shader.init("printbed.vs", "printbed.fs"); - - if (m_shader.is_initialized()) - { - m_shader.start_using(); - m_shader.set_uniform("transparent_background", transparent); - - unsigned int stride = m_triangles.get_vertex_data_size(); - - GLint position_id = m_shader.get_attrib_location("v_position"); - GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords"); - - // show the temporary texture while no compressed data is available - GLuint tex_id = (GLuint)m_temp_texture.get_id(); - if (tex_id == 0) - tex_id = (GLuint)m_texture.get_id(); - - glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id)); - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id)); - - if (position_id != -1) - { - glsafe(::glEnableVertexAttribArray(position_id)); - glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset())); - } - if (tex_coords_id != -1) - { - glsafe(::glEnableVertexAttribArray(tex_coords_id)); - glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset())); - } - - glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count())); - - if (tex_coords_id != -1) - glsafe(::glDisableVertexAttribArray(tex_coords_id)); - - if (position_id != -1) - glsafe(::glDisableVertexAttribArray(position_id)); - - glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); - glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); - - m_shader.stop_using(); - } -} -#else -void Bed3D::render_prusa(const std::string& key, float theta) const -{ - std::string tex_path = resources_dir() + "/icons/bed/" + key; - - // use higher resolution images if graphic card allows - GLint max_tex_size; - glsafe(::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; - - // use anisotropic filter if graphic card allows - GLfloat max_anisotropy = 0.0f; - if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) - glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy)); - - 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, true)) - { - render_custom(); - return; - } - - 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)); + + if (bottom) + glsafe(::glFrontFace(GL_CCW)); + + glsafe(::glDisable(GL_BLEND)); + glsafe(::glDepthMask(GL_TRUE)); + + m_shader.stop_using(); } } - - 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, true)) - { - render_custom(); - return; - } - - 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)); - } - } - - if (theta <= 90.0f) - { - filename = model_path + "_bed.stl"; - if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) { - 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)); - } } -#endif // ENABLE_TEXTURES_FROM_SVG -void Bed3D::render_custom() const +void Bed3D::render_model(const std::string& filename) const { -#if ENABLE_TEXTURES_FROM_SVG - m_texture.reset(); -#else - m_top_texture.reset(); - m_bottom_texture.reset(); -#endif // ENABLE_TEXTURES_FROM_SVG + if (filename.empty()) + return; - unsigned int triangles_vcount = m_triangles.get_vertices_count(); - if (triangles_vcount > 0) + if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) + { + // 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; + m_model.set_offset(shift); + + // update extended bounding box + calc_bounding_boxes(); + } + + if (!m_model.get_filename().empty()) { 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)); -#if ENABLE_TEXTURES_FROM_SVG - glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data())); -#else - glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices())); -#endif // ENABLE_TEXTURES_FROM_SVG - 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)); -#if ENABLE_TEXTURES_FROM_SVG - glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data())); -#else - glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices())); -#endif // ENABLE_TEXTURES_FROM_SVG - glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount)); - - glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); - - glsafe(::glDisable(GL_BLEND)); + m_model.render(); glsafe(::glDisable(GL_LIGHTING)); } } -#if ENABLE_TEXTURES_FROM_SVG +void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const +{ + if (m_custom_texture.empty() && m_custom_model.empty()) + { + render_default(bottom); + return; + } + + if (!bottom) + render_model(m_custom_model); + + render_texture(m_custom_texture, bottom, canvas); +} + +void Bed3D::render_default(bool bottom) const +{ + m_texture.reset(); + + unsigned int triangles_vcount = m_triangles.get_vertices_count(); + if (triangles_vcount > 0) + { + bool has_model = !m_model.get_filename().empty(); + + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + + if (!has_model && !bottom) + { + // draw background + glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f)); + glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); + glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data())); + glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); + } + + // draw grid + glsafe(::glLineWidth(3.0f * m_scale_factor)); + if (has_model && !bottom) + glsafe(::glColor4f(0.75f, 0.75f, 0.75f, 1.0f)); + else + glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f)); + glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data())); + glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count())); + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + + glsafe(::glDisable(GL_BLEND)); + } +} + void Bed3D::reset() { if (m_vbo_id > 0) @@ -830,7 +638,6 @@ void Bed3D::reset() m_vbo_id = 0; } } -#endif // ENABLE_TEXTURES_FROM_SVG } // GUI } // Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index 6c8913a9ba..c9a30e6ec2 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -3,9 +3,7 @@ #include "GLTexture.hpp" #include "3DScene.hpp" -#if ENABLE_TEXTURES_FROM_SVG #include "GLShader.hpp" -#endif // ENABLE_TEXTURES_FROM_SVG class GLUquadric; typedef class GLUquadric GLUquadricObj; @@ -17,7 +15,6 @@ class GLCanvas3D; class GeometryBuffer { -#if ENABLE_TEXTURES_FROM_SVG struct Vertex { float position[3]; @@ -31,27 +28,17 @@ class GeometryBuffer }; std::vector m_vertices; -#else - std::vector m_vertices; - std::vector m_tex_coords; -#endif // ENABLE_TEXTURES_FROM_SVG public: bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords); bool set_from_lines(const Lines& lines, float z); -#if ENABLE_TEXTURES_FROM_SVG const float* get_vertices_data() const; unsigned int get_vertices_data_size() const { return (unsigned int)m_vertices.size() * get_vertex_data_size(); } unsigned int get_vertex_data_size() const { return (unsigned int)(5 * sizeof(float)); } size_t get_position_offset() const { return 0; } size_t get_tex_coords_offset() const { return (size_t)(3 * sizeof(float)); } unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); } -#else - 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; } -#endif // ENABLE_TEXTURES_FROM_SVG }; class Bed3D @@ -87,12 +74,13 @@ public: private: EType m_type; Pointfs m_shape; + std::string m_custom_texture; + std::string m_custom_model; mutable BoundingBoxf3 m_bounding_box; mutable BoundingBoxf3 m_extended_bounding_box; Polygon m_polygon; GeometryBuffer m_triangles; GeometryBuffer m_gridlines; -#if ENABLE_TEXTURES_FROM_SVG mutable GLTexture m_texture; // temporary texture shown until the main texture has still no levels compressed mutable GLTexture m_temp_texture; @@ -100,10 +88,6 @@ private: mutable bool m_requires_canvas_update; mutable Shader m_shader; mutable unsigned int m_vbo_id; -#else - mutable GLTexture m_top_texture; - mutable GLTexture m_bottom_texture; -#endif // ENABLE_TEXTURES_FROM_SVG mutable GLBed m_model; Axes m_axes; @@ -111,9 +95,7 @@ private: public: Bed3D(); -#if ENABLE_TEXTURES_FROM_SVG ~Bed3D() { reset(); } -#endif // ENABLE_TEXTURES_FROM_SVG EType get_type() const { return m_type; } @@ -122,34 +104,26 @@ public: 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); + bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model); const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; } bool contains(const Point& point) const; Point point_projection(const Point& point) const; -#if ENABLE_TEXTURES_FROM_SVG - void render(GLCanvas3D* canvas, float theta, float scale_factor) const; -#else - void render(float theta, float scale_factor) const; -#endif // ENABLE_TEXTURES_FROM_SVG - void render_axes() const; + void render(GLCanvas3D& canvas, float theta, float scale_factor) const; private: void calc_bounding_boxes() const; void calc_triangles(const ExPolygon& poly); void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); EType detect_type(const Pointfs& shape) const; -#if ENABLE_TEXTURES_FROM_SVG - void render_prusa(GLCanvas3D* canvas, const std::string& key, bool bottom) const; - void render_prusa_shader(bool transparent) const; -#else - void render_prusa(const std::string& key, float theta) const; -#endif // ENABLE_TEXTURES_FROM_SVG - void render_custom() const; -#if ENABLE_TEXTURES_FROM_SVG + void render_axes() const; + void render_prusa(GLCanvas3D& canvas, const std::string& key, bool bottom) const; + void render_texture(const std::string& filename, bool bottom, GLCanvas3D& canvas) const; + void render_model(const std::string& filename) const; + void render_custom(GLCanvas3D& canvas, bool bottom) const; + void render_default(bool bottom) const; void reset(); -#endif // ENABLE_TEXTURES_FROM_SVG }; } // GUI diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 9e3eaf41d3..dba5958466 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1736,13 +1736,7 @@ bool GLBed::on_init_from_file(const std::string& filename) m_filename = filename; - ModelObject* model_object = model.objects.front(); - model_object->center_around_origin(); - - TriangleMesh mesh = model.mesh(); - mesh.repair(); - - m_volume.indexed_vertex_array.load_mesh(mesh); + m_volume.indexed_vertex_array.load_mesh(model.mesh()); float color[4] = { 0.235f, 0.235f, 0.235f, 1.0f }; set_color(color, 4); diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index d52204d4a7..1177c91804 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -4,23 +4,25 @@ #include #include #include +#include #include "libslic3r/BoundingBox.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/Polygon.hpp" #include "boost/nowide/iostream.hpp" +#include #include namespace Slic3r { namespace GUI { -void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt) +void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) { SetFont(wxGetApp().normal_font()); m_panel = new BedShapePanel(this); - m_panel->build_panel(default_pt); + m_panel->build_panel(default_pt, custom_texture, custom_model); auto main_sizer = new wxBoxSizer(wxVERTICAL); main_sizer->Add(m_panel, 1, wxEXPAND); @@ -51,59 +53,69 @@ void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect) Refresh(); } -void BedShapePanel::build_panel(ConfigOptionPoints* default_pt) +const std::string BedShapePanel::NONE = "None"; +const std::string BedShapePanel::EMPTY_STRING = ""; + +void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model) { + m_shape = default_pt.values; + m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value; + m_custom_model = custom_model.value.empty() ? NONE : custom_model.value; + auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape"))); + sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font()); // shape options - m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, - wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP); + m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP); sbsizer->Add(m_shape_options_book); auto optgroup = init_shape_options_page(_(L("Rectangular"))); - ConfigOptionDef def; - def.type = coPoints; - def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) }); - def.label = L("Size"); - def.tooltip = L("Size in X and Y of the rectangular plate."); - Option option(def, "rect_size"); - optgroup->append_single_option_line(option); + ConfigOptionDef def; + def.type = coPoints; + def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) }); + def.label = L("Size"); + def.tooltip = L("Size in X and Y of the rectangular plate."); + Option option(def, "rect_size"); + optgroup->append_single_option_line(option); - def.type = coPoints; - def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) }); - def.label = L("Origin"); - def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle."); - option = Option(def, "rect_origin"); - optgroup->append_single_option_line(option); + def.type = coPoints; + def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) }); + def.label = L("Origin"); + def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle."); + option = Option(def, "rect_origin"); + optgroup->append_single_option_line(option); - optgroup = init_shape_options_page(_(L("Circular"))); - def.type = coFloat; - def.set_default_value(new ConfigOptionFloat(200)); - def.sidetext = L("mm"); - def.label = L("Diameter"); - def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center."); - option = Option(def, "diameter"); - optgroup->append_single_option_line(option); + optgroup = init_shape_options_page(_(L("Circular"))); + def.type = coFloat; + def.set_default_value(new ConfigOptionFloat(200)); + def.sidetext = L("mm"); + def.label = L("Diameter"); + def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center."); + option = Option(def, "diameter"); + optgroup->append_single_option_line(option); - optgroup = init_shape_options_page(_(L("Custom"))); - Line line{ "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - auto shape_btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL..."))); - wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL); - shape_sizer->Add(shape_btn, 1, wxEXPAND); + optgroup = init_shape_options_page(_(L("Custom"))); + Line line{ "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + wxButton* shape_btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL..."))); + wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL); + shape_sizer->Add(shape_btn, 1, wxEXPAND); - wxSizer* sizer = new wxBoxSizer(wxVERTICAL); - sizer->Add(shape_sizer, 1, wxEXPAND); + wxSizer* sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(shape_sizer, 1, wxEXPAND); - shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) - { - load_stl(); - })); + shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) + { + load_stl(); + })); - return sizer; - }; - optgroup->append_line(line); + return sizer; + }; + optgroup->append_line(line); + + wxPanel* texture_panel = init_texture_panel(); + wxPanel* model_panel = init_model_panel(); Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent& e) { @@ -112,13 +124,17 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt) // right pane with preview canvas m_canvas = new Bed_2D(this); - m_canvas->m_bed_shape = default_pt->values; + m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent& e) { m_canvas->repaint(m_shape); }); + m_canvas->Bind(wxEVT_SIZE, [this](wxSizeEvent& e) { m_canvas->Refresh(); }); - // main sizer - auto top_sizer = new wxBoxSizer(wxHORIZONTAL); - top_sizer->Add(sbsizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10); - if (m_canvas) - top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10) ; + wxSizer* left_sizer = new wxBoxSizer(wxVERTICAL); + left_sizer->Add(sbsizer, 0, wxEXPAND); + left_sizer->Add(texture_panel, 1, wxEXPAND); + left_sizer->Add(model_panel, 1, wxEXPAND); + + wxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL); + top_sizer->Add(left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10); + top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10); SetSizerAndFit(top_sizer); @@ -134,20 +150,155 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt) // Create a panel for a rectangular / circular / custom bed shape. ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title) { - auto panel = new wxPanel(m_shape_options_book); - ConfigOptionsGroupShp optgroup; - optgroup = std::make_shared(panel, _(L("Settings"))); + wxPanel* panel = new wxPanel(m_shape_options_book); + ConfigOptionsGroupShp optgroup = std::make_shared(panel, _(L("Settings"))); optgroup->label_width = 10; - optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { - update_shape(); - }; - - m_optgroups.push_back(optgroup); - panel->SetSizerAndFit(optgroup->sizer); - m_shape_options_book->AddPage(panel, title); + optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + update_shape(); + }; + + m_optgroups.push_back(optgroup); + panel->SetSizerAndFit(optgroup->sizer); + m_shape_options_book->AddPage(panel, title); - return optgroup; + return optgroup; +} + +wxPanel* BedShapePanel::init_texture_panel() +{ + wxPanel* panel = new wxPanel(this); + ConfigOptionsGroupShp optgroup = std::make_shared(panel, _(L("Texture"))); + + optgroup->label_width = 10; + optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + update_shape(); + }; + + Line line{ "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load..."))); + wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL); + load_sizer->Add(load_btn, 1, wxEXPAND); + + wxStaticText* filename_lbl = new wxStaticText(parent, wxID_ANY, _(NONE)); + wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL); + filename_sizer->Add(filename_lbl, 1, wxEXPAND); + + wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove"))); + wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL); + remove_sizer->Add(remove_btn, 1, wxEXPAND); + + wxSizer* sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(filename_sizer, 1, wxEXPAND); + sizer->Add(load_sizer, 1, wxEXPAND); + sizer->Add(remove_sizer, 1, wxEXPAND); + + load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) + { + load_texture(); + })); + + remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) + { + m_custom_texture = NONE; + update_shape(); + })); + + filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) + { + e.SetText(_(boost::filesystem::path(m_custom_texture).filename().string())); + wxStaticText* lbl = dynamic_cast(e.GetEventObject()); + if (lbl != nullptr) + { + wxString tooltip_text = (m_custom_texture == NONE) ? _(L("")) : _(m_custom_texture); + wxToolTip* tooltip = lbl->GetToolTip(); + if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text)) + lbl->SetToolTip(tooltip_text); + } + })); + + remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) + { + e.Enable(m_custom_texture != NONE); + })); + + return sizer; + }; + optgroup->append_line(line); + + panel->SetSizerAndFit(optgroup->sizer); + + return panel; +} + +wxPanel* BedShapePanel::init_model_panel() +{ + wxPanel* panel = new wxPanel(this); + ConfigOptionsGroupShp optgroup = std::make_shared(panel, _(L("Model"))); + + optgroup->label_width = 10; + optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + update_shape(); + }; + + Line line{ "", "" }; + line.full_width = 1; + line.widget = [this](wxWindow* parent) { + wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load..."))); + wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL); + load_sizer->Add(load_btn, 1, wxEXPAND); + + wxStaticText* filename_lbl = new wxStaticText(parent, wxID_ANY, _(NONE)); + wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL); + filename_sizer->Add(filename_lbl, 1, wxEXPAND); + + wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove"))); + wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL); + remove_sizer->Add(remove_btn, 1, wxEXPAND); + + wxSizer* sizer = new wxBoxSizer(wxVERTICAL); + sizer->Add(filename_sizer, 1, wxEXPAND); + sizer->Add(load_sizer, 1, wxEXPAND); + sizer->Add(remove_sizer, 1, wxEXPAND); + + load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) + { + load_model(); + })); + + remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) + { + m_custom_model = NONE; + update_shape(); + })); + + filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) + { + e.SetText(_(boost::filesystem::path(m_custom_model).filename().string())); + wxStaticText* lbl = dynamic_cast(e.GetEventObject()); + if (lbl != nullptr) + { + wxString tooltip_text = (m_custom_model == NONE) ? _(L("")) : _(m_custom_model); + wxToolTip* tooltip = lbl->GetToolTip(); + if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text)) + lbl->SetToolTip(tooltip_text); + } + })); + + remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e) + { + e.Enable(m_custom_model != NONE); + })); + + return sizer; + }; + optgroup->append_line(line); + + panel->SetSizerAndFit(optgroup->sizer); + + return panel; } // Called from the constructor. @@ -155,20 +306,20 @@ ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& tit // Deduce the bed shape type(rect, circle, custom) // This routine shall be smart enough if the user messes up // with the list of points in the ini file directly. -void BedShapePanel::set_shape(ConfigOptionPoints* points) +void BedShapePanel::set_shape(const ConfigOptionPoints& points) { - auto polygon = Polygon::new_scale(points->values); + auto polygon = Polygon::new_scale(points.values); // is this a rectangle ? - if (points->size() == 4) { - auto lines = polygon.lines(); + if (points.size() == 4) { + auto lines = polygon.lines(); if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { // okay, it's a rectangle // find origin coordf_t x_min, x_max, y_min, y_max; - x_max = x_min = points->values[0](0); - y_max = y_min = points->values[0](1); - for (auto pt : points->values) + x_max = x_min = points.values[0](0); + y_max = y_min = points.values[0](1); + for (auto pt : points.values) { x_min = std::min(x_min, pt(0)); x_max = std::max(x_max, pt(0)); @@ -219,8 +370,8 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points) } } - if (points->size() < 3) { - // Invalid polygon.Revert to default bed dimensions. + if (points.size() < 3) { + // Invalid polygon.Revert to default bed dimensions. m_shape_options_book->SetSelection(SHAPE_RECTANGULAR); auto optgroup = m_optgroups[SHAPE_RECTANGULAR]; optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(200, 200) }); @@ -232,7 +383,7 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points) // This is a custom bed shape, use the polygon provided. m_shape_options_book->SetSelection(SHAPE_CUSTOM); // Copy the polygon to the canvas, make a copy of the array. - m_loaded_bed_shape = points->values; + m_loaded_shape = points.values; update_shape(); } @@ -277,11 +428,11 @@ void BedShapePanel::update_shape() x1 -= dx; y0 -= dy; y1 -= dy; - m_canvas->m_bed_shape = { Vec2d(x0, y0), - Vec2d(x1, y0), - Vec2d(x1, y1), - Vec2d(x0, y1)}; - } + m_shape = { Vec2d(x0, y0), + Vec2d(x1, y0), + Vec2d(x1, y1), + Vec2d(x0, y1) }; + } else if(page_idx == SHAPE_CIRCULAR) { double diameter; try{ @@ -293,16 +444,16 @@ void BedShapePanel::update_shape() if (diameter == 0.0) return ; auto r = diameter / 2; auto twopi = 2 * PI; - auto edges = 60; - std::vector points; - for (size_t i = 1; i <= 60; ++i) { - auto angle = i * twopi / edges; + auto edges = 72; + std::vector points; + for (size_t i = 1; i <= edges; ++i) { + auto angle = i * twopi / edges; points.push_back(Vec2d(r*cos(angle), r*sin(angle))); } - m_canvas->m_bed_shape = points; - } + m_shape = points; + } else if (page_idx == SHAPE_CUSTOM) - m_canvas->m_bed_shape = m_loaded_bed_shape; + m_shape = m_loaded_shape; update_preview(); } @@ -310,27 +461,27 @@ void BedShapePanel::update_shape() // Loads an stl file, projects it to the XY plane and calculates a polygon. void BedShapePanel::load_stl() { - auto dialog = new wxFileDialog(this, _(L("Choose a file to import bed shape from (STL/OBJ/AMF/3MF/PRUSA):")), "", "", - file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (dialog->ShowModal() != wxID_OK) { - dialog->Destroy(); - return; - } - wxArrayString input_file; - dialog->GetPaths(input_file); - dialog->Destroy(); + wxFileDialog dialog(this, _(L("Choose an STL file to import bed shape from:")), "", "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (dialog.ShowModal() != wxID_OK) + return; - std::string file_name = input_file[0].ToUTF8().data(); + std::string file_name = dialog.GetPath().ToUTF8().data(); + if (!boost::algorithm::iends_with(file_name, ".stl")) + { + show_error(this, _(L("Invalid file format."))); + return; + } + + wxBusyCursor wait; Model model; try { - model = Model::read_from_file(file_name); - } - catch (std::exception &e) { - auto msg = _(L("Error!")) + " " + file_name + " : " + e.what() + "."; - show_error(this, msg); - exit(1); + model = Model::read_from_file(file_name); } + catch (std::exception &) { + show_error(this, _(L("Error! Invalid model"))); + return; + } auto mesh = model.mesh(); auto expolygons = mesh.horizontal_projection(); @@ -349,7 +500,53 @@ void BedShapePanel::load_stl() for (auto pt : polygon.points) points.push_back(unscale(pt)); - m_loaded_bed_shape = points; + m_loaded_shape = points; + update_shape(); +} + +void BedShapePanel::load_texture() +{ + wxFileDialog dialog(this, _(L("Choose a file to import bed texture from (PNG/SVG):")), "", "", + file_wildcards(FT_TEX), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + + if (dialog.ShowModal() != wxID_OK) + return; + + m_custom_texture = NONE; + + std::string file_name = dialog.GetPath().ToUTF8().data(); + if (!boost::algorithm::iends_with(file_name, ".png") && !boost::algorithm::iends_with(file_name, ".svg")) + { + show_error(this, _(L("Invalid file format."))); + return; + } + + wxBusyCursor wait; + + m_custom_texture = file_name; + update_shape(); +} + +void BedShapePanel::load_model() +{ + wxFileDialog dialog(this, _(L("Choose an STL file to import bed model from:")), "", "", + file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); + + if (dialog.ShowModal() != wxID_OK) + return; + + m_custom_model = NONE; + + std::string file_name = dialog.GetPath().ToUTF8().data(); + if (!boost::algorithm::iends_with(file_name, ".stl")) + { + show_error(this, _(L("Invalid file format."))); + return; + } + + wxBusyCursor wait; + + m_custom_model = file_name; update_shape(); } diff --git a/src/slic3r/GUI/BedShapeDialog.hpp b/src/slic3r/GUI/BedShapeDialog.hpp index 6600a1c84d..bf12cc8934 100644 --- a/src/slic3r/GUI/BedShapeDialog.hpp +++ b/src/slic3r/GUI/BedShapeDialog.hpp @@ -16,27 +16,40 @@ namespace GUI { using ConfigOptionsGroupShp = std::shared_ptr; class BedShapePanel : public wxPanel { + static const std::string NONE; + static const std::string EMPTY_STRING; + Bed_2D* m_canvas; - std::vector m_loaded_bed_shape; + std::vector m_shape; + std::vector m_loaded_shape; + std::string m_custom_texture; + std::string m_custom_model; public: - BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {} - ~BedShapePanel() {} + BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY), m_custom_texture(NONE), m_custom_model(NONE) {} - void build_panel(ConfigOptionPoints* default_pt); - + void build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model); + + // Returns the resulting bed shape polygon. This value will be stored to the ini file. + const std::vector& get_shape() const { return m_shape; } + const std::string& get_custom_texture() const { return (m_custom_texture != NONE) ? m_custom_texture : EMPTY_STRING; } + const std::string& get_custom_model() const { return (m_custom_model != NONE) ? m_custom_model : EMPTY_STRING; } + +private: ConfigOptionsGroupShp init_shape_options_page(const wxString& title); - void set_shape(ConfigOptionPoints* points); - void update_preview(); + wxPanel* init_texture_panel(); + wxPanel* init_model_panel(); + void set_shape(const ConfigOptionPoints& points); + void update_preview(); void update_shape(); void load_stl(); - - // Returns the resulting bed shape polygon. This value will be stored to the ini file. - std::vector GetValue() { return m_canvas->m_bed_shape; } + void load_texture(); + void load_model(); wxChoicebook* m_shape_options_book; std::vector m_optgroups; + friend class BedShapeDialog; }; class BedShapeDialog : public DPIDialog @@ -45,10 +58,12 @@ class BedShapeDialog : public DPIDialog public: BedShapeDialog(wxWindow* parent) : DPIDialog(parent, wxID_ANY, _(L("Bed Shape")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {} - ~BedShapeDialog() {} - void build_dialog(ConfigOptionPoints* default_pt); - std::vector GetValue() { return m_panel->GetValue(); } + void build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model); + + const std::vector& get_shape() const { return m_panel->get_shape(); } + const std::string& get_custom_texture() const { return m_panel->get_custom_texture(); } + const std::string& get_custom_model() const { return m_panel->get_custom_model(); } protected: void on_dpi_changed(const wxRect &suggested_rect) override; diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 8b08f6f7fa..6a70cb9fdb 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -532,15 +532,21 @@ PageBedShape::PageBedShape(ConfigWizard *parent) { append_text(_(L("Set the shape of your printer's bed."))); - shape_panel->build_panel(wizard_p()->custom_config->option("bed_shape")); + shape_panel->build_panel(*wizard_p()->custom_config->option("bed_shape"), + *wizard_p()->custom_config->option("bed_custom_texture"), + *wizard_p()->custom_config->option("bed_custom_model")); + append(shape_panel); } void PageBedShape::apply_custom_config(DynamicPrintConfig &config) { - const auto points(shape_panel->GetValue()); - auto *opt = new ConfigOptionPoints(points); - config.set_key_value("bed_shape", opt); + const std::vector& points = shape_panel->get_shape(); + const std::string& custom_texture = shape_panel->get_custom_texture(); + const std::string& custom_model = shape_panel->get_custom_model(); + config.set_key_value("bed_shape", new ConfigOptionPoints(points)); + config.set_key_value("bed_custom_texture", new ConfigOptionString(custom_texture)); + config.set_key_value("bed_custom_model", new ConfigOptionString(custom_model)); } PageDiameters::PageDiameters(ConfigWizard *parent) @@ -1086,7 +1092,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) p->load_vendors(); p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({ - "gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature", + "gcode_flavor", "bed_shape", "bed_custom_texture", "bed_custom_model", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature", })); p->index = new ConfigWizardIndex(this); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a244552052..5e505bb416 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -116,87 +116,6 @@ void Size::set_scale_factor(int scale_factor) m_scale_factor = scale_factor; } -#if !ENABLE_TEXTURES_FROM_SVG -GLCanvas3D::Shader::Shader() - : m_shader(nullptr) -{ -} - -GLCanvas3D::Shader::~Shader() -{ - _reset(); -} - -bool GLCanvas3D::Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) -{ - if (is_initialized()) - return true; - - m_shader = new GLShader(); - if (m_shader != nullptr) - { - if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str())) - { - std::cout << "Compilaton of shader failed:" << std::endl; - std::cout << m_shader->last_error << std::endl; - _reset(); - return false; - } - } - - return true; -} - -bool GLCanvas3D::Shader::is_initialized() const -{ - return (m_shader != nullptr); -} - -bool GLCanvas3D::Shader::start_using() const -{ - if (is_initialized()) - { - m_shader->enable(); - return true; - } - else - return false; -} - -void GLCanvas3D::Shader::stop_using() const -{ - if (m_shader != nullptr) - m_shader->disable(); -} - -void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const -{ - if (m_shader != nullptr) - m_shader->set_uniform(name.c_str(), value); -} - -void GLCanvas3D::Shader::set_uniform(const std::string& name, const float* matrix) const -{ - if (m_shader != nullptr) - m_shader->set_uniform(name.c_str(), matrix); -} - -const GLShader* GLCanvas3D::Shader::get_shader() const -{ - return m_shader; -} - -void GLCanvas3D::Shader::_reset() -{ - if (m_shader != nullptr) - { - m_shader->release(); - delete m_shader; - m_shader = nullptr; - } -} -#endif // !ENABLE_TEXTURES_FROM_SVG - GLCanvas3D::LayersEditing::LayersEditing() : m_enabled(false) , m_z_texture_id(0) @@ -383,7 +302,7 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas if (m_tooltip_texture.get_id() == 0) { std::string filename = resources_dir() + "/icons/variable_layer_height_tooltip.png"; - if (!m_tooltip_texture.load_from_file(filename, false, true)) + if (!m_tooltip_texture.load_from_file(filename, false, GLTexture::SingleThreaded, false)) return; } @@ -415,7 +334,7 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co if (m_reset_texture.get_id() == 0) { std::string filename = resources_dir() + "/icons/variable_layer_height_reset.png"; - if (!m_reset_texture.load_from_file(filename, false, true)) + if (!m_reset_texture.load_from_file(filename, false, GLTexture::SingleThreaded, false)) return; } @@ -1642,19 +1561,10 @@ void GLCanvas3D::render() glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); _render_background(); - // textured bed needs to be rendered after objects if the texture is transparent - bool early_bed_render = m_bed.is_custom() || (theta <= 90.0f); - if (early_bed_render) - _render_bed(theta); - _render_objects(); _render_sla_slices(); _render_selection(); - - _render_axes(); - - if (!early_bed_render) - _render_bed(theta); + _render_bed(theta); #if ENABLE_RENDER_SELECTION_CENTER _render_selection_center(); @@ -3986,16 +3896,7 @@ void GLCanvas3D::_render_bed(float theta) const #if ENABLE_RETINA_GL scale_factor = m_retina_helper->get_scale_factor(); #endif // ENABLE_RETINA_GL -#if ENABLE_TEXTURES_FROM_SVG - m_bed.render(const_cast(this), theta, scale_factor); -#else - m_bed.render(theta, scale_factor); -#endif // ENABLE_TEXTURES_FROM_SVG -} - -void GLCanvas3D::_render_axes() const -{ - m_bed.render_axes(); + m_bed.render(const_cast(*this), theta, scale_factor); } void GLCanvas3D::_render_objects() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 941eeaaf60..c9803f9db8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -158,32 +158,6 @@ class GLCanvas3D void reset() { first_volumes.clear(); } }; -#if !ENABLE_TEXTURES_FROM_SVG - class Shader - { - GLShader* m_shader; - - public: - Shader(); - ~Shader(); - - bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); - - bool is_initialized() const; - - bool start_using() const; - void stop_using() const; - - void set_uniform(const std::string& name, float value) const; - void set_uniform(const std::string& name, const float* matrix) const; - - const GLShader* get_shader() const; - - private: - void _reset(); - }; -#endif // !ENABLE_TEXTURES_FROM_SVG - class LayersEditing { public: @@ -688,7 +662,6 @@ private: void _rectangular_selection_picking_pass() const; void _render_background() const; void _render_bed(float theta) const; - void _render_axes() const; void _render_objects() const; void _render_selection() const; #if ENABLE_RENDER_SELECTION_CENTER diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index 7f3bc0893a..577f6e1b50 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -268,7 +268,6 @@ sub SetMatrix } */ -#if ENABLE_TEXTURES_FROM_SVG Shader::Shader() : m_shader(nullptr) { @@ -363,6 +362,5 @@ void Shader::reset() m_shader = nullptr; } } -#endif // ENABLE_TEXTURES_FROM_SVG } // namespace Slic3r diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index 58e2678d03..df2a23f15c 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -37,7 +37,6 @@ public: std::string last_error; }; -#if ENABLE_TEXTURES_FROM_SVG class Shader { GLShader* m_shader; @@ -66,7 +65,6 @@ public: private: void reset(); }; -#endif // ENABLE_TEXTURES_FROM_SVG } diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 171a5c8851..f3421f1508 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -134,7 +134,7 @@ GLTexture::~GLTexture() reset(); } -bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, bool compress) +bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy) { reset(); @@ -142,7 +142,7 @@ bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, bo return false; if (boost::algorithm::iends_with(filename, ".png")) - return load_from_png(filename, use_mipmaps, compress); + return load_from_png(filename, use_mipmaps, compression_type, apply_anisotropy); else return false; } @@ -354,49 +354,10 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, glsafe(::glDisable(GL_BLEND)); } -unsigned int GLTexture::generate_mipmaps(wxImage& image, bool compress) +bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy) { - int w = image.GetWidth(); - int h = image.GetHeight(); - GLint level = 0; - std::vector data(w * h * 4, 0); + bool compression_enabled = (compression_type != None) && GLEW_EXT_texture_compression_s3tc; - while ((w > 1) || (h > 1)) - { - ++level; - - w = std::max(w / 2, 1); - h = std::max(h / 2, 1); - - int n_pixels = w * h; - - image = image.ResampleBicubic(w, h); - - unsigned char* img_rgb = image.GetData(); - unsigned char* img_alpha = image.GetAlpha(); - - data.resize(n_pixels * 4); - for (int i = 0; i < n_pixels; ++i) - { - int data_id = i * 4; - int img_id = i * 3; - data[data_id + 0] = img_rgb[img_id + 0]; - data[data_id + 1] = img_rgb[img_id + 1]; - data[data_id + 2] = img_rgb[img_id + 2]; - data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; - } - - if (compress && GLEW_EXT_texture_compression_s3tc) - glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); - else - glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); - } - - return (unsigned int)level; -} - -bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, bool compress) -{ // Load a PNG with an alpha channel. wxImage image; if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG)) @@ -407,8 +368,32 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, boo m_width = image.GetWidth(); m_height = image.GetHeight(); - int n_pixels = m_width * m_height; + bool requires_rescale = false; + + if (compression_enabled && (compression_type == MultiThreaded)) + { + // the stb_dxt compression library seems to like only texture sizes which are a multiple of 4 + int width_rem = m_width % 4; + int height_rem = m_height % 4; + + if (width_rem != 0) + { + m_width += (4 - width_rem); + requires_rescale = true; + } + + if (height_rem != 0) + { + m_height += (4 - height_rem); + requires_rescale = true; + } + } + + if (requires_rescale) + image = image.ResampleBicubic(m_width, m_height); + + int n_pixels = m_width * m_height; if (n_pixels <= 0) { reset(); @@ -440,28 +425,99 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, boo glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); glsafe(::glGenTextures(1, &m_id)); glsafe(::glBindTexture(GL_TEXTURE_2D, m_id)); - if (compress && GLEW_EXT_texture_compression_s3tc) - glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); + + if (apply_anisotropy) + { + GLfloat max_anisotropy = GLCanvas3DManager::get_gl_info().get_max_anisotropy(); + if (max_anisotropy > 1.0f) + glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy)); + } + + if (compression_enabled) + { + if (compression_type == SingleThreaded) + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); + else + { + // initializes the texture on GPU + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); + // and send the uncompressed data to the compressor + m_compressor.add_level((unsigned int)m_width, (unsigned int)m_height, data); + } + } else glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); + if (use_mipmaps) { // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards - unsigned int levels_count = generate_mipmaps(image, compress); - glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels_count)); - glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)); + int lod_w = m_width; + int lod_h = m_height; + GLint level = 0; + // we do not need to generate all levels down to 1x1 + while ((lod_w > 16) || (lod_h > 16)) + { + ++level; + + lod_w = std::max(lod_w / 2, 1); + lod_h = std::max(lod_h / 2, 1); + n_pixels = lod_w * lod_h; + + image = image.ResampleBicubic(lod_w, lod_h); + + data.resize(n_pixels * 4); + + img_rgb = image.GetData(); + img_alpha = image.GetAlpha(); + + for (int i = 0; i < n_pixels; ++i) + { + int data_id = i * 4; + int img_id = i * 3; + data[data_id + 0] = img_rgb[img_id + 0]; + data[data_id + 1] = img_rgb[img_id + 1]; + data[data_id + 2] = img_rgb[img_id + 2]; + data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; + } + + if (compression_enabled) + { + if (compression_type == SingleThreaded) + glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); + else + { + // initializes the texture on GPU + glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); + // and send the uncompressed data to the compressor + m_compressor.add_level((unsigned int)lod_w, (unsigned int)lod_h, data); + } + } + else + glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); + } + + if (!compression_enabled) + { + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)); + } } else { glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)); } + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); m_source = filename; + if (compression_enabled && (compression_type == MultiThreaded)) + // start asynchronous compression + m_compressor.start_compressing(); + return true; } @@ -472,7 +528,6 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f); if (image == nullptr) { -// printf("Could not open SVG image.\n"); reset(); return false; } @@ -507,7 +562,6 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo NSVGrasterizer* rast = nsvgCreateRasterizer(); if (rast == nullptr) { -// printf("Could not init rasterizer.\n"); nsvgDelete(image); reset(); return false; @@ -579,6 +633,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)); } + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); diff --git a/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp index f4dc05a168..7fc5b8fcf6 100644 --- a/src/slic3r/GUI/GLTexture.hpp +++ b/src/slic3r/GUI/GLTexture.hpp @@ -48,6 +48,13 @@ namespace GUI { }; public: + enum ECompressionType : unsigned char + { + None, + SingleThreaded, + MultiThreaded + }; + struct UV { float u; @@ -75,7 +82,7 @@ namespace GUI { GLTexture(); virtual ~GLTexture(); - bool load_from_file(const std::string& filename, bool use_mipmaps, bool compress); + bool load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy); bool load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px); // meanings of states: (std::pair) // first field (int): @@ -101,11 +108,8 @@ namespace GUI { static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top); static void render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const Quad_UVs& uvs); - protected: - unsigned int generate_mipmaps(wxImage& image, bool compress); - private: - bool load_from_png(const std::string& filename, bool use_mipmaps, bool compress); + bool load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy); bool load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px); friend class Compressor; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index ca52484177..63387bf2db 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -174,7 +174,7 @@ bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture) bool res = false; if (!background_texture.filename.empty()) - res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, true); + res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, GLTexture::SingleThreaded, false); if (res) m_background_texture.metadata = background_texture; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 1a864917dc..8a785760c6 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -67,9 +67,12 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension) /* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA", /* FT_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF", - /* FT_INI */ "INI files (*.ini)|*.ini;*.INI", - /* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG", - /* FT_PNGZIP */"Masked SLA files (*.sl1)|*.sl1;*.SL1", + /* FT_INI */ "INI files (*.ini)|*.ini;*.INI", + /* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG", + + /* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG", + + /* FT_PNGZIP */ "Masked SLA files (*.sl1)|*.sl1;*.SL1", }; std::string out = defaults[file_type]; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 5c897a7010..b74762586b 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -44,6 +44,9 @@ enum FileType FT_INI, FT_SVG, + + FT_TEX, + FT_PNGZIP, FT_SIZE, diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index d5a3a93c45..38af634646 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -43,7 +43,7 @@ bool GLGizmosManager::init() if (!m_background_texture.metadata.filename.empty()) { - if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false, true)) + if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false, GLTexture::SingleThreaded, false)) { reset(); return false; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 387fd34d23..38ad580374 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1853,7 +1853,7 @@ struct Plater::priv // 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_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model); bool can_delete() const; bool can_delete_all() const; @@ -1914,7 +1914,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) : q(q) , main_frame(main_frame) , config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({ - "bed_shape", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance", + "bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance", "brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host", "printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", @@ -2017,11 +2017,21 @@ 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(); }); - view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option("bed_shape")->values); }); + view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) + { + set_bed_shape(config->option("bed_shape")->values, + config->option("bed_custom_texture")->value, + config->option("bed_custom_model")->value); + }); // Preview events: preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); - preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option("bed_shape")->values); }); + preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) + { + set_bed_shape(config->option("bed_shape")->values, + config->option("bed_custom_texture")->value, + config->option("bed_custom_model")->value); + }); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); }); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); }); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_double_slider(evt); }); @@ -3625,9 +3635,9 @@ bool Plater::priv::can_mirror() const return get_selection().is_from_single_instance(); } -void Plater::priv::set_bed_shape(const Pointfs& shape) +void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model) { - bool new_shape = bed.set_shape(shape); + bool new_shape = bed.set_shape(shape, custom_texture, custom_model); if (new_shape) { if (view3D) view3D->bed_shape_changed(); @@ -4559,7 +4569,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) p->config->set_key_value(opt_key, config.option(opt_key)->clone()); if (opt_key == "printer_technology") this->set_printer_technology(config.opt_enum(opt_key)); - else if (opt_key == "bed_shape") { + else if ((opt_key == "bed_shape") || (opt_key == "bed_custom_texture") || (opt_key == "bed_custom_model")) { bed_shape_changed = true; update_scheduled = true; } @@ -4593,7 +4603,9 @@ void Plater::on_config_change(const DynamicPrintConfig &config) } if (bed_shape_changed) - p->set_bed_shape(p->config->option("bed_shape")->values); + p->set_bed_shape(p->config->option("bed_shape")->values, + p->config->option("bed_custom_texture")->value, + p->config->option("bed_custom_model")->value); if (update_scheduled) update(); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 3d467d7f8f..e34c9be285 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -411,7 +411,7 @@ const std::vector& Preset::printer_options() if (s_opts.empty()) { s_opts = { "printer_technology", - "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", + "bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", "host_type", "print_host", "printhost_apikey", "printhost_cafile", "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", @@ -512,6 +512,7 @@ const std::vector& Preset::sla_printer_options() if (s_opts.empty()) { s_opts = { "printer_technology", + "bed_shape", "bed_custom_texture", "bed_custom_model", "max_print_height", "bed_shape", "max_print_height", "display_width", "display_height", "display_pixels_x", "display_pixels_y", "display_mirror_x", "display_mirror_y", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 26bd1116b7..a00f7902f9 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1862,12 +1862,18 @@ void TabPrinter::build_fff() btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { BedShapeDialog dlg(this); - dlg.build_dialog(m_config->option("bed_shape")); + dlg.build_dialog(*m_config->option("bed_shape"), + *m_config->option("bed_custom_texture"), + *m_config->option("bed_custom_model")); if (dlg.ShowModal() == wxID_OK) { - std::vector shape = dlg.GetValue(); + const std::vector& shape = dlg.get_shape(); + const std::string& custom_texture = dlg.get_custom_texture(); + const std::string& custom_model = dlg.get_custom_model(); if (!shape.empty()) { load_key_value("bed_shape", shape); + load_key_value("bed_custom_texture", custom_texture); + load_key_value("bed_custom_model", custom_model); update_changed_ui(); } } @@ -2095,12 +2101,18 @@ void TabPrinter::build_sla() btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) { BedShapeDialog dlg(this); - dlg.build_dialog(m_config->option("bed_shape")); + dlg.build_dialog(*m_config->option("bed_shape"), + *m_config->option("bed_custom_texture"), + *m_config->option("bed_custom_model")); if (dlg.ShowModal() == wxID_OK) { - std::vector shape = dlg.GetValue(); + const std::vector& shape = dlg.get_shape(); + const std::string& custom_texture = dlg.get_custom_texture(); + const std::string& custom_model = dlg.get_custom_model(); if (!shape.empty()) { load_key_value("bed_shape", shape); + load_key_value("bed_custom_texture", custom_texture); + load_key_value("bed_custom_model", custom_model); update_changed_ui(); } }