Merge remote-tracking branch 'origin/master' into ys_bugfixing

This commit is contained in:
YuSanka 2019-06-24 13:13:34 +02:00
commit 0f35a95d75
1134 changed files with 2647 additions and 613 deletions

View file

@ -9,6 +9,7 @@
#include "GUI_App.hpp"
#include "PresetBundle.hpp"
#include "Gizmos/GLGizmoBase.hpp"
#include "GLCanvas3D.hpp"
#include <GL/glew.h>
@ -211,7 +212,7 @@ const double Bed3D::Axes::ArrowLength = 5.0;
Bed3D::Axes::Axes()
: origin(Vec3d::Zero())
, length(Vec3d::Zero())
, length(25.0 * Vec3d::Ones())
{
m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr)
@ -273,6 +274,7 @@ void Bed3D::Axes::render_axis(double length) const
Bed3D::Bed3D()
: m_type(Custom)
, m_requires_canvas_update(false)
#if ENABLE_TEXTURES_FROM_SVG
, m_vbo_id(0)
#endif // ENABLE_TEXTURES_FROM_SVG
@ -290,7 +292,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 +313,7 @@ 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();
// Let the calee to update the UI.
return true;
@ -328,27 +330,26 @@ Point Bed3D::point_projection(const Point& point) const
}
#if ENABLE_TEXTURES_FROM_SVG
void Bed3D::render(float theta, bool useVBOs, float scale_factor) const
void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const
{
m_scale_factor = scale_factor;
EType type = useVBOs ? m_type : Custom;
switch (type)
{
case MK2:
{
render_prusa("mk2", theta > 90.0f);
render_prusa(canvas, "mk2", theta > 90.0f);
break;
}
case MK3:
{
render_prusa("mk3", theta > 90.0f);
render_prusa(canvas, "mk3", theta > 90.0f);
break;
}
case SL1:
{
render_prusa("sl1", theta > 90.0f);
render_prusa(canvas, "sl1", theta > 90.0f);
break;
}
default:
@ -360,7 +361,7 @@ void Bed3D::render(float theta, bool useVBOs, float scale_factor) const
}
}
#else
void Bed3D::render(float theta, bool useVBOs, float scale_factor) const
void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const
{
m_scale_factor = scale_factor;
@ -371,17 +372,17 @@ void Bed3D::render(float theta, bool useVBOs, float scale_factor) const
{
case MK2:
{
render_prusa("mk2", theta, useVBOs);
render_prusa(canvas, "mk2", theta, useVBOs);
break;
}
case MK3:
{
render_prusa("mk3", theta, useVBOs);
render_prusa(canvas, "mk3", theta, useVBOs);
break;
}
case SL1:
{
render_prusa("sl1", theta, useVBOs);
render_prusa(canvas, "sl1", theta, useVBOs);
break;
}
default:
@ -400,13 +401,22 @@ 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;
// extend to contain axes
m_extended_bounding_box.merge(m_axes.length + Axes::ArrowLength * Vec3d::Ones());
// extend to contain model, if any
if (!m_model.get_filename().empty())
m_extended_bounding_box.merge(m_model.get_transformed_bounding_box());
}
void Bed3D::calc_triangles(const ExPolygon& poly)
@ -487,41 +497,51 @@ Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const
}
#if ENABLE_TEXTURES_FROM_SVG
void Bed3D::render_prusa(const std::string &key, bool bottom) const
void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom) const
{
std::string tex_path = resources_dir() + "/icons/bed/" + key;
std::string model_path = resources_dir() + "/models/" + key;
// use anisotropic filter if graphic card allows
GLfloat max_anisotropy = 0.0f;
if (glewIsSupported("GL_EXT_texture_filter_anisotropic"))
glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy));
// use higher resolution images if graphic card allows
GLint max_tex_size;
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size));
// clamp or the texture generation becomes too slow
max_tex_size = std::min(max_tex_size, 8192);
// use higher resolution images if graphic card and opengl version allow
GLint max_tex_size = GLCanvas3DManager::get_gl_info().get_max_tex_size();
std::string filename = tex_path + ".svg";
if ((m_texture.get_id() == 0) || (m_texture.get_source() != filename))
{
if (!m_texture.load_from_svg_file(filename, true, max_tex_size))
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if (!m_temp_texture.load_from_svg_file(filename, false, false, false, max_tex_size / 8))
{
render_custom();
return;
}
if (max_anisotropy > 0.0f)
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_svg_file(filename, true, true, true, max_tex_size))
{
glsafe(::glBindTexture(GL_TEXTURE_2D, m_texture.get_id()));
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
render_custom();
return;
}
}
else if (m_texture.unsent_compressed_data_available())
{
// sends to gpu the already available compressed levels of the main texture
m_texture.send_compressed_data_to_gpu();
// the temporary texture is not needed anymore, reset it
if (m_temp_texture.get_id() != 0)
m_temp_texture.reset();
m_requires_canvas_update = true;
}
else if (m_requires_canvas_update && m_texture.all_compressed_data_sent_to_gpu())
{
if (canvas != nullptr)
canvas->stop_keeping_dirty();
m_requires_canvas_update = false;
}
if (!bottom)
{
@ -539,6 +559,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())
@ -594,7 +617,12 @@ void Bed3D::render_prusa_shader(bool transparent) const
GLint position_id = m_shader.get_attrib_location("v_position");
GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords");
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_texture.get_id()));
// show the temporary texture while no compressed data is available
GLuint tex_id = (GLuint)m_temp_texture.get_id();
if (tex_id == 0)
tex_id = (GLuint)m_texture.get_id();
glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
if (position_id != -1)

View file

@ -13,6 +13,8 @@ typedef class GLUquadric GLUquadricObj;
namespace Slic3r {
namespace GUI {
class GLCanvas3D;
class GeometryBuffer
{
#if ENABLE_TEXTURES_FROM_SVG
@ -85,12 +87,17 @@ public:
private:
EType m_type;
Pointfs m_shape;
BoundingBoxf3 m_bounding_box;
mutable BoundingBoxf3 m_bounding_box;
mutable BoundingBoxf3 m_extended_bounding_box;
Polygon m_polygon;
GeometryBuffer m_triangles;
GeometryBuffer m_gridlines;
#if ENABLE_TEXTURES_FROM_SVG
mutable GLTexture m_texture;
// temporary texture shown until the main texture has still no levels compressed
mutable GLTexture m_temp_texture;
// used to trigger 3D scene update once all compressed textures have been sent to GPU
mutable bool m_requires_canvas_update;
mutable Shader m_shader;
mutable unsigned int m_vbo_id;
#else
@ -117,20 +124,20 @@ 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;
void render(float theta, bool useVBOs, float scale_factor) const;
void render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const;
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;
#if ENABLE_TEXTURES_FROM_SVG
void render_prusa(const std::string& key, bool bottom) const;
void render_prusa(GLCanvas3D* canvas, const std::string& key, bool bottom) const;
void render_prusa_shader(bool transparent) const;
#else
void render_prusa(const std::string &key, float theta, bool useVBOs) const;

View file

@ -2015,7 +2015,7 @@ bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
std::string _3DScene::get_gl_info(bool format_as_html, bool extensions)
{
return s_canvas_mgr.get_gl_info(format_as_html, extensions);
return Slic3r::GUI::GLCanvas3DManager::get_gl_info().to_string(format_as_html, extensions);
}
bool _3DScene::add_canvas(wxGLCanvas* canvas, GUI::Bed3D& bed, GUI::Camera& camera, GUI::GLToolbar& view_toolbar)

View file

@ -555,6 +555,7 @@ public:
const std::string& get_filename() const { return m_filename; }
const BoundingBoxf3& get_bounding_box() const { return m_volume.bounding_box; }
const BoundingBoxf3& get_transformed_bounding_box() const { return m_volume.transformed_bounding_box(); }
void reset();

View file

@ -73,6 +73,9 @@ void AppConfig::set_defaults()
if (get("custom_toolbar_size").empty())
set("custom_toolbar_size", "100");
if (get("camera_type").empty())
set("camera_type", "1");
// Remove legacy window positions/sizes
erase("", "main_frame_maximized");
erase("", "main_frame_pos");

View file

@ -2,6 +2,8 @@
#include "Camera.hpp"
#include "3DScene.hpp"
#include "GUI_App.hpp"
#include "AppConfig.hpp"
#include <GL/glew.h>
@ -19,32 +21,71 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f };
namespace Slic3r {
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()
: type(Ortho)
, zoom(1.0f)
, phi(45.0f)
// , distance(0.0f)
: phi(45.0f)
, requires_zoom_to_bed(false)
, inverted_phi(false)
, m_theta(45.0f)
, m_type(Ortho)
, m_target(Vec3d::Zero())
, 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())
{
}
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";
default:
case Ortho:
return "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;
set_type((EType)next);
}
void Camera::set_target(const Vec3d& target)
{
m_target = target;
@ -65,9 +106,20 @@ void Camera::set_theta(float theta, bool apply_limit)
}
}
void Camera::set_scene_box(const BoundingBoxf3& box)
void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h)
{
m_scene_box = box;
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)
@ -99,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));
@ -107,27 +171,268 @@ 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 + m_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)));
glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw
glsafe(::glTranslated(-camera_pos(0), -camera_pos(1), -camera_pos(2)));
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
{
m_distance = DefaultDistance;
double w = 0.0;
double h = 0.0;
while (true)
{
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));
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()));
switch (m_type)
{
default:
case Ortho:
{
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;
}
}
glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data()));
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
{
ImGuiWrapper& imgui = *wxGetApp().imgui();
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<float>();
Vec3f target = m_target.cast<float>();
float distance = (float)get_distance();
Vec3f forward = get_dir_forward().cast<float>();
Vec3f right = get_dir_right().cast<float>();
Vec3f up = get_dir_up().cast<float>();
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<char*>(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);
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::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
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);
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)
{
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 < 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;
}
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<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));
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

