diff --git a/CMakeLists.txt b/CMakeLists.txt index fd8a27e86..c4e599f36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,6 +169,11 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STRE endif () endif() +if (APPLE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=partial-availability -Werror=unguarded-availability -Werror=unguarded-availability-new") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=partial-availability -Werror=unguarded-availability -Werror=unguarded-availability-new") +endif () + # Where all the bundled libraries reside? set(LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/src) set(LIBDIR_BIN ${CMAKE_CURRENT_BINARY_DIR}/src) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 783c65293..9d65a479f 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.6) include(PrecompiledHeader) -add_library(libslic3r_gui STATIC +set(SLIC3R_GUI_SOURCES pchheader.cpp pchheader.hpp GUI/AboutDialog.cpp @@ -127,6 +127,12 @@ add_library(libslic3r_gui STATIC Utils/HexFile.hpp ) +if (APPLE) + list(APPEND SLIC3R_GUI_SOURCES Utils/RetinaHelperImpl.mm) +endif () + +add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) + target_link_libraries(libslic3r_gui libslic3r avrdude imgui) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp index 28e6e1018..a537870ee 100644 --- a/src/slic3r/GUI/AppConfig.cpp +++ b/src/slic3r/GUI/AppConfig.cpp @@ -59,6 +59,11 @@ void AppConfig::set_defaults() if (get("use_legacy_opengl").empty()) set("use_legacy_opengl", "0"); +#if __APPLE__ + if (get("use_retina_opengl").empty()) + set("use_retina_opengl", "1"); +#endif + if (get("remember_output_path").empty()) set("remember_output_path", "1"); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a4f3f0f04..4c71ff3d8 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -9,6 +9,7 @@ #include "libslic3r/GCode/PreviewData.hpp" #include "libslic3r/Geometry.hpp" #include "libslic3r/Utils.hpp" +#include "libslic3r/Technologies.hpp" #include "slic3r/GUI/3DScene.hpp" #include "slic3r/GUI/BackgroundSlicingProcess.hpp" #include "slic3r/GUI/GLShader.hpp" @@ -20,6 +21,10 @@ #include "GUI_ObjectManipulation.hpp" #include "I18N.hpp" +#if ENABLE_RETINA_GL +#include "slic3r/Utils/RetinaHelper.hpp" +#endif + #include #include @@ -45,6 +50,7 @@ #include #include #include +#include static const float TRACKBALLSIZE = 0.8f; static const float GIMBALL_LOCK_THETA_MAX = 180.0f; @@ -59,8 +65,6 @@ static const float VIEW_BOTTOM[2] = { 0.0f, 180.0f }; static const float VIEW_FRONT[2] = { 0.0f, 90.0f }; static const float VIEW_REAR[2] = { 180.0f, 90.0f }; -static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f; -static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f; static const float GIZMO_RESET_BUTTON_HEIGHT = 22.0f; static const float GIZMO_RESET_BUTTON_WIDTH = 70.f; @@ -194,9 +198,10 @@ Size::Size() { } -Size::Size(int width, int height) +Size::Size(int width, int height, float scale_factor) : m_width(width) , m_height(height) + , m_scale_factor(scale_factor) { } @@ -220,6 +225,16 @@ void Size::set_height(int height) m_height = height; } +int Size::get_scale_factor() const +{ + return m_scale_factor; +} + +void Size::set_scale_factor(int scale_factor) +{ + m_scale_factor = scale_factor; +} + Rect::Rect() : m_left(0.0f) , m_top(0.0f) @@ -869,6 +884,9 @@ GLCanvas3D::LayersEditing::~LayersEditing() delete m_slicing_parameters; } +const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f; +const float GLCanvas3D::LayersEditing::THICKNESS_RESET_BUTTON_HEIGHT = 22.0f; + bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) { if (!m_shader.init(vertex_shader_filename, fragment_shader_filename)) @@ -989,7 +1007,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas) float w = (float)cnv_size.get_width(); float h = (float)cnv_size.get_height(); - return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0.0f, w, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT); + return Rect(w - thickness_bar_width(canvas), 0.0f, w, h - reset_button_height(canvas)); } Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas) @@ -998,7 +1016,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas) float w = (float)cnv_size.get_width(); float h = (float)cnv_size.get_height(); - return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, w, h); + return Rect(w - thickness_bar_width(canvas), h - reset_button_height(canvas), w, h); } Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) @@ -1010,7 +1028,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas) float zoom = canvas.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom); + return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom); } Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas) @@ -1022,7 +1040,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas float zoom = canvas.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); + return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); } @@ -1033,6 +1051,8 @@ bool GLCanvas3D::LayersEditing::_is_initialized() const void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const { + // TODO: do this with ImGui + if (m_tooltip_texture.get_id() == 0) { std::string filename = resources_dir() + "/icons/variable_layer_height_tooltip.png"; @@ -1040,6 +1060,15 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas return; } +#if ENABLE_RETINA_GL + const float scale = canvas.get_canvas_size().get_scale_factor(); + const float width = (float)m_tooltip_texture.get_width() * scale; + const float height = (float)m_tooltip_texture.get_height() * scale; +#else + const float width = (float)m_tooltip_texture.get_width(); + const float height = (float)m_tooltip_texture.get_height(); +#endif + float zoom = canvas.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float gap = 10.0f * inv_zoom; @@ -1047,9 +1076,9 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas float bar_left = bar_rect.get_left(); float reset_bottom = reset_rect.get_bottom(); - float l = bar_left - (float)m_tooltip_texture.get_width() * inv_zoom - gap; + float l = bar_left - width * inv_zoom - gap; float r = bar_left - gap; - float t = reset_bottom + (float)m_tooltip_texture.get_height() * inv_zoom + gap; + float t = reset_bottom + height * inv_zoom + gap; float b = reset_bottom + gap; GLTexture::render_texture(m_tooltip_texture.get_id(), l, r, b, t); @@ -1078,11 +1107,6 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas // The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX); - GLsizei w = (GLsizei)m_layers_texture.width; - GLsizei h = (GLsizei)m_layers_texture.height; - GLsizei half_w = w / 2; - GLsizei half_h = h / 2; - ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); ::glBindTexture(GL_TEXTURE_2D, m_z_texture_id); @@ -1263,6 +1287,25 @@ void GLCanvas3D::LayersEditing::update_slicing_parameters() } } +float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D &canvas) +{ +#if ENABLE_RETINA_GL + return canvas.get_canvas_size().get_scale_factor() * THICKNESS_BAR_WIDTH; +#else + return THICKNESS_BAR_WIDTH; +#endif +} + +float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas) +{ +#if ENABLE_RETINA_GL + return canvas.get_canvas_size().get_scale_factor() * THICKNESS_RESET_BUTTON_HEIGHT; +#else + return THICKNESS_RESET_BUTTON_HEIGHT; +#endif +} + + const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX); const Vec3d GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX); #if ENABLE_MOVE_MIN_THRESHOLD @@ -2816,14 +2859,11 @@ void GLCanvas3D::Selection::_ensure_on_bed() } #endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING -const float GLCanvas3D::Gizmos::OverlayIconsScale = 1.0f; -const float GLCanvas3D::Gizmos::OverlayBorder = 5.0f; -const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayIconsScale; - GLCanvas3D::Gizmos::Gizmos() : m_enabled(false) , m_current(Undefined) { + set_overlay_scale(1.0); } GLCanvas3D::Gizmos::~Gizmos() @@ -2927,6 +2967,13 @@ void GLCanvas3D::Gizmos::set_enabled(bool enable) m_enabled = enable; } +void GLCanvas3D::Gizmos::set_overlay_scale(float scale) +{ + m_overlay_icons_scale = scale; + m_overlay_border = 5.0f * scale; + m_overlay_gap_y = 5.0f * scale; +} + std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const GLCanvas3D::Selection& selection) { std::string name = ""; @@ -2936,22 +2983,22 @@ std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, con float cnv_h = (float)canvas.get_canvas_size().get_height(); float height = _get_total_overlay_height(); - float top_y = 0.5f * (cnv_h - height) + OverlayBorder; + float top_y = 0.5f * (cnv_h - height) + m_overlay_border; for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale; + float icon_size = (float)it->second->get_textures_size() * m_overlay_icons_scale; - bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size); + bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size); if (inside) name = it->second->get_name(); if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); - top_y += (icon_size + OverlayGapY); + top_y += (icon_size + m_overlay_gap_y); } return name; @@ -2964,15 +3011,15 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec float cnv_h = (float)canvas.get_canvas_size().get_height(); float height = _get_total_overlay_height(); - float top_y = 0.5f * (cnv_h - height) + OverlayBorder; + float top_y = 0.5f * (cnv_h - height) + m_overlay_border; for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale; + float icon_size = (float)it->second->get_textures_size() * m_overlay_icons_scale; - bool inside = (OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size); + bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size); if (it->second->is_activable(selection) && inside) { if ((it->second->get_state() == GLGizmoBase::On)) @@ -2989,7 +3036,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec else it->second->set_state(GLGizmoBase::Off); - top_y += (icon_size + OverlayGapY); + top_y += (icon_size + m_overlay_gap_y); } GizmosMap::iterator it = m_gizmos.find(m_current); @@ -3061,18 +3108,18 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const float cnv_h = (float)canvas.get_canvas_size().get_height(); float height = _get_total_overlay_height(); - float top_y = 0.5f * (cnv_h - height) + OverlayBorder; + float top_y = 0.5f * (cnv_h - height) + m_overlay_border; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale; + float icon_size = (float)it->second->get_textures_size() * m_overlay_icons_scale; - if ((OverlayBorder <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= OverlayBorder + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size)) + if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + icon_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + icon_size)) return true; - top_y += (icon_size + OverlayGapY); + top_y += (icon_size + m_overlay_gap_y); } return false; @@ -3345,7 +3392,7 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float height = _get_total_overlay_height(); - float scaled_border = OverlayBorder * inv_zoom; + float scaled_border = m_overlay_border * inv_zoom; float top_x = (-0.5f * cnv_w) * inv_zoom; float top_y = (0.5f * height) * inv_zoom; @@ -3390,7 +3437,7 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva bg_uv_left = bg_uv_i_left; bg_i_left = bg_left; - if ((OverlayBorder > 0) && (bg_uv_top != bg_uv_i_top)) + if ((m_overlay_border > 0) && (bg_uv_top != bg_uv_i_top)) { if (bg_uv_left != bg_uv_i_left) GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_top, bg_top, { { bg_uv_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_i_left, bg_uv_top }, { bg_uv_left, bg_uv_top } }); @@ -3401,15 +3448,15 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_top, bg_top, { { bg_uv_i_right, bg_uv_i_top }, { bg_uv_right, bg_uv_i_top }, { bg_uv_right, bg_uv_top }, { bg_uv_i_right, bg_uv_top } }); } - if ((OverlayBorder > 0) && (bg_uv_left != bg_uv_i_left)) + if ((m_overlay_border > 0) && (bg_uv_left != bg_uv_i_left)) GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_i_bottom, bg_i_top, { { bg_uv_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_left, bg_uv_i_top }, { bg_uv_left, bg_uv_i_top } }); GLTexture::render_sub_texture(bg_tex_id, bg_i_left, bg_i_right, bg_i_bottom, bg_i_top, { { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_i_right, bg_uv_i_top }, { bg_uv_i_left, bg_uv_i_top } }); - if ((OverlayBorder > 0) && (bg_uv_right != bg_uv_i_right)) + if ((m_overlay_border > 0) && (bg_uv_right != bg_uv_i_right)) GLTexture::render_sub_texture(bg_tex_id, bg_i_right, bg_right, bg_i_bottom, bg_i_top, { { bg_uv_i_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_bottom }, { bg_uv_right, bg_uv_i_top }, { bg_uv_i_right, bg_uv_i_top } }); - if ((OverlayBorder > 0) && (bg_uv_bottom != bg_uv_i_bottom)) + if ((m_overlay_border > 0) && (bg_uv_bottom != bg_uv_i_bottom)) { if (bg_uv_left != bg_uv_i_left) GLTexture::render_sub_texture(bg_tex_id, bg_left, bg_i_left, bg_bottom, bg_i_bottom, { { bg_uv_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_bottom }, { bg_uv_i_left, bg_uv_i_bottom }, { bg_uv_left, bg_uv_i_bottom } }); @@ -3421,19 +3468,19 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva } } - top_x += OverlayBorder * inv_zoom; - top_y -= OverlayBorder * inv_zoom; - float scaled_gap_y = OverlayGapY * inv_zoom; + top_x += m_overlay_border * inv_zoom; + top_y -= m_overlay_border * inv_zoom; + float scaled_gap_y = m_overlay_gap_y * inv_zoom; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; - float icon_size = (float)it->second->get_textures_size() * OverlayIconsScale * inv_zoom; + float icon_size = (float)it->second->get_textures_size() * m_overlay_icons_scale * inv_zoom; GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + icon_size, top_y - icon_size, top_y); #if ENABLE_IMGUI if (it->second->get_state() == GLGizmoBase::On) - it->second->render_input_window(2.0f * OverlayBorder + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, selection); + it->second->render_input_window(2.0f * m_overlay_border + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, selection); #endif // ENABLE_IMGUI top_y -= (icon_size + scaled_gap_y); } @@ -3448,17 +3495,17 @@ void GLCanvas3D::Gizmos::_render_current_gizmo(const GLCanvas3D::Selection& sele float GLCanvas3D::Gizmos::_get_total_overlay_height() const { - float height = 2.0f * OverlayBorder; + float height = 2.0f * m_overlay_border; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; - height += (float)it->second->get_textures_size() * OverlayIconsScale + OverlayGapY; + height += (float)it->second->get_textures_size() * m_overlay_icons_scale + m_overlay_gap_y; } - return height - OverlayGapY; + return height - m_overlay_gap_y; } float GLCanvas3D::Gizmos::_get_total_overlay_width() const @@ -3469,10 +3516,10 @@ float GLCanvas3D::Gizmos::_get_total_overlay_width() const if ((it->second == nullptr) || !it->second->is_selectable()) continue; - max_icon_width = std::max(max_icon_width, (float)it->second->get_textures_size() * OverlayIconsScale); + max_icon_width = std::max(max_icon_width, (float)it->second->get_textures_size() * m_overlay_icons_scale); } - return max_icon_width + 2.0f * OverlayBorder; + return max_icon_width + 2.0f * m_overlay_border; } GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const @@ -3491,7 +3538,7 @@ GLCanvas3D::WarningTexture::WarningTexture() { } -bool GLCanvas3D::WarningTexture::generate(const std::string& msg) +bool GLCanvas3D::WarningTexture::generate(const std::string& msg, const GLCanvas3D& canvas) { reset(); @@ -3500,7 +3547,8 @@ bool GLCanvas3D::WarningTexture::generate(const std::string& msg) wxMemoryDC memDC; // select default font - wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + const float scale = canvas.get_canvas_size().get_scale_factor(); + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Scale(scale); font.MakeLarger(); font.MakeBold(); memDC.SetFont(font); @@ -3648,9 +3696,18 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c wxMemoryDC memDC; wxMemoryDC mask_memDC; + // calculate scaling + const float scale = canvas.get_canvas_size().get_scale_factor(); + const int scaled_square = std::floor((float)Px_Square * scale); + const int scaled_title_offset = Px_Title_Offset * scale; + const int scaled_text_offset = Px_Text_Offset * scale; + const int scaled_square_contour = Px_Square_Contour * scale; + const int scaled_border = Px_Border * scale; + // select default font - memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - mask_memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + const wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Scale(scale); + memDC.SetFont(font); + mask_memDC.SetFont(font); // calculates texture size wxCoord w, h; @@ -3667,10 +3724,10 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c max_text_height = std::max(max_text_height, (int)h); } - m_original_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width); - m_original_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square; + m_original_width = std::max(2 * scaled_border + title_width, 2 * (scaled_border + scaled_square_contour) + scaled_square + scaled_text_offset + max_text_width); + m_original_height = 2 * (scaled_border + scaled_square_contour) + title_height + scaled_title_offset + items_count * scaled_square; if (items_count > 1) - m_original_height += (items_count - 1) * Px_Square_Contour; + m_original_height += (items_count - 1) * scaled_square_contour; int pow_of_two_size = (int)next_highest_power_of_2(std::max(m_original_width, m_original_height)); @@ -3694,8 +3751,8 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c memDC.SetTextForeground(use_error_colors ? *wxWHITE : *wxBLACK); mask_memDC.SetTextForeground(*wxWHITE); - int title_x = Px_Border; - int title_y = Px_Border; + int title_x = scaled_border; + int title_y = scaled_border; memDC.DrawText(title, title_x, title_y); mask_memDC.DrawText(title, title_x, title_y); @@ -3703,12 +3760,12 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c mask_memDC.SetBrush(wxBrush(*wxWHITE)); // draw icons contours as background - int squares_contour_x = Px_Border; - int squares_contour_y = Px_Border + title_height + Px_Title_Offset; - int squares_contour_width = Px_Square + 2 * Px_Square_Contour; - int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour; + int squares_contour_x = scaled_border; + int squares_contour_y = scaled_border + title_height + scaled_title_offset; + int squares_contour_width = scaled_square + 2 * scaled_square_contour; + int squares_contour_height = items_count * scaled_square + 2 * scaled_square_contour; if (items_count > 1) - squares_contour_height += (items_count - 1) * Px_Square_Contour; + squares_contour_height += (items_count - 1) * scaled_square_contour; wxColour color(Squares_Border_Color[0], Squares_Border_Color[1], Squares_Border_Color[2]); wxPen pen(color); @@ -3719,15 +3776,15 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c mask_memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height)); // draw items (colored icon + text) - int icon_x = squares_contour_x + Px_Square_Contour; + int icon_x = squares_contour_x + scaled_square_contour; int icon_x_inner = icon_x + 1; - int icon_y = squares_contour_y + Px_Square_Contour; - int icon_y_step = Px_Square + Px_Square_Contour; + int icon_y = squares_contour_y + scaled_square_contour; + int icon_y_step = scaled_square + scaled_square_contour; - int text_x = icon_x + Px_Square + Px_Text_Offset; - int text_y_offset = (Px_Square - max_text_height) / 2; + int text_x = icon_x + scaled_square + scaled_text_offset; + int text_y_offset = (scaled_square - max_text_height) / 2; - int px_inner_square = Px_Square - 2; + int px_inner_square = scaled_square - 2; for (const GCodePreviewData::LegendItem& item : items) { @@ -3741,7 +3798,7 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c brush.SetColour(color); memDC.SetPen(pen); memDC.SetBrush(brush); - memDC.DrawRectangle(wxRect(icon_x, icon_y, Px_Square, Px_Square)); + memDC.DrawRectangle(wxRect(icon_x, icon_y, scaled_square, scaled_square)); // draw icon interior color.Set(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2], item_color_bytes[3]); @@ -3850,6 +3907,9 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) : m_canvas(canvas) , m_context(nullptr) +#if ENABLE_RETINA_GL + , m_retina_helper(nullptr) +#endif , m_in_render(false) , m_toolbar(GLToolbar::Normal) , m_view_toolbar(nullptr) @@ -3889,6 +3949,15 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) m_context = new wxGLContext(m_canvas); #endif // !ENABLE_USE_UNIQUE_GLCONTEXT m_timer.SetOwner(m_canvas); + +#if ENABLE_RETINA_GL + m_retina_helper.reset(new RetinaHelper(canvas)); + + const bool use_retina = wxGetApp().app_config->get("use_retina_opengl") == "1"; + BOOST_LOG_TRIVIAL(debug) << "GLCanvas3D: Use Retina OpenGL: " << use_retina; + m_retina_helper->set_use_retina(use_retina); + BOOST_LOG_TRIVIAL(debug) << "GLCanvas3D: Scaling factor: " << m_retina_helper->get_scale_factor(); +#endif } m_selection.set_volumes(&m_volumes.volumes); @@ -5063,6 +5132,12 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt) void GLCanvas3D::on_mouse(wxMouseEvent& evt) { +#if ENABLE_RETINA_GL + const float scale = m_retina_helper->get_scale_factor(); + evt.SetX(evt.GetX() * scale); + evt.SetY(evt.GetY() * scale); +#endif + #if ENABLE_IMGUI auto imgui = wxGetApp().imgui(); if (imgui->update_mouse_data(evt)) { @@ -5552,7 +5627,15 @@ Size GLCanvas3D::get_canvas_size() const if (m_canvas != nullptr) m_canvas->GetSize(&w, &h); - return Size(w, h); +#if ENABLE_RETINA_GL + const float factor = m_retina_helper->get_scale_factor(); + w *= factor; + h *= factor; +#else + const float factor = 1.0; +#endif + + return Size(w, h, factor); } Point GLCanvas3D::get_local_mouse_position() const @@ -6019,6 +6102,9 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) #if ENABLE_IMGUI wxGetApp().imgui()->set_display_size((float)w, (float)h); +#if ENABLE_RETINA_GL + wxGetApp().imgui()->set_style_scaling(m_retina_helper->get_scale_factor()); +#endif // ENABLE_RETINA_GL #endif // ENABLE_IMGUI // ensures that this canvas is current @@ -6497,18 +6583,28 @@ void GLCanvas3D::_render_current_gizmo() const void GLCanvas3D::_render_gizmos_overlay() const { +#if ENABLE_RETINA_GL + m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor()); +#endif m_gizmos.render_overlay(*this, m_selection); } void GLCanvas3D::_render_toolbar() const { +#if ENABLE_RETINA_GL + m_toolbar.set_icons_scale(m_retina_helper->get_scale_factor()); +#endif m_toolbar.render(*this); } void GLCanvas3D::_render_view_toolbar() const { - if (m_view_toolbar != nullptr) + if (m_view_toolbar != nullptr) { +#if ENABLE_RETINA_GL + m_view_toolbar->set_icons_scale(m_retina_helper->get_scale_factor()); +#endif m_view_toolbar->render(*this); + } } #if ENABLE_SHOW_CAMERA_TARGET @@ -8224,7 +8320,7 @@ void GLCanvas3D::_generate_warning_texture(const std::string& msg) return; #endif // !ENABLE_USE_UNIQUE_GLCONTEXT - m_warning_texture.generate(msg); + m_warning_texture.generate(msg, *this); } void GLCanvas3D::_reset_warning_texture() @@ -8254,6 +8350,10 @@ void GLCanvas3D::_resize_toolbars() const float zoom = get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_RETINA_GL + m_toolbar.set_icons_scale(m_retina_helper->get_scale_factor()); +#endif + GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation(); switch (m_toolbar.get_layout_type()) @@ -8297,6 +8397,10 @@ void GLCanvas3D::_resize_toolbars() const if (m_view_toolbar != nullptr) { +#if ENABLE_RETINA_GL + m_view_toolbar->set_icons_scale(m_retina_helper->get_scale_factor()); +#endif + // places the toolbar on the bottom-left corner of the 3d scene float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar->get_height()) * inv_zoom; float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 1ca9c234f..5d9aafd80 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -2,7 +2,9 @@ #define slic3r_GLCanvas3D_hpp_ #include +#include +#include "libslic3r/Technologies.hpp" #include "3DScene.hpp" #include "GLToolbar.hpp" #include "Event.hpp" @@ -20,6 +22,9 @@ class wxTimerEvent; class wxPaintEvent; class wxGLCanvas; +// Support for Retina OpenGL on Mac OS +#define ENABLE_RETINA_GL __APPLE__ + class GLUquadric; typedef class GLUquadric GLUquadricObj; @@ -36,6 +41,10 @@ namespace GUI { class GLGizmoBase; +#if ENABLE_RETINA_GL +class RetinaHelper; +#endif + class GeometryBuffer { std::vector m_vertices; @@ -55,16 +64,20 @@ class Size { int m_width; int m_height; + float m_scale_factor; public: Size(); - Size(int width, int height); + Size(int width, int height, float scale_factor = 1.0); int get_width() const; void set_width(int width); int get_height() const; void set_height(int height); + + int get_scale_factor() const; + void set_scale_factor(int height); }; class Rect @@ -297,6 +310,9 @@ class GLCanvas3D }; private: + static const float THICKNESS_BAR_WIDTH; + static const float THICKNESS_RESET_BUTTON_HEIGHT; + bool m_use_legacy_opengl; bool m_enabled; Shader m_shader; @@ -380,6 +396,9 @@ class GLCanvas3D void _render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const; void _render_profile(const Rect& bar_rect) const; void update_slicing_parameters(); + + static float thickness_bar_width(const GLCanvas3D &canvas); + static float reset_button_height(const GLCanvas3D &canvas); }; struct Mouse @@ -690,10 +709,6 @@ public: private: class Gizmos { - static const float OverlayIconsScale; - static const float OverlayBorder; - static const float OverlayGapY; - public: enum EType : unsigned char { @@ -714,6 +729,10 @@ private: BackgroundTexture m_background_texture; EType m_current; + float m_overlay_icons_scale; + float m_overlay_border; + float m_overlay_gap_y; + public: Gizmos(); ~Gizmos(); @@ -723,6 +742,8 @@ private: bool is_enabled() const; void set_enabled(bool enable); + void set_overlay_scale(float scale); + std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); void update_on_off_state(const Selection& selection); @@ -812,7 +833,7 @@ private: public: WarningTexture(); - bool generate(const std::string& msg); + bool generate(const std::string& msg, const GLCanvas3D& canvas); void render(const GLCanvas3D& canvas) const; }; @@ -842,6 +863,9 @@ private: wxGLCanvas* m_canvas; wxGLContext* m_context; +#if ENABLE_RETINA_GL + std::unique_ptr m_retina_helper; +#endif bool m_in_render; LegendTexture m_legend_texture; WarningTexture m_warning_texture; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 7ce34d4e2..ed2654f51 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include @@ -24,6 +26,7 @@ namespace GUI { ImGuiWrapper::ImGuiWrapper() : m_font_texture(0) + , m_style_scaling(1.0) , m_mouse_buttons(0) , m_disabled(false) { @@ -39,18 +42,9 @@ bool ImGuiWrapper::init() { ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), 18.0f); - if (font == nullptr) { - font = io.Fonts->AddFontDefault(); - if (font == nullptr) - return false; - } - else { - m_fonts.insert(FontsMap::value_type("Noto Sans Regular 18", font)); - } + init_default_font(m_style_scaling); - io.IniFilename = nullptr; + ImGui::GetIO().IniFilename = nullptr; return true; } @@ -62,6 +56,15 @@ void ImGuiWrapper::set_display_size(float w, float h) io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); } +void ImGuiWrapper::set_style_scaling(float scaling) +{ + if (!std::isnan(scaling) && !std::isinf(scaling) && scaling != m_style_scaling) { + ImGui::GetStyle().ScaleAllSizes(scaling / m_style_scaling); + init_default_font(scaling); + m_style_scaling = scaling; + } +} + bool ImGuiWrapper::update_mouse_data(wxMouseEvent& evt) { ImGuiIO& io = ImGui::GetIO(); @@ -198,6 +201,21 @@ bool ImGuiWrapper::want_any_input() const return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput; } +void ImGuiWrapper::init_default_font(float scaling) +{ + static const float font_size = 18.0f; + + ImGuiIO& io = ImGui::GetIO(); + io.Fonts->Clear(); + ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), font_size * scaling); + if (font == nullptr) { + font = io.Fonts->AddFontDefault(); + if (font == nullptr) { + throw std::runtime_error("ImGui: Could not load deafult font"); + } + } +} + void ImGuiWrapper::create_device_objects() { create_fonts_texture(); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 5293bee26..47a1fb937 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -21,6 +21,7 @@ class ImGuiWrapper FontsMap m_fonts; unsigned m_font_texture; + float m_style_scaling; unsigned m_mouse_buttons; bool m_disabled; @@ -32,6 +33,7 @@ public: void read_glsl_version(); void set_display_size(float w, float h); + void set_style_scaling(float scaling); bool update_mouse_data(wxMouseEvent &evt); void new_frame(); @@ -58,6 +60,7 @@ public: bool want_text_input() const; bool want_any_input() const; private: + void init_default_font(float scaling); void create_device_objects(); void create_fonts_texture(); void render_draw_data(ImDrawData *draw_data); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 5f5da8bd4..2aa5ecadd 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -87,6 +87,7 @@ void PreferencesDialog::build() option = Option (def,"show_incompatible_presets"); m_optgroup->append_single_option_line(option); + // TODO: remove? def.label = L("Use legacy OpenGL 1.1 rendering"); def.type = coBool; def.tooltip = L("If you have rendering issues caused by a buggy OpenGL 2.0 driver, " @@ -96,6 +97,16 @@ void PreferencesDialog::build() option = Option (def,"use_legacy_opengl"); m_optgroup->append_single_option_line(option); +#if __APPLE__ + def.label = L("Use Retina resolution for the 3D scene"); + def.type = coBool; + def.tooltip = L("If enabled, the 3D scene will be rendered in Retina resolution. " + "If you are experiencing 3D performance problems, disabling this option may help."); + def.default_value = new ConfigOptionBool{ app_config->get("use_retina_opengl") == "1" }; + option = Option (def, "use_retina_opengl"); + m_optgroup->append_single_option_line(option); +#endif + auto sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(m_optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); @@ -110,8 +121,9 @@ void PreferencesDialog::build() void PreferencesDialog::accept() { - if (m_values.find("no_defaults") != m_values.end()|| - m_values.find("use_legacy_opengl")!= m_values.end()) { + if (m_values.find("no_defaults") != m_values.end() || + m_values.find("use_legacy_opengl") != m_values.end() || + m_values.find("use_retina_opengl") != m_values.end()) { warning_catcher(this, _(L("You need to restart Slic3r to make the changes effective."))); } diff --git a/src/slic3r/Utils/RetinaHelper.hpp b/src/slic3r/Utils/RetinaHelper.hpp new file mode 100644 index 000000000..659bc7f56 --- /dev/null +++ b/src/slic3r/Utils/RetinaHelper.hpp @@ -0,0 +1,29 @@ +#ifndef slic3r_RetinaHelper_hpp_ +#define slic3r_RetinaHelper_hpp_ + +class wxWindow; + + +namespace Slic3r { +namespace GUI { + +class RetinaHelper +{ +public: + RetinaHelper(wxWindow* window); + ~RetinaHelper(); + + void set_use_retina(bool value); + bool get_use_retina(); + float get_scale_factor(); + +private: + wxWindow* m_window; + void* m_self; +}; + + +} // namespace GUI +} // namespace Slic3r + +#endif // RetinaHelper_h diff --git a/src/slic3r/Utils/RetinaHelperImpl.hmm b/src/slic3r/Utils/RetinaHelperImpl.hmm new file mode 100644 index 000000000..0edde2990 --- /dev/null +++ b/src/slic3r/Utils/RetinaHelperImpl.hmm @@ -0,0 +1,15 @@ +#import + +class wxEvtHandler; + +@interface RetinaHelperImpl : NSObject +{ + NSView *view; + wxEvtHandler* handler; +} + +-(id)initWithView:(NSView *)view handler:(wxEvtHandler *)handler; +-(void)setViewWantsBestResolutionOpenGLSurface:(BOOL)value; +-(BOOL)getViewWantsBestResolutionOpenGLSurface; +-(float)getBackingScaleFactor; +@end diff --git a/src/slic3r/Utils/RetinaHelperImpl.mm b/src/slic3r/Utils/RetinaHelperImpl.mm new file mode 100644 index 000000000..de0402d34 --- /dev/null +++ b/src/slic3r/Utils/RetinaHelperImpl.mm @@ -0,0 +1,111 @@ +// The RetinaHelper was originally written by Andreas Stahl, 2013 + +#import "RetinaHelper.hpp" +#import "RetinaHelperImpl.hmm" +#import + +#import "wx/window.h" + +@implementation RetinaHelperImpl + +namespace Slic3r { +namespace GUI { + +RetinaHelper::RetinaHelper(wxWindow* window) : + m_window(window) +{ + m_self = nullptr; + m_self = [[RetinaHelperImpl alloc] initWithView:window->GetHandle() handler:window->GetEventHandler()]; +} + +RetinaHelper::~RetinaHelper() +{ + [m_self release]; +} + +void RetinaHelper::set_use_retina(bool aValue) +{ + [(id)m_self setViewWantsBestResolutionOpenGLSurface:aValue]; +} + +bool RetinaHelper::get_use_retina() +{ + return [(id)m_self getViewWantsBestResolutionOpenGLSurface]; +} + +float RetinaHelper::get_scale_factor() +{ + return [(id)m_self getViewWantsBestResolutionOpenGLSurface] ? [(id)m_self getBackingScaleFactor] : 1.0f; +} + +} // namespace GUI +} // namespace Slic3r + + +-(id)initWithView:(NSView *)aView handler:(wxEvtHandler *)aHandler +{ + self = [super init]; + if (self) { + handler = aHandler; + view = aView; + // register for backing change notifications + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + if (nc) { + [nc addObserver:self selector:@selector(windowDidChangeBackingProperties:) + name:NSWindowDidChangeBackingPropertiesNotification object:nil]; + } + } + return self; +} + +-(void) dealloc +{ + // unregister from all notifications + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + if (nc) { + [nc removeObserver:self]; + } + [super dealloc]; +} + +-(void)setViewWantsBestResolutionOpenGLSurface:(BOOL)value +{ + [view setWantsBestResolutionOpenGLSurface:value]; +} + +-(BOOL)getViewWantsBestResolutionOpenGLSurface +{ + return [view wantsBestResolutionOpenGLSurface]; +} + +-(float)getBackingScaleFactor +{ + return [[view window] backingScaleFactor]; +} + +- (void)windowDidChangeBackingProperties:(NSNotification *)notification +{ + NSWindow *theWindow = (NSWindow *)[notification object]; + + if (theWindow == [view window]) { + CGFloat newBackingScaleFactor = [theWindow backingScaleFactor]; + CGFloat oldBackingScaleFactor = [[[notification userInfo] + objectForKey:@"NSBackingPropertyOldScaleFactorKey"] + doubleValue]; + + if (newBackingScaleFactor != oldBackingScaleFactor) { + // generate a wx resize event and pass it to the handler's queue + wxSizeEvent *event = new wxSizeEvent(); + // use the following line if this resize event should have the physical pixel resolution + // but that is not recommended, because ordinary resize events won't do so either + // which would necessitate a case-by-case switch in the resize handler method. + // NSRect nsrect = [view convertRectToBacking:[view bounds]]; + NSRect nsrect = [view bounds]; + wxRect rect = wxRect(nsrect.origin.x, nsrect.origin.y, nsrect.size.width, nsrect.size.height); + event->SetRect(rect); + event->SetSize(rect.GetSize()); + handler->QueueEvent(event); + } + } +} +@end