Fixed automatic update of perspective camera

This commit is contained in:
Enrico Turri 2019-08-03 09:07:38 +02:00
parent 77df54947b
commit 8078e00c13
2 changed files with 76 additions and 68 deletions

View file

@ -22,10 +22,10 @@ namespace Slic3r {
namespace GUI {
const double Camera::DefaultDistance = 1000.0;
double Camera::FrustrumMinZSize = 50.0;
double Camera::FrustrumMinZRange = 50.0;
double Camera::FrustrumMinNearZ = 100.0;
double Camera::FrustrumZMargin = 10.0;
double Camera::FovMinDeg = 0.5;
double Camera::FovMaxDeg = 75.0;
double Camera::MaxFovDeg = 60.0;
Camera::Camera()
: phi(45.0f)
@ -186,7 +186,8 @@ void Camera::apply_view_matrix() const
void Camera::apply_projection(const BoundingBoxf3& box) const
{
m_distance = DefaultDistance;
set_distance(DefaultDistance);
double w = 0.0;
double h = 0.0;
@ -194,15 +195,14 @@ void Camera::apply_projection(const BoundingBoxf3& box) const
{
m_frustrum_zs = calc_tight_frustrum_zs_around(box);
w = (double)m_viewport[2];
h = (double)m_viewport[3];
w = 0.5 * (double)m_viewport[2];
h = 0.5 * (double)m_viewport[3];
double two_zoom = 2.0 * m_zoom;
if (two_zoom != 0.0)
if (m_zoom != 0.0)
{
double inv_two_zoom = 1.0 / two_zoom;
w *= inv_two_zoom;
h *= inv_two_zoom;
double inv_zoom = 1.0 / m_zoom;
w *= inv_zoom;
h *= inv_zoom;
}
switch (m_type)
@ -226,21 +226,16 @@ void Camera::apply_projection(const BoundingBoxf3& box) const
if (m_type == Perspective)
{
double fov_rad = 2.0 * std::atan(h / m_frustrum_zs.first);
double fov_deg = Geometry::rad2deg(fov_rad);
double fov_deg = Geometry::rad2deg(2.0 * std::atan(h / m_frustrum_zs.first));
// adjust camera distance to keep fov in a limited range
if (fov_deg > FovMaxDeg + 0.001)
if (fov_deg > MaxFovDeg)
{
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();
double delta_z = h / ::tan(0.5 * Geometry::deg2rad(MaxFovDeg)) - m_frustrum_zs.first;
if (delta_z > 0.001)
set_distance(m_distance + delta_z);
else
break;
}
else
break;
@ -328,42 +323,50 @@ void Camera::debug_render() const
std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const
{
std::pair<double, double> ret = std::make_pair(DBL_MAX, -DBL_MAX);
std::pair<double, double> ret;
Vec3d bb_min = box.min;
Vec3d bb_max = box.max;
// box vertices in world space
std::vector<Vec3d> 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 (negative Zs are in front of the camera)
for (const Vec3d& v : vertices)
while (true)
{
double z = -(m_view_matrix * v)(2);
ret.first = std::min(ret.first, z);
ret.second = std::max(ret.second, z);
}
ret = std::make_pair(DBL_MAX, -DBL_MAX);
// apply margin
ret.first -= FrustrumZMargin;
ret.second += FrustrumZMargin;
// box vertices in world space
std::vector<Vec3d> vertices;
vertices.reserve(8);
vertices.push_back(box.min);
vertices.emplace_back(box.max(0), box.min(1), box.min(2));
vertices.emplace_back(box.max(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.min(1), box.max(2));
vertices.emplace_back(box.max(0), box.min(1), box.max(2));
vertices.push_back(box.max);
vertices.emplace_back(box.min(0), box.max(1), box.max(2));
// ensure min size
if (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;
// set the Z range in eye coordinates (negative Zs are in front of the camera)
for (const Vec3d& v : vertices)
{
double z = -(m_view_matrix * v)(2);
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 < FrustrumMinZRange)
{
double mid_z = 0.5 * (ret.first + ret.second);
double half_size = 0.5 * FrustrumMinZRange;
ret.first = mid_z - half_size;
ret.second = mid_z + half_size;
}
if (ret.first >= FrustrumMinNearZ)
break;
// ensure min Near Z
set_distance(m_distance + FrustrumMinNearZ - ret.first);
}
return ret;
@ -385,21 +388,19 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca
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<Vec3d> 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));
vertices.push_back(box.min);
vertices.emplace_back(box.max(0), box.min(1), box.min(2));
vertices.emplace_back(box.max(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.min(1), box.max(2));
vertices.emplace_back(box.max(0), box.min(1), box.max(2));
vertices.push_back(box.max);
vertices.emplace_back(box.min(0), box.max(1), box.max(2));
double max_x = 0.0;
double max_y = 0.0;
@ -430,6 +431,12 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca
return std::min((double)canvas_w / (2.0 * max_x), (double)canvas_h / (2.0 * max_y));
}
void Camera::set_distance(double distance) const
{
m_distance = distance;
apply_view_matrix();
}
} // GUI
} // Slic3r

View file

@ -10,10 +10,10 @@ namespace GUI {
struct Camera
{
static const double DefaultDistance;
static double FrustrumMinZSize;
static double FrustrumMinZRange;
static double FrustrumMinNearZ;
static double FrustrumZMargin;
static double FovMinDeg;
static double FovMaxDeg;
static double MaxFovDeg;
enum EType : unsigned char
{
@ -101,6 +101,7 @@ private:
// the camera MUST be outside of the bounding box in eye coordinate of the given box
std::pair<double, double> calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const;
double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const;
void set_distance(double distance) const;
};
} // GUI