View file

@ -9,44 +9,64 @@ namespace GUI {
struct Camera
{
static const double DefaultDistance;
static double FrustrumMinZSize;
static double FrustrumZMargin;
static double FovMinDeg;
static double FovMaxDeg;
enum EType : unsigned char
{
Unknown,
// Perspective,
Ortho,
Perspective,
Num_types
};
EType type;
float zoom;
float phi;
// float distance;
bool requires_zoom_to_bed;
bool inverted_phi;
private:
EType m_type;
Vec3d m_target;
float m_theta;
double m_zoom;
// Distance between camera position and camera target measured along the camera Z axis
mutable double m_distance;
mutable double m_gui_scale;
mutable std::array<int, 4> m_viewport;
mutable Transform3d m_view_matrix;
mutable Transform3d m_projection_matrix;
mutable std::pair<double, double> m_frustrum_zs;
BoundingBoxf3 m_scene_box;
public:
Camera();
EType get_type() const { return m_type; }
std::string get_type_as_string() const;
void set_type(EType type);
void set_type(const std::string& type);
void select_next_type();
const Vec3d& get_target() const { return m_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; }
void set_theta(float theta, bool apply_limit);
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) { m_zoom = zoom; }
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
void set_scene_box(const BoundingBoxf3& box);
void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; }
bool select_view(const std::string& direction);
@ -60,9 +80,26 @@ 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; }
double get_fov() const;
void apply_viewport(int x, int y, unsigned int w, unsigned int h) const;
void apply_view_matrix() const;
void apply_ortho_projection(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max) const;
void apply_projection(const BoundingBoxf3& box) const;
void zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h);
#if ENABLE_CAMERA_STATISTICS
void debug_render() const;
#endif // ENABLE_CAMERA_STATISTICS
private:
// 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<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;
};
} // GUI

View file

