Adaptive perspective camera frustrum
This commit is contained in:
parent
e5be8adadf
commit
8b3d88bc0a
3 changed files with 99 additions and 18 deletions
src/slic3r/GUI
|
@ -24,6 +24,8 @@ namespace GUI {
|
||||||
const double Camera::DefaultDistance = 1000.0;
|
const double Camera::DefaultDistance = 1000.0;
|
||||||
double Camera::FrustrumMinZSize = 50.0;
|
double Camera::FrustrumMinZSize = 50.0;
|
||||||
double Camera::FrustrumZMargin = 10.0;
|
double Camera::FrustrumZMargin = 10.0;
|
||||||
|
double Camera::FovMinDeg = 5.0;
|
||||||
|
double Camera::FovMaxDeg = 75.0;
|
||||||
|
|
||||||
Camera::Camera()
|
Camera::Camera()
|
||||||
: phi(45.0f)
|
: phi(45.0f)
|
||||||
|
@ -34,6 +36,7 @@ Camera::Camera()
|
||||||
, m_theta(45.0f)
|
, m_theta(45.0f)
|
||||||
, m_zoom(1.0)
|
, m_zoom(1.0)
|
||||||
, m_distance(DefaultDistance)
|
, m_distance(DefaultDistance)
|
||||||
|
, m_gui_scale(1.0)
|
||||||
, m_view_matrix(Transform3d::Identity())
|
, m_view_matrix(Transform3d::Identity())
|
||||||
, m_projection_matrix(Transform3d::Identity())
|
, m_projection_matrix(Transform3d::Identity())
|
||||||
{
|
{
|
||||||
|
@ -148,6 +151,18 @@ bool Camera::select_view(const std::string& direction)
|
||||||
return false;
|
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
|
void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const
|
||||||
{
|
{
|
||||||
glsafe(::glViewport(0, 0, w, h));
|
glsafe(::glViewport(0, 0, w, h));
|
||||||
|
@ -173,11 +188,17 @@ void Camera::apply_view_matrix() const
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::apply_projection(const BoundingBoxf3& box) const
|
void Camera::apply_projection(const BoundingBoxf3& box) const
|
||||||
|
{
|
||||||
|
m_distance = DefaultDistance;
|
||||||
|
double w = 0.0;
|
||||||
|
double h = 0.0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
m_frustrum_zs = calc_tight_frustrum_zs_around(box);
|
m_frustrum_zs = calc_tight_frustrum_zs_around(box);
|
||||||
|
|
||||||
double w = (double)m_viewport[2];
|
w = (double)m_viewport[2];
|
||||||
double h = (double)m_viewport[3];
|
h = (double)m_viewport[3];
|
||||||
|
|
||||||
double two_zoom = 2.0 * m_zoom;
|
double two_zoom = 2.0 * m_zoom;
|
||||||
if (two_zoom != 0.0)
|
if (two_zoom != 0.0)
|
||||||
|
@ -187,6 +208,50 @@ void Camera::apply_projection(const BoundingBoxf3& box) const
|
||||||
h *= 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));
|
glsafe(::glMatrixMode(GL_PROJECTION));
|
||||||
glsafe(::glLoadIdentity());
|
glsafe(::glLoadIdentity());
|
||||||
|
|
||||||
|
@ -231,17 +296,22 @@ void Camera::debug_render() const
|
||||||
std::string type = get_type_as_string();
|
std::string type = get_type_as_string();
|
||||||
Vec3f position = get_position().cast<float>();
|
Vec3f position = get_position().cast<float>();
|
||||||
Vec3f target = m_target.cast<float>();
|
Vec3f target = m_target.cast<float>();
|
||||||
|
float distance = (float)get_distance();
|
||||||
Vec3f forward = get_dir_forward().cast<float>();
|
Vec3f forward = get_dir_forward().cast<float>();
|
||||||
Vec3f right = get_dir_right().cast<float>();
|
Vec3f right = get_dir_right().cast<float>();
|
||||||
Vec3f up = get_dir_up().cast<float>();
|
Vec3f up = get_dir_up().cast<float>();
|
||||||
float nearZ = (float)m_frustrum_zs.first;
|
float nearZ = (float)m_frustrum_zs.first;
|
||||||
float farZ = (float)m_frustrum_zs.second;
|
float farZ = (float)m_frustrum_zs.second;
|
||||||
float deltaZ = farZ - nearZ;
|
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<char*>(type.data()), type.length(), ImGuiInputTextFlags_ReadOnly);
|
ImGui::InputText("Type", const_cast<char*>(type.data()), type.length(), ImGuiInputTextFlags_ReadOnly);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
|
ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||||
ImGui::InputFloat3("Target", target.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::Separator();
|
||||||
ImGui::InputFloat3("Forward", forward.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
|
ImGui::InputFloat3("Forward", forward.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||||
ImGui::InputFloat3("Right", right.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("Near Z", &nearZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
|
||||||
ImGui::InputFloat("Far Z", &farZ, 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::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();
|
imgui.end();
|
||||||
}
|
}
|
||||||
#endif // ENABLE_CAMERA_STATISTICS
|
#endif // ENABLE_CAMERA_STATISTICS
|
||||||
|
@ -273,11 +348,10 @@ std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBo
|
||||||
vertices.push_back(bb_max);
|
vertices.push_back(bb_max);
|
||||||
vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2));
|
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)
|
for (const Vec3d& v : vertices)
|
||||||
{
|
{
|
||||||
// ensure non-negative values
|
double z = -(m_view_matrix * v)(2);
|
||||||
double z = std::max(-(m_view_matrix * v)(2), 0.0);
|
|
||||||
ret.first = std::min(ret.first, z);
|
ret.first = std::min(ret.first, z);
|
||||||
ret.second = std::max(ret.second, z);
|
ret.second = std::max(ret.second, z);
|
||||||
}
|
}
|
||||||
|
@ -295,8 +369,6 @@ std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBo
|
||||||
ret.second = mid_z + half_size;
|
ret.second = mid_z + half_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(ret.first > 0.0);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ struct Camera
|
||||||
static const double DefaultDistance;
|
static const double DefaultDistance;
|
||||||
static double FrustrumMinZSize;
|
static double FrustrumMinZSize;
|
||||||
static double FrustrumZMargin;
|
static double FrustrumZMargin;
|
||||||
|
static double FovMinDeg;
|
||||||
|
static double FovMaxDeg;
|
||||||
|
|
||||||
enum EType : unsigned char
|
enum EType : unsigned char
|
||||||
{
|
{
|
||||||
|
@ -31,7 +33,8 @@ private:
|
||||||
float m_theta;
|
float m_theta;
|
||||||
double m_zoom;
|
double m_zoom;
|
||||||
// Distance between camera position and camera target measured along the camera Z axis
|
// Distance between camera position and camera target measured along the camera Z axis
|
||||||
double m_distance;
|
mutable double m_distance;
|
||||||
|
mutable double m_gui_scale;
|
||||||
|
|
||||||
mutable std::array<int, 4> m_viewport;
|
mutable std::array<int, 4> m_viewport;
|
||||||
mutable Transform3d m_view_matrix;
|
mutable Transform3d m_view_matrix;
|
||||||
|
@ -52,13 +55,14 @@ public:
|
||||||
const Vec3d& get_target() const { return m_target; }
|
const Vec3d& get_target() const { return m_target; }
|
||||||
void set_target(const Vec3d& target);
|
void set_target(const Vec3d& target);
|
||||||
|
|
||||||
|
double get_distance() const { return m_distance; }
|
||||||
|
double get_gui_scale() const { return m_gui_scale; }
|
||||||
|
|
||||||
float get_theta() const { return m_theta; }
|
float get_theta() const { return m_theta; }
|
||||||
void set_theta(float theta, bool apply_limit);
|
void set_theta(float theta, bool apply_limit);
|
||||||
|
|
||||||
double get_zoom() const { return m_zoom; }
|
double get_zoom() const { return m_zoom; }
|
||||||
void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h);
|
void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h);
|
||||||
// this method does not check if the given zoom is valid, use instead the other set_zoom() method
|
|
||||||
// called only by: void GLCanvas3D::update_ui_from_settings()
|
|
||||||
void set_zoom(double zoom) { m_zoom = zoom; }
|
void set_zoom(double zoom) { m_zoom = zoom; }
|
||||||
|
|
||||||
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
|
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
|
||||||
|
@ -79,6 +83,8 @@ public:
|
||||||
double get_near_z() const { return m_frustrum_zs.first; }
|
double get_near_z() const { return m_frustrum_zs.first; }
|
||||||
double get_far_z() const { return m_frustrum_zs.second; }
|
double get_far_z() const { return m_frustrum_zs.second; }
|
||||||
|
|
||||||
|
double get_fov() const;
|
||||||
|
|
||||||
void apply_viewport(int x, int y, unsigned int w, unsigned int h) const;
|
void apply_viewport(int x, int y, unsigned int w, unsigned int h) const;
|
||||||
void apply_view_matrix() const;
|
void apply_view_matrix() const;
|
||||||
void apply_projection(const BoundingBoxf3& box) const;
|
void apply_projection(const BoundingBoxf3& box) const;
|
||||||
|
|
|
@ -3902,8 +3902,11 @@ void GLCanvas3D::_render_overlays() const
|
||||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||||
glsafe(::glPushMatrix());
|
glsafe(::glPushMatrix());
|
||||||
glsafe(::glLoadIdentity());
|
glsafe(::glLoadIdentity());
|
||||||
// ensure the textures are renderered inside the frustrum
|
// ensure that the textures are renderered inside the frustrum
|
||||||
glsafe(::glTranslated(0.0, 0.0, -(m_camera.get_near_z() + 0.5)));
|
glsafe(::glTranslated(0.0, 0.0, -(m_camera.get_near_z() + 0.5)));
|
||||||
|
// ensure that the overlay fits the frustrum near z plane
|
||||||
|
double gui_scale = m_camera.get_gui_scale();
|
||||||
|
glsafe(::glScaled(gui_scale, gui_scale, 1.0));
|
||||||
|
|
||||||
_render_gizmos_overlay();
|
_render_gizmos_overlay();
|
||||||
_render_warning_texture();
|
_render_warning_texture();
|
||||||
|
|
Loading…
Add table
Reference in a new issue