From 1a91add2e60f3a4d22cdd6c8a10e57dc8095e0a2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 14 Jun 2019 10:38:09 +0200 Subject: [PATCH] Tighter camera frustrum to reduce z-fighting --- src/slic3r/GUI/3DBed.cpp | 26 +++++++- src/slic3r/GUI/3DBed.hpp | 8 ++- src/slic3r/GUI/Camera.cpp | 55 ++++++++++++++-- src/slic3r/GUI/Camera.hpp | 9 +++ src/slic3r/GUI/GLCanvas3D.cpp | 76 ++++++++++------------- src/slic3r/GUI/GLCanvas3D.hpp | 3 +- src/slic3r/GUI/GLTexture.hpp | 1 + src/slic3r/GUI/GLToolbar.cpp | 7 --- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 9 --- 9 files changed, 123 insertions(+), 71 deletions(-) diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 8392e534a..c82b34f49 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -273,6 +273,7 @@ void Bed3D::Axes::render_axis(double length) const Bed3D::Bed3D() : m_type(Custom) + , m_extended_bounding_box_dirty(true) #if ENABLE_TEXTURES_FROM_SVG , m_vbo_id(0) #endif // ENABLE_TEXTURES_FROM_SVG @@ -290,7 +291,7 @@ bool Bed3D::set_shape(const Pointfs& shape) m_shape = shape; m_type = new_type; - calc_bounding_box(); + calc_bounding_boxes(); ExPolygon poly; for (const Vec2d& p : m_shape) @@ -311,7 +312,9 @@ bool Bed3D::set_shape(const Pointfs& shape) // Set the origin and size for painting of the coordinate system axes. m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); - m_axes.length = 0.1 * get_bounding_box().max_size() * Vec3d::Ones(); + m_axes.length = 0.1 * m_bounding_box.max_size() * Vec3d::Ones(); + + m_extended_bounding_box_dirty = true; // Let the calee to update the UI. return true; @@ -400,13 +403,27 @@ void Bed3D::render_axes() const m_axes.render(); } -void Bed3D::calc_bounding_box() +void Bed3D::calc_bounding_boxes() const { m_bounding_box = BoundingBoxf3(); for (const Vec2d& p : m_shape) { m_bounding_box.merge(Vec3d(p(0), p(1), 0.0)); } + + m_extended_bounding_box = m_bounding_box; + + if (m_extended_bounding_box_dirty) + { + // extend to contain Z axis + m_extended_bounding_box.merge(0.1 * m_bounding_box.max_size() * Vec3d::UnitZ()); + + if (!m_model.get_filename().empty()) + // extend to contain model + m_extended_bounding_box.merge(m_model.get_bounding_box()); + + m_extended_bounding_box_dirty = false; + } } void Bed3D::calc_triangles(const ExPolygon& poly) @@ -539,6 +556,9 @@ void Bed3D::render_prusa(const std::string &key, bool bottom) const offset += Vec3d(0.0, 0.0, -0.03); m_model.center_around(offset); + + // update extended bounding box + calc_bounding_boxes(); } if (!m_model.get_filename().empty()) diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp index e60cdf94e..79e96fdf0 100644 --- a/src/slic3r/GUI/3DBed.hpp +++ b/src/slic3r/GUI/3DBed.hpp @@ -85,7 +85,9 @@ public: private: EType m_type; Pointfs m_shape; - BoundingBoxf3 m_bounding_box; + mutable BoundingBoxf3 m_bounding_box; + mutable BoundingBoxf3 m_extended_bounding_box; + mutable bool m_extended_bounding_box_dirty; Polygon m_polygon; GeometryBuffer m_triangles; GeometryBuffer m_gridlines; @@ -117,7 +119,7 @@ public: // Return true if the bed shape changed, so the calee will update the UI. bool set_shape(const Pointfs& shape); - const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; } + 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; @@ -125,7 +127,7 @@ public: void render_axes() const; private: - void calc_bounding_box(); + 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; diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 5aa881e3e..1fc2f6be3 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -23,6 +23,8 @@ namespace Slic3r { namespace GUI { const float Camera::DefaultDistance = 1000.0f; +double Camera::FrustrumMinZSize = 50.0; +double Camera::FrustrumZMargin = 10.0; Camera::Camera() : type(Ortho) @@ -127,6 +129,8 @@ void Camera::apply_view_matrix() const void Camera::apply_projection(const BoundingBoxf3& box) const { + m_frustrum_zs = calc_tight_frustrum_zs_around(box); + switch (type) { case Ortho: @@ -141,10 +145,7 @@ void Camera::apply_projection(const BoundingBoxf3& box) const h2 *= inv_two_zoom; } - // FIXME: calculate a tighter value for depth will improve z-fighting - // Set at least some minimum depth in case the bounding box is empty to avoid an OpenGL driver error. - double depth = std::max(1.0, 5.0 * box.max_size()); - apply_ortho_projection(-w2, w2, -h2, h2, (double)distance - depth, (double)distance + depth); + apply_ortho_projection(-w2, w2, -h2, h2, m_frustrum_zs.first, m_frustrum_zs.second); break; } // case Perspective: @@ -166,6 +167,8 @@ void Camera::debug_render() const Vec3f forward = get_dir_forward().cast(); Vec3f right = get_dir_right().cast(); Vec3f up = get_dir_up().cast(); + float nearZ = (float)m_frustrum_zs.first; + float farZ = (float)m_frustrum_zs.second; ImGui::InputText("Type", const_cast(type.data()), type.length(), ImGuiInputTextFlags_ReadOnly); ImGui::Separator(); @@ -175,6 +178,9 @@ void Camera::debug_render() const ImGui::InputFloat3("Forward", forward.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Right", right.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Up", up.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); + ImGui::Separator(); + ImGui::InputFloat("Near Z", &nearZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); + ImGui::InputFloat("Far Z", &farZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); imgui.end(); } #endif // ENABLE_CAMERA_STATISTICS @@ -190,6 +196,47 @@ void Camera::apply_ortho_projection(double x_min, double x_max, double y_min, do glsafe(::glMatrixMode(GL_MODELVIEW)); } +std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const +{ + std::pair ret = std::make_pair(DBL_MAX, -DBL_MAX); + + Vec3d bb_min = box.min; + Vec3d bb_max = box.max; + + // bbox vertices in world space + std::vector vertices; + vertices.reserve(8); + vertices.push_back(bb_min); + vertices.emplace_back(bb_max(0), bb_min(1), bb_min(2)); + vertices.emplace_back(bb_max(0), bb_max(1), bb_min(2)); + vertices.emplace_back(bb_min(0), bb_max(1), bb_min(2)); + vertices.emplace_back(bb_min(0), bb_min(1), bb_max(2)); + vertices.emplace_back(bb_max(0), bb_min(1), bb_max(2)); + vertices.push_back(bb_max); + vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2)); + + // set the Z range in eye coordinates (only negative Zs are in front of the camera) + for (const Vec3d& v : vertices) + { + // ensure non-negative values + double z = std::max(-(m_view_matrix * v)(2), 0.0); + ret.first = std::min(ret.first, z); + ret.second = std::max(ret.second, z); + } + + // apply margin + ret.first -= FrustrumZMargin; + ret.second += FrustrumZMargin; + + // ensure min size + if (ret.second - ret.first < FrustrumMinZSize) + ret.second = ret.first + FrustrumMinZSize; + + assert(ret.first > 0.0); + + return ret; +} + } // GUI } // Slic3r diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index 83dbb0f6d..18dec6083 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -10,6 +10,8 @@ namespace GUI { struct Camera { static const float DefaultDistance; + static double FrustrumMinZSize; + static double FrustrumZMargin; enum EType : unsigned char { @@ -34,6 +36,7 @@ private: mutable std::array m_viewport; mutable Transform3d m_view_matrix; mutable Transform3d m_projection_matrix; + mutable std::pair m_frustrum_zs; BoundingBoxf3 m_scene_box; @@ -63,6 +66,9 @@ public: Vec3d get_position() const { return m_view_matrix.matrix().inverse().block(0, 3, 3, 1); } + double get_near_z() const { return m_frustrum_zs.first; } + double get_far_z() const { return m_frustrum_zs.second; } + void apply_viewport(int x, int y, unsigned int w, unsigned int h) const; void apply_view_matrix() const; void apply_projection(const BoundingBoxf3& box) const; @@ -73,6 +79,9 @@ public: private: void apply_ortho_projection(double x_min, double x_max, double y_min, double y_max, double z_min, double z_max) const; + // returns tight values for nearZ and farZ plane around the given bounding box + // the camera MUST be outside of the bounding box in eye coordinate of the given box + std::pair calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const; }; } // GUI diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6648d744b..6980e5267 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -304,22 +304,10 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const const Rect& bar_rect = get_bar_rect_viewport(canvas); const Rect& reset_rect = get_reset_rect_viewport(canvas); - glsafe(::glDisable(GL_DEPTH_TEST)); - - // The viewport and camera are set to complete view and glOrtho(-$x / 2, $x / 2, -$y / 2, $y / 2, -$depth, $depth), - // where x, y is the window size divided by $self->_zoom. - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - _render_tooltip_texture(canvas, bar_rect, reset_rect); _render_reset_texture(reset_rect); _render_active_object_annotations(canvas, bar_rect); _render_profile(bar_rect); - - // Revert the matrices. - glsafe(::glPopMatrix()); - - glsafe(::glEnable(GL_DEPTH_TEST)); } float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) @@ -880,10 +868,6 @@ void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0)) { - glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - const Size& cnv_size = canvas.get_canvas_size(); float zoom = canvas.get_camera().zoom; float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; @@ -904,9 +888,6 @@ void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const uvs.right_top = { uv_right, uv_top }; GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs); - - glsafe(::glPopMatrix()); - glsafe(::glEnable(GL_DEPTH_TEST)); } } @@ -1160,10 +1141,6 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const { if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0)) { - glsafe(::glDisable(GL_DEPTH_TEST)); - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - const Size& cnv_size = canvas.get_canvas_size(); float zoom = canvas.get_camera().zoom; float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; @@ -1184,9 +1161,6 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const uvs.right_top = { uv_right, uv_top }; GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs); - - glsafe(::glPopMatrix()); - glsafe(::glEnable(GL_DEPTH_TEST)); } } @@ -1466,7 +1440,7 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); - bb.merge(m_bed.get_bounding_box()); + bb.merge(m_bed.get_bounding_box(false)); if (m_config != nullptr) { @@ -1550,7 +1524,7 @@ void GLCanvas3D::allow_multisample(bool allow) void GLCanvas3D::zoom_to_bed() { - _zoom_to_bounding_box(m_bed.get_bounding_box()); + _zoom_to_bounding_box(m_bed.get_bounding_box(false)); } void GLCanvas3D::zoom_to_volumes() @@ -1624,7 +1598,7 @@ void GLCanvas3D::render() } m_camera.apply_view_matrix(); - m_camera.apply_projection(_max_bounding_box()); + m_camera.apply_projection(_max_bounding_box(true)); GLfloat position_cam[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; glsafe(::glLightfv(GL_LIGHT1, GL_POSITION, position_cam)); @@ -1686,16 +1660,7 @@ void GLCanvas3D::render() m_rectangle_selection.render(*this); // draw overlays - _render_gizmos_overlay(); - _render_warning_texture(); - _render_legend_texture(); -#if !ENABLE_SVG_ICONS - _resize_toolbars(); -#endif // !ENABLE_SVG_ICONS - _render_toolbar(); - _render_view_toolbar(); - if ((m_layers_editing.last_object_id >= 0) && (m_layers_editing.object_max_z() > 0.0f)) - m_layers_editing.render_overlay(*this); + _render_overlays(); #if ENABLE_RENDER_STATISTICS ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -3285,7 +3250,7 @@ void GLCanvas3D::set_camera_zoom(float zoom) zoom = m_camera.zoom / (1.0f - zoom); // Don't allow to zoom too far outside the scene. - float zoom_min = _get_zoom_to_bounding_box_factor(_max_bounding_box()); + float zoom_min = _get_zoom_to_bounding_box_factor(_max_bounding_box(false)); if (zoom_min > 0.0f) zoom = std::max(zoom, zoom_min * 0.7f); @@ -3375,7 +3340,7 @@ Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos) double GLCanvas3D::get_size_proportional_to_max_bed_size(double factor) const { - return factor * m_bed.get_bounding_box().max_size(); + return factor * m_bed.get_bounding_box(false).max_size(); } void GLCanvas3D::set_cursor(ECursorType type) @@ -3613,10 +3578,10 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h) m_dirty = false; } -BoundingBoxf3 GLCanvas3D::_max_bounding_box() const +BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_bed_model) const { BoundingBoxf3 bb = volumes_bounding_box(); - bb.merge(m_bed.get_bounding_box()); + bb.merge(m_bed.get_bounding_box(include_bed_model)); return bb; } @@ -3907,7 +3872,7 @@ void GLCanvas3D::_render_objects() const if (m_config != nullptr) { - const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(); + const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(false); m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); m_volumes.check_outside_state(m_config, nullptr); } @@ -3989,6 +3954,29 @@ void GLCanvas3D::_render_selection_center() const } #endif // ENABLE_RENDER_SELECTION_CENTER +void GLCanvas3D::_render_overlays() const +{ + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glPushMatrix()); + glsafe(::glLoadIdentity()); + // ensure the textures are renderered inside the frustrum + glsafe(::glTranslated(0.0, 0.0, -(m_camera.get_near_z() + 0.5))); + + _render_gizmos_overlay(); + _render_warning_texture(); + _render_legend_texture(); +#if !ENABLE_SVG_ICONS + _resize_toolbars(); +#endif // !ENABLE_SVG_ICONS + _render_toolbar(); + _render_view_toolbar(); + + if ((m_layers_editing.last_object_id >= 0) && (m_layers_editing.object_max_z() > 0.0f)) + m_layers_editing.render_overlay(*this); + + glsafe(::glPopMatrix()); +} + void GLCanvas3D::_render_warning_texture() const { m_warning_texture.render(*this); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 51a36cf69..a0174dd7f 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -635,7 +635,7 @@ private: bool _set_current(); void _resize(unsigned int w, unsigned int h); - BoundingBoxf3 _max_bounding_box() const; + BoundingBoxf3 _max_bounding_box(bool include_bed_model) const; void _zoom_to_bounding_box(const BoundingBoxf3& bbox); float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const; @@ -652,6 +652,7 @@ private: #if ENABLE_RENDER_SELECTION_CENTER void _render_selection_center() const; #endif // ENABLE_RENDER_SELECTION_CENTER + void _render_overlays() const; void _render_warning_texture() const; void _render_legend_texture() const; void _render_volumes_for_picking() const; diff --git a/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp index e00b3a3be..0c5c0efbe 100644 --- a/src/slic3r/GUI/GLTexture.hpp +++ b/src/slic3r/GUI/GLTexture.hpp @@ -62,6 +62,7 @@ namespace GUI { protected: unsigned int generate_mipmaps(wxImage& image); + private: bool load_from_png(const std::string& filename, bool use_mipmaps); bool load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px); diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 00cbdfec7..e73533d37 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -390,19 +390,12 @@ void GLToolbar::render(const GLCanvas3D& parent) const generate_icons_texture(); #endif // ENABLE_SVG_ICONS - glsafe(::glDisable(GL_DEPTH_TEST)); - - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - switch (m_layout.type) { default: case Layout::Horizontal: { render_horizontal(parent); break; } case Layout::Vertical: { render_vertical(parent); break; } } - - glsafe(::glPopMatrix()); } bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 1006d2bd1..32809c01d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -531,18 +531,9 @@ void GLGizmosManager::render_overlay(const GLCanvas3D& canvas, const Selection& generate_icons_texture(); #endif // ENABLE_SVG_ICONS - glsafe(::glDisable(GL_DEPTH_TEST)); - - glsafe(::glPushMatrix()); - glsafe(::glLoadIdentity()); - do_render_overlay(canvas, selection); - - glsafe(::glPopMatrix()); } - - bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt, GLCanvas3D& canvas) { bool processed = false;