@ -5,11 +5,14 @@
#include <stdexcept>
#include <boost/format.hpp>
#include <boost/asio.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
#include <boost/optional.hpp>
#if _WIN32
#include <regex>
#endif
#include "libslic3r/Utils.hpp"
#include "avrdude/avrdude-slic3r.hpp"
#include "GUI.hpp"
@ -104,7 +107,7 @@ struct FirmwareDialog::priv
// GUI elements
wxComboBox *port_picker;
wxStaticText *port_autodetect;
wxStaticText *txt_port_autodetect;
wxFilePickerCtrl *hex_picker;
wxStaticText *txt_status;
wxGauge *progressbar;
@ -131,6 +134,7 @@ struct FirmwareDialog::priv
// Data
std::vector<SerialPortInfo> ports;
optional<SerialPortInfo> port;
bool port_autodetect;
HexFile hex_file;
// This is a shared pointer holding the background AvrDude task
@ -147,6 +151,7 @@ struct FirmwareDialog::priv
btn_flash_label_flashing(_(L("Cancel"))),
label_status_flashing(_(L("Flashing in progress. Please do not disconnect the printer!"))),
timer_pulse(q),
port_autodetect(false),
progress_tasks_done(0),
progress_tasks_bar(0),
user_cancelled(false),
@ -158,7 +163,8 @@ struct FirmwareDialog::priv
void set_txt_status(const wxString &label);
void flashing_start(unsigned tasks);
void flashing_done(AvrDudeComplete complete);
void enable_port_picker(bool enable);
void set_autodetect(bool autodetect);
void update_flash_enabled();
void load_hex_file(const wxString &path);
void queue_event(AvrdudeEvent aevt, wxString message);
@ -171,6 +177,7 @@ struct FirmwareDialog::priv
void prepare_mk2();
void prepare_mk3();
void prepare_avr109(Avr109Pid usb_pid);
bool get_serial_port();
void perform_upload();
void user_cancel();
@ -211,8 +218,10 @@ void FirmwareDialog::priv::find_serial_ports()
idx = i;
break;
}
if (idx != -1)
if (idx != -1) {
port_picker->SetSelection(idx);
update_flash_enabled();
}
}
}
}
@ -277,20 +286,30 @@ void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete)
}
}
void FirmwareDialog::priv::enable_port_picker(bool enable)
void FirmwareDialog::priv::set_autodetect(bool autodetect)
{
port_picker->Show(enable);
btn_rescan->Show(enable);
port_autodetect->Show(! enable);
port_autodetect = autodetect;
port_picker->Show(!autodetect);
btn_rescan->Show(!autodetect);
txt_port_autodetect->Show(autodetect);
q->Layout();
fit_no_shrink();
}
void FirmwareDialog::priv::update_flash_enabled()
{
const bool hex_exists = wxFileExists(hex_picker->GetPath());
const bool port_valid = port_autodetect || get_serial_port();
btn_flash->Enable(hex_exists && port_valid);
}
void FirmwareDialog::priv::load_hex_file(const wxString &path)
{
hex_file = HexFile(path.wx_str());
const bool auto_lookup = hex_file.device == HexFile::DEV_MM_CONTROL || hex_file.device == HexFile::DEV_CW1;
enable_port_picker(! auto_lookup);
const bool autodetect = hex_file.device == HexFile::DEV_MM_CONTROL || hex_file.device == HexFile::DEV_CW1;
set_autodetect(autodetect);
}
void FirmwareDialog::priv::queue_event(AvrdudeEvent aevt, wxString message)
@ -553,6 +572,31 @@ void FirmwareDialog::priv::prepare_avr109(Avr109Pid usb_pid)
}
bool FirmwareDialog::priv::get_serial_port()
{
const int selection = port_picker->GetSelection();
if (selection != wxNOT_FOUND) {
port = this->ports[selection];
} else {
// User has supplied a custom filename
std::string path_u8 = GUI::into_u8(port_picker->GetValue());
#ifdef _WIN32
static const std::regex com_pattern("COM[0-9]+", std::regex::icase);
std::smatch matches;
if (std::regex_match(path_u8, matches, com_pattern)) {
#else
if (fs::is_other(fs::path(path_u8))) {
#endif
port = SerialPortInfo(std::move(path_u8));
} else {
port = boost::none;
}
}
return !!port;
}
void FirmwareDialog::priv::perform_upload()
{
auto filename = hex_picker->GetPath();
@ -560,14 +604,8 @@ void FirmwareDialog::priv::perform_upload()
load_hex_file(filename); // Might already be loaded, but we want to make sure it's fresh
int selection = port_picker->GetSelection();
if (selection != wxNOT_FOUND) {
port = this->ports[selection];
// Verify whether the combo box list selection equals to the combo box edit value.
if (wxString::FromUTF8(port->friendly_name.data()) != port_picker->GetValue()) {
return;
}
if (!port_autodetect && !get_serial_port()) {
return;
}
const bool extra_verbose = false; // For debugging
@ -769,13 +807,13 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
p->port_picker = new wxComboBox(panel, wxID_ANY);
p->port_autodetect = new wxStaticText(panel, wxID_ANY, _(L("Autodetected")));
p->txt_port_autodetect = new wxStaticText(panel, wxID_ANY, _(L("Autodetected")));
p->btn_rescan = new wxButton(panel, wxID_ANY, _(L("Rescan")));
auto *port_sizer = new wxBoxSizer(wxHORIZONTAL);
port_sizer->Add(p->port_picker, 1, wxEXPAND | wxRIGHT, SPACING);
port_sizer->Add(p->btn_rescan, 0);
port_sizer->Add(p->port_autodetect, 1, wxEXPAND);
p->enable_port_picker(true);
port_sizer->Add(p->txt_port_autodetect, 1, wxEXPAND);
p->set_autodetect(false);
auto *label_progress = new wxStaticText(panel, wxID_ANY, _(L("Progress:")));
p->progressbar = new wxGauge(panel, wxID_ANY, 1, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL | wxGA_SMOOTH);
@ -836,10 +874,13 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
p->hex_picker->Bind(wxEVT_FILEPICKER_CHANGED, [this](wxFileDirPickerEvent& evt) {
if (wxFileExists(evt.GetPath())) {
this->p->load_hex_file(evt.GetPath());
this->p->btn_flash->Enable();
}
p->update_flash_enabled();
});
p->port_picker->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) { p->update_flash_enabled(); });
p->port_picker->Bind(wxEVT_TEXT, [this](wxCommandEvent &) { p->update_flash_enabled(); });
p->spoiler->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, [=](wxCollapsiblePaneEvent &evt) {
if (evt.GetCollapsed()) {
this->SetMinSize(wxSize(p->min_width, p->min_height));

View file

@ -300,22 +300,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)
@ -370,7 +358,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
float half_w = 0.5f * (float)cnv_size.get_width();
float half_h = 0.5f * (float)cnv_size.get_height();
float zoom = canvas.get_camera().zoom;
float zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
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);
@ -382,7 +370,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas
float half_w = 0.5f * (float)cnv_size.get_width();
float half_h = 0.5f * (float)cnv_size.get_height();
float zoom = canvas.get_camera().zoom;
float zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
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);
@ -401,7 +389,7 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas
if (m_tooltip_texture.get_id() == 0)
{
std::string filename = resources_dir() + "/icons/variable_layer_height_tooltip.png";
if (!m_tooltip_texture.load_from_file(filename, false))
if (!m_tooltip_texture.load_from_file(filename, false, true))
return;
}
@ -413,7 +401,7 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas
const float width = (float)m_tooltip_texture.get_width() * scale;
const float height = (float)m_tooltip_texture.get_height() * scale;
float zoom = canvas.get_camera().zoom;
float zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float gap = 10.0f * inv_zoom;
@ -433,7 +421,7 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co
if (m_reset_texture.get_id() == 0)
{
std::string filename = resources_dir() + "/icons/variable_layer_height_reset.png";
if (!m_reset_texture.load_from_file(filename, false))
if (!m_reset_texture.load_from_file(filename, false, true))
return;
}
@ -728,7 +716,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
}
}
_generate(text, canvas, red_colored); // GUI::GLTexture::reset() is called at the beginning of generate(...)
generate(text, canvas, true, red_colored); // GUI::GLTexture::reset() is called at the beginning of generate(...)
// save information for rescaling
m_msg_text = text;
@ -789,7 +777,7 @@ static void msw_disable_cleartype(wxFont &font)
}
#endif /* __WXMSW__ */
bool GLCanvas3D::WarningTexture::_generate(const std::string& msg_utf8, const GLCanvas3D& canvas, const bool red_colored/* = false*/)
bool GLCanvas3D::WarningTexture::generate(const std::string& msg_utf8, const GLCanvas3D& canvas, bool compress, bool red_colored/* = false*/)
{
reset();
@ -863,7 +851,10 @@ bool GLCanvas3D::WarningTexture::_generate(const std::string& msg_utf8, const GL
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (compress && GLEW_EXT_texture_compression_s3tc)
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
@ -879,12 +870,8 @@ 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 zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float left = (-0.5f * (float)m_original_width) * inv_zoom;
float top = (-0.5f * (float)cnv_size.get_height() + (float)m_original_height + 2.0f) * inv_zoom;
@ -903,9 +890,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));
}
}
@ -914,7 +898,7 @@ void GLCanvas3D::WarningTexture::msw_rescale(const GLCanvas3D& canvas)
if (m_msg_text.empty())
return;
_generate(m_msg_text, canvas, m_is_colored_red);
generate(m_msg_text, canvas, true, m_is_colored_red);
}
const unsigned char GLCanvas3D::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
@ -957,7 +941,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePrevie
}
}
bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas)
bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas, bool compress)
{
reset();
@ -1146,7 +1130,10 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (compress && GLEW_EXT_texture_compression_s3tc)
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
@ -1159,12 +1146,8 @@ 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 zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom;
float top = (0.5f * (float)cnv_size.get_height()) * inv_zoom;
@ -1183,9 +1166,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));
}
}
@ -1229,6 +1209,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
#endif // ENABLE_SVG_ICONS
, m_use_clipping_planes(false)
, m_sidebar_field("")
, m_keep_dirty(false)
, m_config(nullptr)
, m_process(nullptr)
, m_model(nullptr)
@ -1446,6 +1427,8 @@ void GLCanvas3D::bed_shape_changed()
m_camera.set_scene_box(scene_bounding_box());
m_camera.requires_zoom_to_bed = true;
m_dirty = true;
if (m_bed.is_prusa())
start_keeping_dirty();
}
void GLCanvas3D::set_color_by(const std::string& value)
@ -1467,7 +1450,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)
{
@ -1475,6 +1458,7 @@ BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
bb.min(2) = std::min(bb.min(2), -h);
bb.max(2) = std::max(bb.max(2), h);
}
return bb;
}
@ -1550,20 +1534,20 @@ void GLCanvas3D::allow_multisample(bool allow)
void GLCanvas3D::zoom_to_bed()
{
_zoom_to_bounding_box(m_bed.get_bounding_box());
_zoom_to_box(m_bed.get_bounding_box(false));
}
void GLCanvas3D::zoom_to_volumes()
{
m_apply_zoom_to_volumes_filter = true;
_zoom_to_bounding_box(volumes_bounding_box());
_zoom_to_box(volumes_bounding_box());
m_apply_zoom_to_volumes_filter = false;
}
void GLCanvas3D::zoom_to_selection()
{
if (!m_selection.is_empty())
_zoom_to_bounding_box(m_selection.get_bounding_box());
_zoom_to_box(m_selection.get_bounding_box());
}
void GLCanvas3D::select_view(const std::string& direction)
@ -1624,6 +1608,7 @@ void GLCanvas3D::render()
}
m_camera.apply_view_matrix();
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));
@ -1685,29 +1670,31 @@ 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();
imgui.set_next_window_bg_alpha(0.5f);
imgui.begin(std::string("Render statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
imgui.text(_(L("Last frame")) +": ");
imgui.text("Last frame: ");
ImGui::SameLine();
imgui.text(std::to_string(m_render_stats.last_frame));
ImGui::SameLine();
imgui.text(" "+_(L("ms")));
imgui.text(" ms");
ImGui::Separator();
imgui.text("Compressed textures: ");
ImGui::SameLine();
imgui.text(GLCanvas3DManager::are_compressed_textures_supported() ? "supported" : "not supported");
imgui.text("Max texture size: ");
ImGui::SameLine();
imgui.text(std::to_string(GLCanvas3DManager::get_gl_info().get_max_tex_size()));
imgui.end();
#endif // ENABLE_RENDER_STATISTICS
#if ENABLE_CAMERA_STATISTICS
m_camera.debug_render();
#endif // ENABLE_CAMERA_STATISTICS
wxGetApp().imgui()->render();
m_canvas->SwapBuffers();
@ -2311,6 +2298,9 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
return;
_refresh_if_shown_on_screen();
if (m_keep_dirty)
m_dirty = true;
}
void GLCanvas3D::on_char(wxKeyEvent& evt)
@ -2407,9 +2397,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case 'B':
case 'b': { zoom_to_bed(); break; }
case 'I':
case 'i': { set_camera_zoom(1.0f); break; }
case 'i': { set_camera_zoom(1.0); break; }
case 'K':
case 'k': { m_camera.select_next_type(); m_dirty = true; break; }
case 'O':
case 'o': { set_camera_zoom(-1.0f); break; }
case 'o': { set_camera_zoom(-1.0); break; }
case 'Z':
case 'z': { m_selection.is_empty() ? zoom_to_volumes() : zoom_to_selection(); break; }
default: { evt.Skip(); break; }
@ -2530,7 +2522,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
m_layers_editing.band_width = std::max(std::min(m_layers_editing.band_width * (1.0f + 0.1f * (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta()), 10.0f), 1.5f);
if (m_canvas != nullptr)
m_canvas->Refresh();
return;
}
}
@ -2541,8 +2533,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
return;
// Calculate the zoom delta and apply it to the current zoom factor
float zoom = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta();
set_camera_zoom(zoom);
set_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta());
}
void GLCanvas3D::on_timer(wxTimerEvent& evt)
@ -3294,21 +3285,11 @@ void GLCanvas3D::do_mirror()
post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
void GLCanvas3D::set_camera_zoom(float zoom)
void GLCanvas3D::set_camera_zoom(double zoom)
{
zoom = std::max(std::min(zoom, 4.0f), -4.0f) / 10.0f;
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());
if (zoom_min > 0.0f)
zoom = std::max(zoom, zoom_min * 0.7f);
// Don't allow to zoom too close to the scene.
zoom = std::min(zoom, 100.0f);
m_camera.zoom = zoom;
_refresh_if_shown_on_screen();
const Size& cnv_size = get_canvas_size();
m_camera.set_zoom(zoom, _max_bounding_box(false), cnv_size.get_width(), cnv_size.get_height());
m_dirty = true;
}
void GLCanvas3D::update_gizmos_on_off_state()
@ -3342,8 +3323,7 @@ void GLCanvas3D::update_ui_from_settings()
if (new_scaling != orig_scaling) {
BOOST_LOG_TRIVIAL(debug) << "GLCanvas3D: Scaling factor: " << new_scaling;
m_camera.zoom /= orig_scaling;
m_camera.zoom *= new_scaling;
m_camera.set_zoom(m_camera.get_zoom() * new_scaling / orig_scaling);
_refresh_if_shown_on_screen();
}
#endif
@ -3390,7 +3370,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)
@ -3621,143 +3601,25 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
// ensures that this canvas is current
_set_current();
// updates camera
m_camera.apply_viewport(0, 0, w, h);
const BoundingBoxf3& bbox = _max_bounding_box();
switch (m_camera.type)
{
case Camera::Ortho:
{
float w2 = w;
float h2 = h;
float two_zoom = 2.0f * m_camera.zoom;
if (two_zoom != 0.0f)
{
float inv_two_zoom = 1.0f / 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.
float depth = std::max(1.f, 5.0f * (float)bbox.max_size());
m_camera.apply_ortho_projection(-w2, w2, -h2, h2, -depth, depth);
break;
}
// case Camera::Perspective:
// {
// float bbox_r = (float)bbox.radius();
// float fov = PI * 45.0f / 180.0f;
// float fov_tan = tan(0.5f * fov);
// float cam_distance = 0.5f * bbox_r / fov_tan;
// m_camera.distance = cam_distance;
//
// float nr = cam_distance - bbox_r * 1.1f;
// float fr = cam_distance + bbox_r * 1.1f;
// if (nr < 1.0f)
// nr = 1.0f;
//
// if (fr < nr + 1.0f)
// fr = nr + 1.0f;
//
// float h2 = fov_tan * nr;
// float w2 = h2 * w / h;
// ::glFrustum(-w2, w2, -h2, h2, nr, fr);
//
// break;
// }
default:
{
throw std::runtime_error("Invalid camera type.");
break;
}
}
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;
}
void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox)
void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box)
{
// Calculate the zoom factor needed to adjust viewport to bounding box.
float zoom = _get_zoom_to_bounding_box_factor(bbox);
if (zoom > 0.0f)
{
m_camera.zoom = zoom;
// center view around bounding box center
m_camera.set_target(bbox.center());
m_dirty = true;
}
}
float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const
{
float max_bb_size = bbox.max_size();
if (max_bb_size == 0.0f)
return -1.0f;
// project the bbox vertices on a plane perpendicular to the camera forward axis
// then calculates the vertices coordinate on this plane along the camera xy axes
// we need the view matrix, we let opengl calculate it (same as done in render())
m_camera.apply_view_matrix();
Vec3d right = m_camera.get_dir_right();
Vec3d up = m_camera.get_dir_up();
Vec3d forward = m_camera.get_dir_forward();
Vec3d bb_min = bbox.min;
Vec3d bb_max = bbox.max;
Vec3d bb_center = bbox.center();
// bbox 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));
double max_x = 0.0;
double max_y = 0.0;
// margin factor to give some empty space around the bbox
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, margin_factor * std::abs(x_on_plane));
max_y = std::max(max_y, margin_factor * std::abs(y_on_plane));
}
if ((max_x == 0.0) || (max_y == 0.0))
return -1.0f;
max_x *= 2.0;
max_y *= 2.0;
const Size& cnv_size = get_canvas_size();
return (float)std::min((double)cnv_size.get_width() / max_x, (double)cnv_size.get_height() / max_y);
m_camera.zoom_to_box(box, cnv_size.get_width(), cnv_size.get_height());
m_dirty = true;
}
void GLCanvas3D::_refresh_if_shown_on_screen()
@ -3943,7 +3805,7 @@ void GLCanvas3D::_render_bed(float theta) const
#if ENABLE_RETINA_GL
scale_factor = m_retina_helper->get_scale_factor();
#endif // ENABLE_RETINA_GL
m_bed.render(theta, m_use_VBOs, scale_factor);
m_bed.render(const_cast<GLCanvas3D*>(this), theta, m_use_VBOs, scale_factor);
}
void GLCanvas3D::_render_axes() const
@ -3972,7 +3834,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);
}
@ -4054,6 +3916,32 @@ 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 that the textures are renderered inside the frustrum
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_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);
@ -4150,7 +4038,7 @@ void GLCanvas3D::_render_toolbar() const
#endif // ENABLE_RETINA_GL
Size cnv_size = get_canvas_size();
float zoom = m_camera.zoom;
float zoom = (float)m_camera.get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation();
@ -4218,7 +4106,7 @@ void GLCanvas3D::_render_view_toolbar() const
#endif // ENABLE_RETINA_GL
Size cnv_size = get_canvas_size();
float zoom = m_camera.zoom;
float zoom = (float)m_camera.get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
// places the toolbar on the bottom-left corner of the 3d scene
@ -5699,7 +5587,7 @@ std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& col
void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
m_legend_texture.generate(preview_data, tool_colors, *this);
m_legend_texture.generate(preview_data, tool_colors, *this, true);
}
void GLCanvas3D::_set_warning_texture(WarningTexture::Warning warning, bool state)

