From 8c6304688de8575fa3634868b00cca59e1b2c8bd Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 16 May 2019 15:54:11 +0200 Subject: Camera refactoring: Frustrum calculations moved into Camera class --- src/slic3r/GUI/Camera.cpp | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) (limited to 'src/slic3r/GUI/Camera.cpp') diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index dd6cbefe1..5f7a7ce4b 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -28,6 +28,8 @@ Camera::Camera() , inverted_phi(false) , m_theta(45.0f) , m_target(Vec3d::Zero()) + , m_view_matrix(Transform3d::Identity()) + , m_projection_matrix(Transform3d::Identity()) { } @@ -65,11 +67,6 @@ void Camera::set_theta(float theta, bool apply_limit) } } -void Camera::set_scene_box(const BoundingBoxf3& box) -{ - m_scene_box = box; -} - bool Camera::select_view(const std::string& direction) { const float* dir_vec = nullptr; @@ -111,13 +108,43 @@ void Camera::apply_view_matrix() const glsafe(::glLoadIdentity()); glsafe(::glRotatef(-m_theta, 1.0f, 0.0f, 0.0f)); // pitch - glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw - glsafe(::glTranslated(-m_target(0), -m_target(1), -m_target(2))); + glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw + + glsafe(::glTranslated(-m_target(0), -m_target(1), -m_target(2))); // target to origin glsafe(::glGetDoublev(GL_MODELVIEW_MATRIX, m_view_matrix.data())); } -void Camera::apply_ortho_projection(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max) const +void Camera::apply_projection(const BoundingBoxf3& box) const +{ + switch (type) + { + case Ortho: + { + double w2 = (double)m_viewport[2]; + double h2 = (double)m_viewport[3]; + double two_zoom = 2.0 * zoom; + if (two_zoom != 0.0) + { + double inv_two_zoom = 1.0 / two_zoom; + w2 *= inv_two_zoom; + 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, -depth, depth); + + break; + } +// case Perspective: +// { +// } + } +} + +void Camera::apply_ortho_projection(double x_min, double x_max, double y_min, double y_max, double z_min, double z_max) const { glsafe(::glMatrixMode(GL_PROJECTION)); glsafe(::glLoadIdentity()); -- cgit v1.2.3 From 783a52710949153a64beaf1dd50b23c6d0bfe7a7 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 20 May 2019 09:39:57 +0200 Subject: Added imgui debug dialog for camera statistics --- src/slic3r/GUI/Camera.cpp | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'src/slic3r/GUI/Camera.cpp') diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 5f7a7ce4b..a9edb7626 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -2,6 +2,9 @@ #include "Camera.hpp" #include "3DScene.hpp" +#if ENABLE_CAMERA_STATISTICS +#include "GUI_App.hpp" +#endif // ENABLE_CAMERA_STATISTICS #include @@ -109,7 +112,6 @@ void Camera::apply_view_matrix() const glsafe(::glRotatef(-m_theta, 1.0f, 0.0f, 0.0f)); // pitch glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw - glsafe(::glTranslated(-m_target(0), -m_target(1), -m_target(2))); // target to origin glsafe(::glGetDoublev(GL_MODELVIEW_MATRIX, m_view_matrix.data())); @@ -144,6 +146,29 @@ void Camera::apply_projection(const BoundingBoxf3& box) const } } +#if ENABLE_CAMERA_STATISTICS +void Camera::debug_render() const +{ + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.set_next_window_bg_alpha(0.5f); + imgui.begin(std::string("Camera statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + Vec3f position = get_position().cast(); + Vec3f target = m_target.cast(); + Vec3f forward = get_dir_forward().cast(); + Vec3f right = get_dir_right().cast(); + Vec3f up = get_dir_up().cast(); + + ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); + ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); + ImGui::Separator(); + 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.end(); +} +#endif // ENABLE_CAMERA_STATISTICS + void Camera::apply_ortho_projection(double x_min, double x_max, double y_min, double y_max, double z_min, double z_max) const { glsafe(::glMatrixMode(GL_PROJECTION)); -- cgit v1.2.3 From f0b228c4d2f2d385bbfd3fe917fd928ffaadd1d3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 13 Jun 2019 09:12:44 +0200 Subject: Added support for distance between camera position and camera target --- src/slic3r/GUI/Camera.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/slic3r/GUI/Camera.cpp') diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index a9edb7626..4c7cb314a 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -22,11 +22,13 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f }; namespace Slic3r { namespace GUI { +const float Camera::DefaultDistance = 1000.0f; + Camera::Camera() : type(Ortho) , zoom(1.0f) , phi(45.0f) -// , distance(0.0f) + , distance(DefaultDistance) , requires_zoom_to_bed(false) , inverted_phi(false) , m_theta(45.0f) @@ -107,12 +109,18 @@ void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const void Camera::apply_view_matrix() const { + double theta_rad = Geometry::deg2rad(-(double)m_theta); + double phi_rad = Geometry::deg2rad((double)phi); + double sin_theta = ::sin(theta_rad); + Vec3d camera_pos = m_target + (double)distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad)); + glsafe(::glMatrixMode(GL_MODELVIEW)); glsafe(::glLoadIdentity()); glsafe(::glRotatef(-m_theta, 1.0f, 0.0f, 0.0f)); // pitch glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw - glsafe(::glTranslated(-m_target(0), -m_target(1), -m_target(2))); // target to origin + + glsafe(::glTranslated(-camera_pos(0), -camera_pos(1), -camera_pos(2))); glsafe(::glGetDoublev(GL_MODELVIEW_MATRIX, m_view_matrix.data())); } @@ -136,8 +144,7 @@ void Camera::apply_projection(const BoundingBoxf3& box) const // 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, -depth, depth); - + apply_ortho_projection(-w2, w2, -h2, h2, (double)distance - depth, (double)distance + depth); break; } // case Perspective: -- cgit v1.2.3 From a99466ef1df99d52277a5f78a4fbb5481d2dd584 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 13 Jun 2019 10:24:19 +0200 Subject: Method Camera::apply_projection() called at every rendered frame --- src/slic3r/GUI/Camera.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/slic3r/GUI/Camera.cpp') diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 4c7cb314a..5aa881e3e 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -48,7 +48,7 @@ std::string Camera::get_type_as_string() const // case Perspective: // return "perspective"; case Ortho: - return "ortho"; + return "orthographic"; }; } @@ -160,12 +160,15 @@ void Camera::debug_render() const imgui.set_next_window_bg_alpha(0.5f); imgui.begin(std::string("Camera statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + std::string type = get_type_as_string(); Vec3f position = get_position().cast(); Vec3f target = m_target.cast(); Vec3f forward = get_dir_forward().cast(); Vec3f right = get_dir_right().cast(); Vec3f up = get_dir_up().cast(); + ImGui::InputText("Type", const_cast(type.data()), type.length(), ImGuiInputTextFlags_ReadOnly); + ImGui::Separator(); ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::Separator(); -- cgit v1.2.3 From 1a91add2e60f3a4d22cdd6c8a10e57dc8095e0a2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 14 Jun 2019 10:38:09 +0200 Subject: Tighter camera frustrum to reduce z-fighting --- src/slic3r/GUI/Camera.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) (limited to 'src/slic3r/GUI/Camera.cpp') 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 -- cgit v1.2.3 From ac8de0bcaff04b6dc038128c1544ed182f5b3f14 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 14 Jun 2019 15:37:29 +0200 Subject: Follow-up of 1a91add2e60f3a4d22cdd6c8a10e57dc8095e0a2 -> Improvements to tighter camera frustrum to reduce z-fighting --- src/slic3r/GUI/Camera.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/slic3r/GUI/Camera.cpp') diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 1fc2f6be3..f6cefc801 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -169,6 +169,7 @@ void Camera::debug_render() const Vec3f up = get_dir_up().cast(); float nearZ = (float)m_frustrum_zs.first; float farZ = (float)m_frustrum_zs.second; + float deltaZ = farZ - nearZ; ImGui::InputText("Type", const_cast(type.data()), type.length(), ImGuiInputTextFlags_ReadOnly); ImGui::Separator(); @@ -181,6 +182,7 @@ void Camera::debug_render() const 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::InputFloat("Delta Z", &deltaZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); imgui.end(); } #endif // ENABLE_CAMERA_STATISTICS @@ -230,7 +232,12 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo // ensure min size if (ret.second - ret.first < FrustrumMinZSize) - ret.second = ret.first + FrustrumMinZSize; + { + double mid_z = 0.5 * (ret.first + ret.second); + double half_size = 0.5 * FrustrumMinZSize; + ret.first = mid_z - half_size; + ret.second = mid_z + half_size; + } assert(ret.first > 0.0); -- cgit v1.2.3 From a3e6412113b1760ff2ab980cbecffe6505426ba2 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Jun 2019 13:01:18 +0200 Subject: Enabled perspective camera --- src/slic3r/GUI/Camera.cpp | 77 ++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 35 deletions(-) (limited to 'src/slic3r/GUI/Camera.cpp') diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index f6cefc801..1620548ef 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -22,19 +22,19 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f }; namespace Slic3r { namespace GUI { -const float Camera::DefaultDistance = 1000.0f; +const double Camera::DefaultDistance = 1000.0; double Camera::FrustrumMinZSize = 50.0; double Camera::FrustrumZMargin = 10.0; Camera::Camera() - : type(Ortho) - , zoom(1.0f) + : zoom(1.0f) , phi(45.0f) - , distance(DefaultDistance) , requires_zoom_to_bed(false) , inverted_phi(false) - , m_theta(45.0f) + , m_type(Ortho) , m_target(Vec3d::Zero()) + , m_theta(45.0f) + , m_distance(DefaultDistance) , m_view_matrix(Transform3d::Identity()) , m_projection_matrix(Transform3d::Identity()) { @@ -42,18 +42,27 @@ Camera::Camera() std::string Camera::get_type_as_string() const { - switch (type) + switch (m_type) { default: case Unknown: return "unknown"; -// case Perspective: -// return "perspective"; + case Perspective: + return "perspective"; case Ortho: return "orthographic"; }; } +void Camera::select_next_type() +{ + unsigned char next = (unsigned char)m_type + 1; + if (next == (unsigned char)Num_types) + next = 1; + + m_type = (EType)next; +} + void Camera::set_target(const Vec3d& target) { m_target = target; @@ -114,7 +123,7 @@ void Camera::apply_view_matrix() const double theta_rad = Geometry::deg2rad(-(double)m_theta); double phi_rad = Geometry::deg2rad((double)phi); double sin_theta = ::sin(theta_rad); - Vec3d camera_pos = m_target + (double)distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad)); + Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad)); glsafe(::glMatrixMode(GL_MODELVIEW)); glsafe(::glLoadIdentity()); @@ -131,27 +140,36 @@ void Camera::apply_projection(const BoundingBoxf3& box) const { m_frustrum_zs = calc_tight_frustrum_zs_around(box); - switch (type) + double w = (double)m_viewport[2]; + double h = (double)m_viewport[3]; + double two_zoom = 2.0 * zoom; + if (two_zoom != 0.0) + { + double inv_two_zoom = 1.0 / two_zoom; + w *= inv_two_zoom; + h *= inv_two_zoom; + } + + glsafe(::glMatrixMode(GL_PROJECTION)); + glsafe(::glLoadIdentity()); + + switch (m_type) { + default: case Ortho: { - double w2 = (double)m_viewport[2]; - double h2 = (double)m_viewport[3]; - double two_zoom = 2.0 * zoom; - if (two_zoom != 0.0) - { - double inv_two_zoom = 1.0 / two_zoom; - w2 *= inv_two_zoom; - h2 *= inv_two_zoom; - } - - apply_ortho_projection(-w2, w2, -h2, h2, m_frustrum_zs.first, m_frustrum_zs.second); + glsafe(::glOrtho(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second)); + break; + } + case Perspective: + { + glsafe(::glFrustum(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second)); break; } -// case Perspective: -// { -// } } + + glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data())); + glsafe(::glMatrixMode(GL_MODELVIEW)); } #if ENABLE_CAMERA_STATISTICS @@ -187,17 +205,6 @@ void Camera::debug_render() const } #endif // ENABLE_CAMERA_STATISTICS -void Camera::apply_ortho_projection(double x_min, double x_max, double y_min, double y_max, double z_min, double z_max) const -{ - glsafe(::glMatrixMode(GL_PROJECTION)); - glsafe(::glLoadIdentity()); - - glsafe(::glOrtho(x_min, x_max, y_min, y_max, z_min, z_max)); - glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data())); - - 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); -- cgit v1.2.3 From da8179d9c7a7f1892775a3f6d858db60a1c55501 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Jun 2019 14:18:51 +0200 Subject: More camera related functionalities moved from GLCanvas3D to Camera --- src/slic3r/GUI/Camera.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 4 deletions(-) (limited to 'src/slic3r/GUI/Camera.cpp') diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 1620548ef..a3ecb5e54 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -27,13 +27,13 @@ double Camera::FrustrumMinZSize = 50.0; double Camera::FrustrumZMargin = 10.0; Camera::Camera() - : zoom(1.0f) - , phi(45.0f) + : phi(45.0f) , requires_zoom_to_bed(false) , inverted_phi(false) , m_type(Ortho) , m_target(Vec3d::Zero()) , m_theta(45.0f) + , m_zoom(1.0) , m_distance(DefaultDistance) , m_view_matrix(Transform3d::Identity()) , m_projection_matrix(Transform3d::Identity()) @@ -83,6 +83,22 @@ void Camera::set_theta(float theta, bool apply_limit) } } +void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h) +{ + zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0; + zoom = m_zoom / (1.0 - zoom); + + // Don't allow to zoom too far outside the scene. + double zoom_min = calc_zoom_to_bounding_box_factor(max_box, canvas_w, canvas_h); + if (zoom_min > 0.0) + zoom = std::max(zoom, zoom_min * 0.7); + + // Don't allow to zoom too close to the scene. + zoom = std::min(zoom, 100.0); + + m_zoom = zoom; +} + bool Camera::select_view(const std::string& direction) { const float* dir_vec = nullptr; @@ -142,7 +158,8 @@ void Camera::apply_projection(const BoundingBoxf3& box) const double w = (double)m_viewport[2]; double h = (double)m_viewport[3]; - double two_zoom = 2.0 * zoom; + + double two_zoom = 2.0 * m_zoom; if (two_zoom != 0.0) { double inv_two_zoom = 1.0 / two_zoom; @@ -172,6 +189,18 @@ void Camera::apply_projection(const BoundingBoxf3& box) const glsafe(::glMatrixMode(GL_MODELVIEW)); } +void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h) +{ + // Calculate the zoom factor needed to adjust the view around the given box. + double zoom = calc_zoom_to_bounding_box_factor(box, canvas_w, canvas_h); + if (zoom > 0.0) + { + m_zoom = zoom; + // center view around box center + m_target = box.center(); + } +} + #if ENABLE_CAMERA_STATISTICS void Camera::debug_render() const { @@ -212,7 +241,7 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo Vec3d bb_min = box.min; Vec3d bb_max = box.max; - // bbox vertices in world space + // box vertices in world space std::vector vertices; vertices.reserve(8); vertices.push_back(bb_min); @@ -251,6 +280,67 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo return ret; } +double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const +{ + double max_bb_size = box.max_size(); + if (max_bb_size == 0.0) + return -1.0; + + // project the box vertices on a plane perpendicular to the camera forward axis + // then calculates the vertices coordinate on this plane along the camera xy axes + + // ensure that the view matrix is updated + apply_view_matrix(); + + Vec3d right = get_dir_right(); + Vec3d up = get_dir_up(); + Vec3d forward = get_dir_forward(); + + Vec3d bb_min = box.min; + Vec3d bb_max = box.max; + Vec3d bb_center = box.center(); + + // box 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)); + + double max_x = 0.0; + double max_y = 0.0; + + // margin factor to give some empty space around the box + double margin_factor = 1.25; + + for (const Vec3d& v : vertices) + { + // project vertex on the plane perpendicular to camera forward axis + Vec3d pos(v(0) - bb_center(0), v(1) - bb_center(1), v(2) - bb_center(2)); + Vec3d proj_on_plane = pos - pos.dot(forward) * forward; + + // calculates vertex coordinate along camera xy axes + double x_on_plane = proj_on_plane.dot(right); + double y_on_plane = proj_on_plane.dot(up); + + max_x = std::max(max_x, std::abs(x_on_plane)); + max_y = std::max(max_y, std::abs(y_on_plane)); + } + + if ((max_x == 0.0) || (max_y == 0.0)) + return -1.0f; + + max_x *= margin_factor; + max_y *= margin_factor; + + return std::min((double)canvas_w / (2.0 * max_x), (double)canvas_h / (2.0 * max_y)); +} + } // GUI } // Slic3r -- cgit v1.2.3 From b7f67369c99cf204d8071b1d42abc3c2704f1d82 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 20 Jun 2019 10:02:52 +0200 Subject: Serialized camera type and fixed Mac build --- src/slic3r/GUI/Camera.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'src/slic3r/GUI/Camera.cpp') diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index a3ecb5e54..b8c182ef4 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -2,9 +2,8 @@ #include "Camera.hpp" #include "3DScene.hpp" -#if ENABLE_CAMERA_STATISTICS #include "GUI_App.hpp" -#endif // ENABLE_CAMERA_STATISTICS +#include "AppConfig.hpp" #include @@ -44,23 +43,44 @@ std::string Camera::get_type_as_string() const { switch (m_type) { - default: case Unknown: return "unknown"; case Perspective: return "perspective"; + default: case Ortho: return "orthographic"; }; } +void Camera::set_type(EType type) +{ + if (m_type != type) + { + m_type = type; + + wxGetApp().app_config->set("camera_type", std::to_string(m_type)); + wxGetApp().app_config->save(); + } +} + +void Camera::set_type(const std::string& type) +{ + if (!type.empty() && (type != "1")) + { + unsigned char type_id = atoi(type.c_str()); + if (((unsigned char)Ortho < type_id) && (type_id < (unsigned char)Num_types)) + set_type((Camera::EType)type_id); + } +} + void Camera::select_next_type() { unsigned char next = (unsigned char)m_type + 1; if (next == (unsigned char)Num_types) next = 1; - m_type = (EType)next; + set_type((EType)next); } void Camera::set_target(const Vec3d& target) -- cgit v1.2.3 From 8b3d88bc0a654fe8c488bcf4f30b6896587c116c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 24 Jun 2019 09:38:46 +0200 Subject: Adaptive perspective camera frustrum --- src/slic3r/GUI/Camera.cpp | 100 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 14 deletions(-) (limited to 'src/slic3r/GUI/Camera.cpp') diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index b8c182ef4..1f8513ac2 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -24,6 +24,8 @@ namespace GUI { const double Camera::DefaultDistance = 1000.0; double Camera::FrustrumMinZSize = 50.0; double Camera::FrustrumZMargin = 10.0; +double Camera::FovMinDeg = 5.0; +double Camera::FovMaxDeg = 75.0; Camera::Camera() : phi(45.0f) @@ -34,6 +36,7 @@ Camera::Camera() , m_theta(45.0f) , m_zoom(1.0) , m_distance(DefaultDistance) + , m_gui_scale(1.0) , m_view_matrix(Transform3d::Identity()) , m_projection_matrix(Transform3d::Identity()) { @@ -148,6 +151,18 @@ bool Camera::select_view(const std::string& direction) return false; } +double Camera::get_fov() const +{ + switch (m_type) + { + case Perspective: + return 2.0 * Geometry::rad2deg(std::atan(1.0 / m_projection_matrix.matrix()(1, 1))); + default: + case Ortho: + return 0.0; + }; +} + void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const { glsafe(::glViewport(0, 0, w, h)); @@ -174,17 +189,67 @@ void Camera::apply_view_matrix() const void Camera::apply_projection(const BoundingBoxf3& box) const { - m_frustrum_zs = calc_tight_frustrum_zs_around(box); - - double w = (double)m_viewport[2]; - double h = (double)m_viewport[3]; + m_distance = DefaultDistance; + double w = 0.0; + double h = 0.0; - double two_zoom = 2.0 * m_zoom; - if (two_zoom != 0.0) + while (true) { - double inv_two_zoom = 1.0 / two_zoom; - w *= inv_two_zoom; - h *= inv_two_zoom; + m_frustrum_zs = calc_tight_frustrum_zs_around(box); + + w = (double)m_viewport[2]; + h = (double)m_viewport[3]; + + double two_zoom = 2.0 * m_zoom; + if (two_zoom != 0.0) + { + double inv_two_zoom = 1.0 / two_zoom; + w *= inv_two_zoom; + h *= inv_two_zoom; + } + + switch (m_type) + { + default: + case Ortho: + { + m_gui_scale = 1.0; + break; + } + case Perspective: + { + // scale near plane to keep w and h constant on the plane at z = m_distance + double scale = m_frustrum_zs.first / m_distance; + w *= scale; + h *= scale; + m_gui_scale = scale; + break; + } + } + + if (m_type == Perspective) + { + double fov_rad = 2.0 * std::atan(h / m_frustrum_zs.first); + double fov_deg = Geometry::rad2deg(fov_rad); + + // adjust camera distance to keep fov in a limited range + if (fov_deg > FovMaxDeg + 0.001) + { + double new_near_z = h / ::tan(0.5 * Geometry::deg2rad(FovMaxDeg)); + m_distance += (new_near_z - m_frustrum_zs.first); + apply_view_matrix(); + } + else if (fov_deg < FovMinDeg - 0.001) + { + double new_near_z = h / ::tan(0.5 * Geometry::deg2rad(FovMinDeg)); + m_distance += (new_near_z - m_frustrum_zs.first); + apply_view_matrix(); + } + else + break; + } + else + break; } glsafe(::glMatrixMode(GL_PROJECTION)); @@ -231,17 +296,22 @@ void Camera::debug_render() const std::string type = get_type_as_string(); Vec3f position = get_position().cast(); Vec3f target = m_target.cast(); + float distance = (float)get_distance(); 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; float deltaZ = farZ - nearZ; + float zoom = (float)m_zoom; + float fov = (float)get_fov(); + float gui_scale = (float)get_gui_scale(); ImGui::InputText("Type", const_cast(type.data()), type.length(), ImGuiInputTextFlags_ReadOnly); ImGui::Separator(); ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); + ImGui::InputFloat("Distance", &distance, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::Separator(); ImGui::InputFloat3("Forward", forward.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); ImGui::InputFloat3("Right", right.data(), "%.6f", ImGuiInputTextFlags_ReadOnly); @@ -250,6 +320,11 @@ void Camera::debug_render() const 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::InputFloat("Delta Z", &deltaZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); + ImGui::Separator(); + ImGui::InputFloat("Zoom", &zoom, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); + ImGui::InputFloat("Fov", &fov, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); + ImGui::Separator(); + ImGui::InputFloat("GUI scale", &gui_scale, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly); imgui.end(); } #endif // ENABLE_CAMERA_STATISTICS @@ -273,11 +348,10 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo 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) + // set the Z range in eye coordinates (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); + double z = -(m_view_matrix * v)(2); ret.first = std::min(ret.first, z); ret.second = std::max(ret.second, z); } @@ -295,8 +369,6 @@ std::pair Camera::calc_tight_frustrum_zs_around(const BoundingBo ret.second = mid_z + half_size; } - assert(ret.first > 0.0); - return ret; } -- cgit v1.2.3