View file

@ -376,7 +376,7 @@ class GLCanvas3D
std::vector<Warning> m_warnings;
// Generates the texture with given text.
bool _generate(const std::string& msg, const GLCanvas3D& canvas, const bool red_colored = false);
bool generate(const std::string& msg, const GLCanvas3D& canvas, bool compress, bool red_colored = false);
};
class LegendTexture : public GUI::GLTexture
@ -399,7 +399,7 @@ class GLCanvas3D
void fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas,
std::vector<std::pair<double, double>>& cp_legend_values);
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas);
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas, bool compress);
void render(const GLCanvas3D& canvas) const;
};
@ -443,6 +443,7 @@ private:
bool m_use_clipping_planes;
mutable SlaCap m_sla_caps[2];
std::string m_sidebar_field;
bool m_keep_dirty;
mutable GLVolumeCollection m_volumes;
Selection m_selection;
@ -599,7 +600,7 @@ public:
void do_flatten();
void do_mirror();
void set_camera_zoom(float zoom);
void set_camera_zoom(double zoom);
void update_gizmos_on_off_state();
void reset_all_gizmos() { m_gizmos.reset_all_states(); }
@ -629,6 +630,9 @@ public:
void set_cursor(ECursorType type);
void msw_rescale();
void start_keeping_dirty() { m_keep_dirty = true; }
void stop_keeping_dirty() { m_keep_dirty = false; }
private:
bool _is_shown_on_screen() const;
@ -637,10 +641,9 @@ 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;
void _zoom_to_box(const BoundingBoxf3& box);
void _refresh_if_shown_on_screen();
@ -654,6 +657,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;

View file

@ -15,40 +15,112 @@
#include <string>
#include <iostream>
#ifdef __APPLE__
#include "../Utils/MacDarkMode.hpp"
#endif // __APPLE__
namespace Slic3r {
namespace GUI {
GLCanvas3DManager::GLInfo::GLInfo()
: version("")
, glsl_version("")
, vendor("")
, renderer("")
: m_detected(false)
, m_version("")
, m_glsl_version("")
, m_vendor("")
, m_renderer("")
, m_max_tex_size(0)
, m_max_anisotropy(0.0f)
{
}
void GLCanvas3DManager::GLInfo::detect()
const std::string& GLCanvas3DManager::GLInfo::get_version() const
{
if (!m_detected)
detect();
return m_version;
}
const std::string& GLCanvas3DManager::GLInfo::get_glsl_version() const
{
if (!m_detected)
detect();
return m_glsl_version;
}
const std::string& GLCanvas3DManager::GLInfo::get_vendor() const
{
if (!m_detected)
detect();
return m_vendor;
}
const std::string& GLCanvas3DManager::GLInfo::get_renderer() const
{
if (!m_detected)
detect();
return m_renderer;
}
int GLCanvas3DManager::GLInfo::get_max_tex_size() const
{
if (!m_detected)
detect();
// clamp to avoid the texture generation become too slow and use too much GPU memory
#ifdef __APPLE__
// and use smaller texture for non retina systems
return (Slic3r::GUI::mac_max_scaling_factor() > 1.0) ? std::min(m_max_tex_size, 8192) : std::min(m_max_tex_size / 2, 4096);
#else
// and use smaller texture for older OpenGL versions
return is_version_greater_or_equal_to(3, 0) ? std::min(m_max_tex_size, 8192) : std::min(m_max_tex_size / 2, 4096);
#endif // __APPLE__
}
float GLCanvas3DManager::GLInfo::get_max_anisotropy() const
{
if (!m_detected)
detect();
return m_max_anisotropy;
}
void GLCanvas3DManager::GLInfo::detect() const
{
const char* data = (const char*)::glGetString(GL_VERSION);
if (data != nullptr)
version = data;
m_version = data;
data = (const char*)::glGetString(GL_SHADING_LANGUAGE_VERSION);
if (data != nullptr)
glsl_version = data;
m_glsl_version = data;
data = (const char*)::glGetString(GL_VENDOR);
if (data != nullptr)
vendor = data;
m_vendor = data;
data = (const char*)::glGetString(GL_RENDERER);
if (data != nullptr)
renderer = data;
m_renderer = data;
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_tex_size));
if (GLEW_EXT_texture_filter_anisotropic)
glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &m_max_anisotropy));
m_detected = true;
}
bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
{
if (!m_detected)
detect();
std::vector<std::string> tokens;
boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on);
boost::split(tokens, m_version, boost::is_any_of(" "), boost::token_compress_on);
if (tokens.empty())
return false;
@ -75,6 +147,9 @@ bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int majo
std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool extensions) const
{
if (!m_detected)
detect();
std::stringstream out;
std::string h2_start = format_as_html ? "<b>" : "";
@ -84,10 +159,10 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
std::string line_end = format_as_html ? "<br>" : "\n";
out << h2_start << "OpenGL installation" << h2_end << line_end;
out << b_start << "GL version: " << b_end << (version.empty() ? "N/A" : version) << line_end;
out << b_start << "Vendor: " << b_end << (vendor.empty() ? "N/A" : vendor) << line_end;
out << b_start << "Renderer: " << b_end << (renderer.empty() ? "N/A" : renderer) << line_end;
out << b_start << "GLSL version: " << b_end << (glsl_version.empty() ? "N/A" : glsl_version) << line_end;
out << b_start << "GL version: " << b_end << (m_version.empty() ? "N/A" : m_version) << line_end;
out << b_start << "Vendor: " << b_end << (m_vendor.empty() ? "N/A" : m_vendor) << line_end;
out << b_start << "Renderer: " << b_end << (m_renderer.empty() ? "N/A" : m_renderer) << line_end;
out << b_start << "GLSL version: " << b_end << (m_glsl_version.empty() ? "N/A" : m_glsl_version) << line_end;
if (extensions)
{
@ -111,6 +186,8 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
}
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown;
bool GLCanvas3DManager::s_compressed_textures_supported = false;
GLCanvas3DManager::GLInfo GLCanvas3DManager::s_gl_info;
GLCanvas3DManager::GLCanvas3DManager()
: m_context(nullptr)
@ -134,7 +211,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLTo
if (canvas == nullptr)
return false;
if (_get_canvas(canvas) != m_canvases.end())
if (do_get_canvas(canvas) != m_canvases.end())
return false;
GLCanvas3D* canvas3D = new GLCanvas3D(canvas, bed, camera, view_toolbar);
@ -159,7 +236,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLTo
bool GLCanvas3DManager::remove(wxGLCanvas* canvas)
{
CanvasesMap::iterator it = _get_canvas(canvas);
CanvasesMap::iterator it = do_get_canvas(canvas);
if (it == m_canvases.end())
return false;
@ -190,31 +267,29 @@ void GLCanvas3DManager::init_gl()
if (!m_gl_initialized)
{
glewInit();
m_gl_info.detect();
const AppConfig* config = GUI::get_app_config();
m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1");
m_use_VBOs = !m_use_legacy_opengl && m_gl_info.is_version_greater_or_equal_to(2, 0);
m_use_VBOs = !m_use_legacy_opengl && s_gl_info.is_version_greater_or_equal_to(2, 0);
m_gl_initialized = true;
if (GLEW_EXT_texture_compression_s3tc)
s_compressed_textures_supported = true;
else
s_compressed_textures_supported = false;
}
}
std::string GLCanvas3DManager::get_gl_info(bool format_as_html, bool extensions) const
{
return m_gl_info.to_string(format_as_html, extensions);
}
bool GLCanvas3DManager::init(wxGLCanvas* canvas)
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
CanvasesMap::const_iterator it = do_get_canvas(canvas);
if (it != m_canvases.end())
return (it->second != nullptr) ? _init(*it->second) : false;
return (it->second != nullptr) ? init(*it->second) : false;
else
return false;
}
GLCanvas3D* GLCanvas3DManager::get_canvas(wxGLCanvas* canvas)
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
CanvasesMap::const_iterator it = do_get_canvas(canvas);
return (it != m_canvases.end()) ? it->second : nullptr;
}
@ -224,29 +299,28 @@ wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent)
if (s_multisample == MS_Unknown)
{
_detect_multisample(attribList);
// debug output
std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl;
detect_multisample(attribList);
// // debug output
// std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl;
}
if (! can_multisample()) {
if (! can_multisample())
attribList[4] = 0;
}
return new wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
}
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas)
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::do_get_canvas(wxGLCanvas* canvas)
{
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);
}
GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) const
GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::do_get_canvas(wxGLCanvas* canvas) const
{
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);
}
bool GLCanvas3DManager::_init(GLCanvas3D& canvas)
bool GLCanvas3DManager::init(GLCanvas3D& canvas)
{
if (!m_gl_initialized)
init_gl();
@ -254,7 +328,7 @@ bool GLCanvas3DManager::_init(GLCanvas3D& canvas)
return canvas.init(m_use_VBOs, m_use_legacy_opengl);
}
void GLCanvas3DManager::_detect_multisample(int* attribList)
void GLCanvas3DManager::detect_multisample(int* attribList)
{
int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER;
const AppConfig* app_config = GUI::get_app_config();

View file

@ -29,21 +29,39 @@ struct Camera;
class GLCanvas3DManager
{
struct GLInfo
public:
class GLInfo
{
std::string version;
std::string glsl_version;
std::string vendor;
std::string renderer;
mutable bool m_detected;
mutable std::string m_version;
mutable std::string m_glsl_version;
mutable std::string m_vendor;
mutable std::string m_renderer;
mutable int m_max_tex_size;
mutable float m_max_anisotropy;
public:
GLInfo();
void detect();
const std::string& get_version() const;
const std::string& get_glsl_version() const;
const std::string& get_vendor() const;
const std::string& get_renderer() const;
int get_max_tex_size() const;
float get_max_anisotropy() const;
bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const;
std::string to_string(bool format_as_html, bool extensions) const;
private:
void detect() const;
};
private:
enum EMultisampleState : unsigned char
{
MS_Unknown,
@ -55,11 +73,12 @@ class GLCanvas3DManager
CanvasesMap m_canvases;
wxGLContext* m_context;
GLInfo m_gl_info;
static GLInfo s_gl_info;
bool m_gl_initialized;
bool m_use_legacy_opengl;
bool m_use_VBOs;
static EMultisampleState s_multisample;
static bool s_compressed_textures_supported;
public:
GLCanvas3DManager();
@ -72,21 +91,24 @@ public:
unsigned int count() const;
void init_gl();
std::string get_gl_info(bool format_as_html, bool extensions) const;
bool init(wxGLCanvas* canvas);
GLCanvas3D* get_canvas(wxGLCanvas* canvas);
static bool can_multisample() { return s_multisample == MS_Enabled; }
static bool are_compressed_textures_supported() { return s_compressed_textures_supported; }
static wxGLCanvas* create_wxglcanvas(wxWindow *parent);
private:
CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas);
CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const;
static const GLInfo& get_gl_info() { return s_gl_info; }
bool _init(GLCanvas3D& canvas);
static void _detect_multisample(int* attribList);
private:
CanvasesMap::iterator do_get_canvas(wxGLCanvas* canvas);
CanvasesMap::const_iterator do_get_canvas(wxGLCanvas* canvas) const;
bool init(GLCanvas3D& canvas);
static void detect_multisample(int* attribList);
};
} // namespace GUI

View file

@ -68,7 +68,7 @@ namespace GUI {
if (!is_dragging())
return;
float zoom = canvas.get_camera().zoom;
float zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
Size cnv_size = canvas.get_canvas_size();

View file

@ -12,6 +12,10 @@
#include <vector>
#include <algorithm>
#include <thread>
#define STB_DXT_IMPLEMENTATION
#include "stb_dxt/stb_dxt.h"
#include "nanosvg/nanosvg.h"
#include "nanosvg/nanosvgrast.h"
@ -21,6 +25,99 @@
namespace Slic3r {
namespace GUI {
void GLTexture::Compressor::reset()
{
// force compression completion, if any
m_abort_compressing = true;
// wait for compression completion, if any
while (m_is_compressing) {}
m_levels.clear();
}
void GLTexture::Compressor::add_level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data)
{
m_levels.emplace_back(w, h, data);
}
void GLTexture::Compressor::start_compressing()
{
m_is_compressing = true;
m_abort_compressing = false;
std::thread t(&GLTexture::Compressor::compress, this);
t.detach();
}
bool GLTexture::Compressor::unsent_compressed_data_available() const
{
for (const Level& level : m_levels)
{
if (!level.sent_to_gpu && level.compressed)
return true;
}
return false;
}
void GLTexture::Compressor::send_compressed_data_to_gpu()
{
// this method should be called inside the main thread of Slicer or a new OpenGL context (sharing resources) would be needed
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_texture.m_id));
for (int i = 0; i < (int)m_levels.size(); ++i)
{
Level& level = m_levels[i];
if (!level.sent_to_gpu && level.compressed)
{
glsafe(::glCompressedTexSubImage2D(GL_TEXTURE_2D, (GLint)i, 0, 0, (GLsizei)level.w, (GLsizei)level.h, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)level.compressed_data.size(), (const GLvoid*)level.compressed_data.data()));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (i > 0) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
level.sent_to_gpu = true;
// we are done with the compressed data, we can discard it
level.compressed_data.clear();
level.compressed = false;
}
}
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
}
bool GLTexture::Compressor::all_compressed_data_sent_to_gpu() const
{
for (const Level& level : m_levels)
{
if (!level.sent_to_gpu)
return false;
}
return true;
}
void GLTexture::Compressor::compress()
{
// reference: https://github.com/Cyan4973/RygsDXTc
for (Level& level : m_levels)
{
if (m_abort_compressing)
break;
// stb_dxt library, despite claiming that the needed size of the destination buffer is equal to (source buffer size)/4,
// crashes if doing so, so we start with twice the required size
level.compressed_data = std::vector<unsigned char>(level.w * level.h * 2, 0);
int compressed_size = 0;
rygCompress(level.compressed_data.data(), level.src_data.data(), level.w, level.h, 1, compressed_size);
level.compressed_data.resize(compressed_size);
// we are done with the source data, we can discard it
level.src_data.clear();
level.compressed = true;
}
m_is_compressing = false;
m_abort_compressing = false;
}
GLTexture::Quad_UVs GLTexture::FullTextureUVs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } };
GLTexture::GLTexture()
@ -28,6 +125,7 @@ GLTexture::GLTexture()
, m_width(0)
, m_height(0)
, m_source("")
, m_compressor(*this)
{
}
@ -36,7 +134,7 @@ GLTexture::~GLTexture()
reset();
}
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps)
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, bool compress)
{
reset();
@ -44,12 +142,12 @@ bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps)
return false;
if (boost::algorithm::iends_with(filename, ".png"))
return load_from_png(filename, use_mipmaps);
return load_from_png(filename, use_mipmaps, compress);
else
return false;
}
bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps, unsigned int max_size_px)
bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
{
reset();
@ -57,12 +155,12 @@ bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps
return false;
if (boost::algorithm::iends_with(filename, ".svg"))
return load_from_svg(filename, use_mipmaps, max_size_px);
return load_from_svg(filename, use_mipmaps, compress, apply_anisotropy, max_size_px);
else
return false;
}
bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px)
bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px, bool compress)
{
reset();
@ -178,7 +276,10 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (compress && GLEW_EXT_texture_compression_s3tc)
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
@ -222,6 +323,7 @@ void GLTexture::reset()
m_width = 0;
m_height = 0;
m_source = "";
m_compressor.reset();
}
void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top)
@ -252,7 +354,7 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
glsafe(::glDisable(GL_BLEND));
}
unsigned int GLTexture::generate_mipmaps(wxImage& image)
unsigned int GLTexture::generate_mipmaps(wxImage& image, bool compress)
{
int w = image.GetWidth();
int h = image.GetHeight();
@ -284,13 +386,16 @@ unsigned int GLTexture::generate_mipmaps(wxImage& image)
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
}
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (compress && GLEW_EXT_texture_compression_s3tc)
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
}
return (unsigned int)level;
}
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps)
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, bool compress)
{
// Load a PNG with an alpha channel.
wxImage image;
@ -335,11 +440,14 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps)
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (compress && GLEW_EXT_texture_compression_s3tc)
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (use_mipmaps)
{
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
unsigned int levels_count = generate_mipmaps(image);
unsigned int levels_count = generate_mipmaps(image, compress);
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels_count));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
}
@ -357,8 +465,10 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps)
return true;
}
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px)
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
{
bool compression_enabled = compress && GLEW_EXT_texture_compression_s3tc;
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
if (image == nullptr)
{
@ -371,6 +481,20 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
m_width = (int)(scale * image->width);
m_height = (int)(scale * image->height);
if (compression_enabled)
{
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
int width_rem = m_width % 4;
int height_rem = m_height % 4;
if (width_rem != 0)
m_width += (4 - width_rem);
if (height_rem != 0)
m_height += (4 - height_rem);
}
int n_pixels = m_width * m_height;
if (n_pixels <= 0)
@ -397,14 +521,32 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (apply_anisotropy)
{
GLfloat max_anisotropy = GLCanvas3DManager::get_gl_info().get_max_anisotropy();
if (max_anisotropy > 1.0f)
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
}
if (compression_enabled)
{
// initializes the texture on GPU
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
// and send the uncompressed data to the compressor
m_compressor.add_level((unsigned int)m_width, (unsigned int)m_height, data);
}
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (use_mipmaps)
{
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
int lod_w = m_width;
int lod_h = m_height;
GLint level = 0;
while ((lod_w > 1) || (lod_h > 1))
// we do not need to generate all levels down to 1x1
while ((lod_w > 16) || (lod_h > 16))
{
++level;
@ -412,12 +554,25 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
lod_h = std::max(lod_h / 2, 1);
scale /= 2.0f;
data.resize(lod_w * lod_h * 4);
nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4);
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (compression_enabled)
{
// initializes the texture on GPU
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
// and send the uncompressed data to the compressor
m_compressor.add_level((unsigned int)lod_w, (unsigned int)lod_h, data);
}
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
}
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
if (!compression_enabled)
{
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
}
}
else
{
@ -430,6 +585,10 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
m_source = filename;
if (compression_enabled)
// start asynchronous compression
m_compressor.start_compressing();
nsvgDeleteRasterizer(rast);
nsvgDelete(image);

View file

@ -11,6 +11,42 @@ namespace GUI {
class GLTexture
{
class Compressor
{
struct Level
{
unsigned int w;
unsigned int h;
std::vector<unsigned char> src_data;
std::vector<unsigned char> compressed_data;
bool compressed;
bool sent_to_gpu;
Level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data) : w(w), h(h), src_data(data), compressed(false), sent_to_gpu(false) {}
};
GLTexture& m_texture;
std::vector<Level> m_levels;
bool m_is_compressing;
bool m_abort_compressing;
public:
explicit Compressor(GLTexture& texture) : m_texture(texture), m_is_compressing(false), m_abort_compressing(false) {}
void reset();
void add_level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data);
void start_compressing();
bool unsent_compressed_data_available() const;
void send_compressed_data_to_gpu();
bool all_compressed_data_sent_to_gpu() const;
private:
void compress();
};
public:
struct UV
{
@ -33,13 +69,14 @@ namespace GUI {
int m_width;
int m_height;
std::string m_source;
Compressor m_compressor;
public:
GLTexture();
virtual ~GLTexture();
bool load_from_file(const std::string& filename, bool use_mipmaps);
bool load_from_svg_file(const std::string& filename, bool use_mipmaps, unsigned int max_size_px);
bool load_from_file(const std::string& filename, bool use_mipmaps, bool compress);
bool load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
// meanings of states: (std::pair<int, bool>)
// first field (int):
// 0 -> no changes
@ -48,7 +85,7 @@ namespace GUI {
// second field (bool):
// false -> no changes
// true -> add background color
bool load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px);
bool load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px, bool compress);
void reset();
unsigned int get_id() const { return m_id; }
@ -57,14 +94,21 @@ namespace GUI {
const std::string& get_source() const { return m_source; }
bool unsent_compressed_data_available() const { return m_compressor.unsent_compressed_data_available(); }
void send_compressed_data_to_gpu() { m_compressor.send_compressed_data_to_gpu(); }
bool all_compressed_data_sent_to_gpu() const { return m_compressor.all_compressed_data_sent_to_gpu(); }
static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top);
static void render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const Quad_UVs& uvs);
protected:
unsigned int generate_mipmaps(wxImage& image);
unsigned int generate_mipmaps(wxImage& image, bool compress);
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);
bool load_from_png(const std::string& filename, bool use_mipmaps, bool compress);
bool load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
friend class Compressor;
};
} // namespace GUI

View file

@ -194,7 +194,7 @@ bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const Bac
#endif // ENABLE_SVG_ICONS
if (!background_texture.filename.empty())
res = m_background_texture.texture.load_from_file(path + background_texture.filename, false);
res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, true);
if (res)
m_background_texture.metadata = background_texture;
@ -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)
@ -614,7 +607,7 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
{
// NB: mouse_pos is already scaled appropriately
float zoom = parent.get_camera().zoom;
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
@ -719,7 +712,7 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
{
// NB: mouse_pos is already scaled appropriately
float zoom = parent.get_camera().zoom;
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
@ -836,7 +829,7 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3
{
// NB: mouse_pos is already scaled appropriately
float zoom = parent.get_camera().zoom;
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
@ -919,7 +912,7 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
{
// NB: mouse_pos is already scaled appropriately
float zoom = parent.get_camera().zoom;
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
@ -1015,7 +1008,7 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
return;
#endif // !ENABLE_SVG_ICONS
float zoom = parent.get_camera().zoom;
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = inv_zoom * m_layout.scale;
@ -1170,7 +1163,7 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const
return;
#endif // !ENABLE_SVG_ICONS
float zoom = parent.get_camera().zoom;
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = inv_zoom * m_layout.scale;
@ -1338,7 +1331,7 @@ bool GLToolbar::generate_icons_texture() const
states.push_back(std::make_pair(1, true));
}
bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_layout.icons_size * m_layout.scale));
bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_layout.icons_size * m_layout.scale), true);
if (res)
m_icons_texture_dirty = false;

View file

@ -265,10 +265,8 @@ bool GUI_App::on_init_inner()
}
CallAfter([this] {
if (!config_wizard_startup(app_conf_exists)) {
// Only notify if there was no wizard so as not to bother too much ...
preset_updater->slic3r_update_notify();
}
config_wizard_startup(app_conf_exists);
preset_updater->slic3r_update_notify();
preset_updater->sync(preset_bundle);
});
}

View file

@ -1485,66 +1485,6 @@ void ObjectList::load_part( ModelObject* model_object,
}
// Find volume transformation, so that the chained (instance_trafo * volume_trafo) will be as close to identity
// as possible in least squares norm in regard to the 8 corners of bbox.
// Bounding box is expected to be centered around zero in all axes.
Geometry::Transformation volume_to_bed_transformation(const Geometry::Transformation &instance_transformation, const BoundingBoxf3 &bbox)
{
Geometry::Transformation out;
if (instance_transformation.is_scaling_uniform()) {
// No need to run the non-linear least squares fitting for uniform scaling.
// Just set the inverse.
out.set_from_transform(instance_transformation.get_matrix(true).inverse());
}
else if (Geometry::is_rotation_ninety_degrees(instance_transformation.get_rotation()))
{
// Anisotropic scaling, rotation by multiples of ninety degrees.
Eigen::Matrix3d instance_rotation_trafo =
(Eigen::AngleAxisd(instance_transformation.get_rotation().z(), Vec3d::UnitZ()) *
Eigen::AngleAxisd(instance_transformation.get_rotation().y(), Vec3d::UnitY()) *
Eigen::AngleAxisd(instance_transformation.get_rotation().x(), Vec3d::UnitX())).toRotationMatrix();
Eigen::Matrix3d volume_rotation_trafo =
(Eigen::AngleAxisd(-instance_transformation.get_rotation().x(), Vec3d::UnitX()) *
Eigen::AngleAxisd(-instance_transformation.get_rotation().y(), Vec3d::UnitY()) *
Eigen::AngleAxisd(-instance_transformation.get_rotation().z(), Vec3d::UnitZ())).toRotationMatrix();
// 8 corners of the bounding box.
auto pts = Eigen::MatrixXd(8, 3);
pts(0, 0) = bbox.min.x(); pts(0, 1) = bbox.min.y(); pts(0, 2) = bbox.min.z();
pts(1, 0) = bbox.min.x(); pts(1, 1) = bbox.min.y(); pts(1, 2) = bbox.max.z();
pts(2, 0) = bbox.min.x(); pts(2, 1) = bbox.max.y(); pts(2, 2) = bbox.min.z();
pts(3, 0) = bbox.min.x(); pts(3, 1) = bbox.max.y(); pts(3, 2) = bbox.max.z();
pts(4, 0) = bbox.max.x(); pts(4, 1) = bbox.min.y(); pts(4, 2) = bbox.min.z();
pts(5, 0) = bbox.max.x(); pts(5, 1) = bbox.min.y(); pts(5, 2) = bbox.max.z();
pts(6, 0) = bbox.max.x(); pts(6, 1) = bbox.max.y(); pts(6, 2) = bbox.min.z();
pts(7, 0) = bbox.max.x(); pts(7, 1) = bbox.max.y(); pts(7, 2) = bbox.max.z();
// Corners of the bounding box transformed into the modifier mesh coordinate space, with inverse rotation applied to the modifier.
auto qs = pts *
(instance_rotation_trafo *
Eigen::Scaling(instance_transformation.get_scaling_factor().cwiseProduct(instance_transformation.get_mirror())) *
volume_rotation_trafo).inverse().transpose();
// Fill in scaling based on least squares fitting of the bounding box corners.
Vec3d scale;
for (int i = 0; i < 3; ++ i)
scale(i) = pts.col(i).dot(qs.col(i)) / pts.col(i).dot(pts.col(i));
out.set_rotation(Geometry::extract_euler_angles(volume_rotation_trafo));
out.set_scaling_factor(Vec3d(std::abs(scale(0)), std::abs(scale(1)), std::abs(scale(2))));
out.set_mirror(Vec3d(scale(0) > 0 ? 1. : -1, scale(1) > 0 ? 1. : -1, scale(2) > 0 ? 1. : -1));
}
else
{
// General anisotropic scaling, general rotation.
// Keep the modifier mesh in the instance coordinate system, so the modifier mesh will not be aligned with the world.
// Scale it to get the required size.
out.set_scaling_factor(instance_transformation.get_scaling_factor().cwiseInverse());
}
return out;
}
void ObjectList::load_generic_subobject(const std::string& type_name, const ModelVolumeType type)
{
const auto obj_idx = get_selected_obj_idx();
@ -1598,7 +1538,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
// Transform the new modifier to be aligned with the print bed.
const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box();
new_volume->set_transformation(volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
new_volume->set_transformation(Geometry::Transformation::volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
// Set the modifier position.
auto offset = (type_name == "Slab") ?
// Slab: Lift to print bed

View file

@ -65,7 +65,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
if (!m_background_texture.metadata.filename.empty())
{
if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false))
if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false, true))
{
reset();
return false;
@ -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;
@ -939,7 +930,7 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio
float cnv_w = (float)canvas.get_canvas_size().get_width();
float cnv_h = (float)canvas.get_canvas_size().get_height();
float zoom = canvas.get_camera().zoom;
float zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float height = get_total_overlay_height();
@ -1160,7 +1151,7 @@ bool GLGizmosManager::generate_icons_texture() const
states.push_back(std::make_pair(0, false));
states.push_back(std::make_pair(0, true));
bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_overlay_icons_size * m_overlay_scale));
bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_overlay_icons_size * m_overlay_scale), true);
if (res)
m_icons_texture_dirty = false;

View file

@ -206,7 +206,7 @@ void ImGuiWrapper::new_frame()
}
if (m_font_texture == 0) {
init_font();
init_font(true);
}
ImGui::NewFrame();
@ -383,7 +383,7 @@ bool ImGuiWrapper::want_any_input() const
return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput;
}
void ImGuiWrapper::init_font()
void ImGuiWrapper::init_font(bool compress)
{
destroy_font();
@ -412,7 +412,10 @@ void ImGuiWrapper::init_font()
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
if (compress && GLEW_EXT_texture_compression_s3tc)
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
// Store our identifier
io.Fonts->TexID = (ImTextureID)(intptr_t)m_font_texture;

View file

@ -77,7 +77,7 @@ public:
bool want_any_input() const;
private:
void init_font();
void init_font(bool compress);
void init_input();
void init_style();
void render_draw_data(ImDrawData *draw_data);

View file

@ -147,6 +147,7 @@ void KBShortcutsDialog::fill_shortcuts()
plater_shortcuts.push_back(Shortcut("F", L("Press to scale selection to fit print volume\nin Gizmo scale")));
plater_shortcuts.push_back(Shortcut(alt, L("Press to activate deselection rectangle\nor to scale or rotate selected objects\naround their own center")));
plater_shortcuts.push_back(Shortcut(ctrl, L("Press to activate one direction scaling in Gizmo scale")));
plater_shortcuts.push_back(Shortcut("K", L("Change camera type")));
plater_shortcuts.push_back(Shortcut("B", L("Zoom to Bed")));
plater_shortcuts.push_back(Shortcut("Z", L("Zoom to all objects in scene, if none selected")));
plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object")));

View file

@ -1772,6 +1772,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
q->Layout();
set_current_panel(view3D);
// updates camera type from .ini file
camera.set_type(get_config("camera_type"));
}
void Plater::priv::update(bool force_full_scene_refresh)

View file

@ -2,6 +2,7 @@
#define PROGRESSSTATUSBAR_HPP
#include <memory>
#include <string>
#include <functional>
#include <string>

View file

@ -1893,25 +1893,59 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
void Selection::paste_volumes_from_clipboard()
{
int obj_idx = get_object_idx();
if ((obj_idx < 0) || ((int)m_model->objects.size() <= obj_idx))
int dst_obj_idx = get_object_idx();
if ((dst_obj_idx < 0) || ((int)m_model->objects.size() <= dst_obj_idx))
return;
ModelObject* dst_object = m_model->objects[dst_obj_idx];
int dst_inst_idx = get_instance_idx();
if ((dst_inst_idx < 0) || ((int)dst_object->instances.size() <= dst_inst_idx))
return;
ModelObject* src_object = m_clipboard.get_object(0);
if (src_object != nullptr)
{
ModelObject* dst_object = m_model->objects[obj_idx];
ModelInstance* dst_instance = dst_object->instances[dst_inst_idx];
BoundingBoxf3 dst_instance_bb = dst_object->instance_bounding_box(dst_inst_idx);
Transform3d src_matrix = src_object->instances[0]->get_transformation().get_matrix(true);
Transform3d dst_matrix = dst_instance->get_transformation().get_matrix(true);
bool from_same_object = (src_object->input_file == dst_object->input_file) && src_matrix.isApprox(dst_matrix);
// used to keep relative position of multivolume selections when pasting from another object
BoundingBoxf3 total_bb;
ModelVolumePtrs volumes;
for (ModelVolume* src_volume : src_object->volumes)
{
ModelVolume* dst_volume = dst_object->add_volume(*src_volume);
dst_volume->set_new_unique_id();
double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
dst_volume->translate(offset, offset, 0.0);
if (from_same_object)
{
// // if the volume comes from the same object, apply the offset in world system
// double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
// dst_volume->translate(dst_matrix.inverse() * Vec3d(offset, offset, 0.0));
}
else
{
// if the volume comes from another object, apply the offset as done when adding modifiers
// see ObjectList::load_generic_subobject()
total_bb.merge(dst_volume->mesh().bounding_box().transformed(src_volume->get_matrix()));
}
volumes.push_back(dst_volume);
}
wxGetApp().obj_list()->paste_volumes_into_list(obj_idx, volumes);
// keeps relative position of multivolume selections
if (!from_same_object)
{
for (ModelVolume* v : volumes)
{
v->set_offset((v->get_offset() - total_bb.center()) + dst_matrix.inverse() * (Vec3d(dst_instance_bb.max(0), dst_instance_bb.min(1), dst_instance_bb.min(2)) + 0.5 * total_bb.size() - dst_instance->get_transformation().get_offset()));
}
}
wxGetApp().obj_list()->paste_volumes_into_list(dst_obj_idx, volumes);
}
}