Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/slic3r')
-rw-r--r--src/slic3r/Config/Version.cpp1
-rw-r--r--src/slic3r/Config/Version.hpp4
-rw-r--r--src/slic3r/GUI/3DBed.cpp90
-rw-r--r--src/slic3r/GUI/3DBed.hpp17
-rw-r--r--src/slic3r/GUI/3DScene.cpp2
-rw-r--r--src/slic3r/GUI/3DScene.hpp1
-rw-r--r--src/slic3r/GUI/AppConfig.cpp3
-rw-r--r--src/slic3r/GUI/Camera.cpp339
-rw-r--r--src/slic3r/GUI/Camera.hpp49
-rw-r--r--src/slic3r/GUI/FirmwareDialog.cpp87
-rw-r--r--src/slic3r/GUI/GLCanvas3D.cpp333
-rw-r--r--src/slic3r/GUI/GLCanvas3D.hpp17
-rw-r--r--src/slic3r/GUI/GLCanvas3DManager.cpp144
-rw-r--r--src/slic3r/GUI/GLCanvas3DManager.hpp46
-rw-r--r--src/slic3r/GUI/GLSelectionRectangle.cpp2
-rw-r--r--src/slic3r/GUI/GLTexture.cpp193
-rw-r--r--src/slic3r/GUI/GLTexture.hpp56
-rw-r--r--src/slic3r/GUI/GLToolbar.cpp23
-rw-r--r--src/slic3r/GUI/GUI_App.cpp6
-rw-r--r--src/slic3r/GUI/GUI_ObjectList.cpp62
-rw-r--r--src/slic3r/GUI/GUI_ObjectManipulation.cpp6
-rw-r--r--src/slic3r/GUI/GUI_Preview.cpp6
-rw-r--r--src/slic3r/GUI/GUI_Preview.hpp1
-rw-r--r--src/slic3r/GUI/Gizmos/GLGizmosManager.cpp15
-rw-r--r--src/slic3r/GUI/ImGuiWrapper.cpp9
-rw-r--r--src/slic3r/GUI/ImGuiWrapper.hpp2
-rw-r--r--src/slic3r/GUI/KBShortcutsDialog.cpp1
-rw-r--r--src/slic3r/GUI/MainFrame.cpp4
-rw-r--r--src/slic3r/GUI/Plater.cpp62
-rw-r--r--src/slic3r/GUI/Plater.hpp5
-rw-r--r--src/slic3r/GUI/ProgressStatusBar.hpp1
-rw-r--r--src/slic3r/GUI/Selection.cpp80
-rw-r--r--src/slic3r/GUI/Selection.hpp3
-rw-r--r--src/slic3r/GUI/wxExtensions.cpp96
-rw-r--r--src/slic3r/GUI/wxExtensions.hpp12
-rw-r--r--src/slic3r/Utils/PresetUpdater.cpp86
-rw-r--r--src/slic3r/Utils/Semver.hpp5
-rw-r--r--src/slic3r/Utils/Serial.hpp3
38 files changed, 1317 insertions, 555 deletions
diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp
index fe3adfd7f..865884c6f 100644
--- a/src/slic3r/Config/Version.cpp
+++ b/src/slic3r/Config/Version.cpp
@@ -192,6 +192,7 @@ size_t Index::load(const boost::filesystem::path &path)
{
m_configs.clear();
m_vendor = path.stem().string();
+ m_path = path;
boost::nowide::ifstream ifs(path.string());
std::string line;
diff --git a/src/slic3r/Config/Version.hpp b/src/slic3r/Config/Version.hpp
index e689286af..560bc29c2 100644
--- a/src/slic3r/Config/Version.hpp
+++ b/src/slic3r/Config/Version.hpp
@@ -72,6 +72,9 @@ public:
// if the index is valid.
const_iterator recommended() const;
+ // Returns the filesystem path from which this index has originally been loaded
+ const boost::filesystem::path& path() const { return m_path; }
+
// Load all vendor specific indices.
// Throws Slic3r::file_parser_error and the standard std file access exceptions.
static std::vector<Index> load_db();
@@ -79,6 +82,7 @@ public:
private:
std::string m_vendor;
std::vector<Version> m_configs;
+ boost::filesystem::path m_path;
};
} // namespace Config
diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp
index 8392e534a..d73e423e0 100644
--- a/src/slic3r/GUI/3DBed.cpp
+++ b/src/slic3r/GUI/3DBed.cpp
@@ -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)
diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp
index e60cdf94e..68a74f61c 100644
--- a/src/slic3r/GUI/3DBed.hpp
+++ b/src/slic3r/GUI/3DBed.hpp
@@ -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;
diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp
index 33ab1f5d1..f9a79f2d8 100644
--- a/src/slic3r/GUI/3DScene.cpp
+++ b/src/slic3r/GUI/3DScene.cpp
@@ -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)
diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp
index 0414a1ed3..d197372ab 100644
--- a/src/slic3r/GUI/3DScene.hpp
+++ b/src/slic3r/GUI/3DScene.hpp
@@ -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();
diff --git a/src/slic3r/GUI/AppConfig.cpp b/src/slic3r/GUI/AppConfig.cpp
index d4970880b..edc9845a1 100644
--- a/src/slic3r/GUI/AppConfig.cpp
+++ b/src/slic3r/GUI/AppConfig.cpp
@@ -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");
diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp
index dd6cbefe1..1f8513ac2 100644
--- a/src/slic3r/GUI/Camera.cpp
+++ b/src/slic3r/GUI/Camera.cpp
@@ -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
diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp
index 1c75ef4b6..bd2541ce2 100644
--- a/src/slic3r/GUI/Camera.hpp
+++ b/src/slic3r/GUI/Camera.hpp
@@ -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
diff --git a/src/slic3r/GUI/FirmwareDialog.cpp b/src/slic3r/GUI/FirmwareDialog.cpp
index b400e27ea..15a09aa71 100644
--- a/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/src/slic3r/GUI/FirmwareDialog.cpp
@@ -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));
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 3d9b24c11..25319e9ce 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -17,6 +17,7 @@
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/PresetBundle.hpp"
#include "slic3r/GUI/Tab.hpp"
+#include "slic3r/GUI/GUI_Preview.hpp"
#include "GUI_App.hpp"
#include "GUI_ObjectList.hpp"
#include "GUI_ObjectManipulation.hpp"
@@ -299,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)
@@ -369,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);
@@ -381,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);
@@ -400,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;
}
@@ -412,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;
@@ -432,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;
}
@@ -727,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;
@@ -788,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();
@@ -862,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));
@@ -878,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;
@@ -902,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));
}
}
@@ -913,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 };
@@ -956,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();
@@ -1145,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));
@@ -1158,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;
@@ -1182,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));
}
}
@@ -1209,6 +1190,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
+wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar)
: m_canvas(canvas)
@@ -1227,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)
@@ -1444,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)
@@ -1465,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)
{
@@ -1473,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;
}
@@ -1548,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)
@@ -1622,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));
@@ -1683,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();
@@ -2309,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)
@@ -2387,17 +2379,29 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case '4': { select_view("rear"); break; }
case '5': { select_view("left"); break; }
case '6': { select_view("right"); break; }
- case '+': { post_event(Event<int>(EVT_GLCANVAS_INCREASE_INSTANCES, +1)); break; }
- case '-': { post_event(Event<int>(EVT_GLCANVAS_INCREASE_INSTANCES, -1)); break; }
+ case '+': {
+ if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
+ post_event(wxKeyEvent(EVT_GLCANVAS_EDIT_COLOR_CHANGE, evt));
+ else
+ post_event(Event<int>(EVT_GLCANVAS_INCREASE_INSTANCES, +1));
+ break; }
+ case '-': {
+ if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
+ post_event(wxKeyEvent(EVT_GLCANVAS_EDIT_COLOR_CHANGE, evt));
+ else
+ post_event(Event<int>(EVT_GLCANVAS_INCREASE_INSTANCES, -1));
+ break; }
case '?': { post_event(SimpleEvent(EVT_GLCANVAS_QUESTION_MARK)); break; }
case 'A':
case 'a': { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; }
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; }
@@ -2472,15 +2476,10 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
else if (keyCode == WXK_LEFT ||
keyCode == WXK_RIGHT ||
keyCode == WXK_UP ||
- keyCode == WXK_DOWN ||
- keyCode == '+' ||
- keyCode == WXK_NUMPAD_ADD ||
- keyCode == '-' ||
- keyCode == 390 ||
- keyCode == WXK_DELETE ||
- keyCode == WXK_BACK )
+ keyCode == WXK_DOWN )
{
- post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt));
+ if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr)
+ post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt));
}
}
}
@@ -2523,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;
}
}
@@ -2534,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)
@@ -3287,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()
@@ -3335,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
@@ -3383,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)
@@ -3501,7 +3488,7 @@ bool GLCanvas3D::_init_toolbar()
item.tooltip = _utf8(L("Copy")) + " [" + GUI::shortkey_ctrl_prefix() + "C]";
item.sprite_id = 4;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); };
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_copy(); };
+ item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); };
if (!m_toolbar.add_item(item))
return false;
@@ -3512,7 +3499,7 @@ bool GLCanvas3D::_init_toolbar()
item.tooltip = _utf8(L("Paste")) + " [" + GUI::shortkey_ctrl_prefix() + "V]";
item.sprite_id = 5;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); };
- item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_paste(); };
+ item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); };
if (!m_toolbar.add_item(item))
return false;
@@ -3614,143 +3601,25 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
// ensures that this canvas is current
_set_current();
- 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;
- }
- }
+ // updates camera
+ m_camera.apply_viewport(0, 0, w, h);
m_dirty = false;
}
-BoundingBoxf3 GLCanvas3D::_max_bounding_box() const
+BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_bed_model) const
{
BoundingBoxf3 bb = volumes_bounding_box();
- bb.merge(m_bed.get_bounding_box());
+ bb.merge(m_bed.get_bounding_box(include_bed_model));
return bb;
}
-void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox)
-{
- // 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
+void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box)
{
- 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()
@@ -3936,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
@@ -3965,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);
}
@@ -4047,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);
@@ -4143,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();
@@ -4211,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
@@ -5704,7 +5599,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)
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 96bb56bd9..d39a910b3 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -125,6 +125,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
+wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
class GLCanvas3D
{
@@ -375,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
@@ -398,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;
};
@@ -442,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;
@@ -598,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(); }
@@ -628,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;
@@ -636,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();
@@ -653,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;
diff --git a/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp
index e409bed0d..4f64b4e87 100644
--- a/src/slic3r/GUI/GLCanvas3DManager.cpp
+++ b/src/slic3r/GUI/GLCanvas3DManager.cpp
@@ -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)
+{
+}
+
+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()
+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();
diff --git a/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp
index 75647e6b2..26c2558d0 100644
--- a/src/slic3r/GUI/GLCanvas3DManager.hpp
+++ b/src/slic3r/GUI/GLCanvas3DManager.hpp
@@ -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);
+ static const GLInfo& get_gl_info() { return s_gl_info; }
+
private:
- CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas);
- CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const;
+ 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);
+ bool init(GLCanvas3D& canvas);
+ static void detect_multisample(int* attribList);
};
} // namespace GUI
diff --git a/src/slic3r/GUI/GLSelectionRectangle.cpp b/src/slic3r/GUI/GLSelectionRectangle.cpp
index 9684bb5ec..327cb1fde 100644
--- a/src/slic3r/GUI/GLSelectionRectangle.cpp
+++ b/src/slic3r/GUI/GLSelectionRectangle.cpp
@@ -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();
diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp
index 2fff0869a..171a5c885 100644
--- a/src/slic3r/GUI/GLTexture.cpp
+++ b/src/slic3r/GUI/GLTexture.cpp
@@ -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);
diff --git a/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp
index e00b3a3be..f4dc05a16 100644
--- a/src/slic3r/GUI/GLTexture.hpp
+++ b/src/slic3r/GUI/GLTexture.hpp
@@ -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
diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp
index 00cbdfec7..f8082ad7e 100644
--- a/src/slic3r/GUI/GLToolbar.cpp
+++ b/src/slic3r/GUI/GLToolbar.cpp
@@ -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;
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 3aada45f3..4f1c3adc8 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -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);
});
}
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index a95b71bcb..d7d1a1af7 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -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
diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
index 310000ecc..372cd79ef 100644
--- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp
+++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp
@@ -499,11 +499,13 @@ void ObjectManipulation::update_if_dirty()
if (selection.requires_uniform_scale()) {
m_lock_bnt->SetLock(true);
- m_lock_bnt->Disable();
+ m_lock_bnt->SetToolTip(_(L("You cann't use non-uniform scaling mode for multiple objects/parts selection")));
+ m_lock_bnt->disable();
}
else {
m_lock_bnt->SetLock(m_uniform_scale);
- m_lock_bnt->Enable();
+ m_lock_bnt->SetToolTip(wxEmptyString);
+ m_lock_bnt->enable();
}
{
diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp
index ec7308382..2f5e10962 100644
--- a/src/slic3r/GUI/GUI_Preview.cpp
+++ b/src/slic3r/GUI/GUI_Preview.cpp
@@ -420,6 +420,12 @@ void Preview::move_double_slider(wxKeyEvent& evt)
m_slider->OnKeyDown(evt);
}
+void Preview::edit_double_slider(wxKeyEvent& evt)
+{
+ if (m_slider)
+ m_slider->OnChar(evt);
+}
+
void Preview::bind_event_handlers()
{
this->Bind(wxEVT_SIZE, &Preview::on_size, this);
diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp
index ed4555f5c..93038b0e5 100644
--- a/src/slic3r/GUI/GUI_Preview.hpp
+++ b/src/slic3r/GUI/GUI_Preview.hpp
@@ -123,6 +123,7 @@ public:
void msw_rescale();
void move_double_slider(wxKeyEvent& evt);
+ void edit_double_slider(wxKeyEvent& evt);
private:
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model);
diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
index 1006d2bd1..c7435636d 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
@@ -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;
diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp
index 67016077c..ca1538bf7 100644
--- a/src/slic3r/GUI/ImGuiWrapper.cpp
+++ b/src/slic3r/GUI/ImGuiWrapper.cpp
@@ -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;
diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp
index 37ef90ff3..0479e4743 100644
--- a/src/slic3r/GUI/ImGuiWrapper.hpp
+++ b/src/slic3r/GUI/ImGuiWrapper.hpp
@@ -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);
diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp
index 347dac13e..1af658ed3 100644
--- a/src/slic3r/GUI/KBShortcutsDialog.cpp
+++ b/src/slic3r/GUI/KBShortcutsDialog.cpp
@@ -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")));
diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp
index 667dcd899..d800f6f38 100644
--- a/src/slic3r/GUI/MainFrame.cpp
+++ b/src/slic3r/GUI/MainFrame.cpp
@@ -505,10 +505,10 @@ void MainFrame::init_menubar()
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C",
_(L("Copy selection to clipboard")), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); },
- menu_icon("copy_menu"), nullptr, [this](){return m_plater->can_copy(); }, this);
+ menu_icon("copy_menu"), nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this);
append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "V",
_(L("Paste clipboard")), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); },
- menu_icon("paste_menu"), nullptr, [this](){return m_plater->can_paste(); }, this);
+ menu_icon("paste_menu"), nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this);
}
// Window menu
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 7f95513ad..f68267cef 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1758,6 +1758,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); });
+ preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_double_slider(evt); });
q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this);
q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this);
@@ -1771,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)
@@ -4183,30 +4187,14 @@ void Plater::update_object_menu() { p->update_object_menu(); }
void Plater::copy_selection_to_clipboard()
{
- p->view3D->get_canvas3d()->get_selection().copy_to_clipboard();
+ if (can_copy_to_clipboard())
+ p->view3D->get_canvas3d()->get_selection().copy_to_clipboard();
}
void Plater::paste_from_clipboard()
{
- p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
-}
-
-bool Plater::can_paste_from_clipboard() const
-{
- const Selection& selection = p->view3D->get_canvas3d()->get_selection();
- const Selection::Clipboard& clipboard = selection.get_clipboard();
- Selection::EMode mode = clipboard.get_mode();
-
- if (clipboard.is_empty())
- return false;
-
- if ((mode == Selection::Volume) && !selection.is_from_single_instance())
- return false;
-
- if ((mode == Selection::Instance) && (selection.get_mode() != Selection::Instance))
- return false;
-
- return true;
+ if (can_paste_from_clipboard())
+ p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
}
void Plater::msw_rescale()
@@ -4233,7 +4221,37 @@ bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); }
bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); }
bool Plater::can_arrange() const { return p->can_arrange(); }
bool Plater::can_layers_editing() const { return p->can_layers_editing(); }
-bool Plater::can_copy() const { return !is_selection_empty(); }
-bool Plater::can_paste() const { return can_paste_from_clipboard(); }
+bool Plater::can_paste_from_clipboard() const
+{
+ const Selection& selection = p->view3D->get_canvas3d()->get_selection();
+ const Selection::Clipboard& clipboard = selection.get_clipboard();
+
+ if (clipboard.is_empty())
+ return false;
+
+ if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) && !clipboard.is_sla_compliant())
+ return false;
+
+ Selection::EMode mode = clipboard.get_mode();
+ if ((mode == Selection::Volume) && !selection.is_from_single_instance())
+ return false;
+
+ if ((mode == Selection::Instance) && (selection.get_mode() != Selection::Instance))
+ return false;
+
+ return true;
+}
+
+bool Plater::can_copy_to_clipboard() const
+{
+ if (is_selection_empty())
+ return false;
+
+ const Selection& selection = p->view3D->get_canvas3d()->get_selection();
+ if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) && !selection.is_sla_compliant())
+ return false;
+
+ return true;
+}
}} // namespace Slic3r::GUI
diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp
index 761bf7a05..2851af654 100644
--- a/src/slic3r/GUI/Plater.hpp
+++ b/src/slic3r/GUI/Plater.hpp
@@ -200,7 +200,6 @@ public:
void copy_selection_to_clipboard();
void paste_from_clipboard();
- bool can_paste_from_clipboard() const;
bool can_delete() const;
bool can_delete_all() const;
@@ -212,8 +211,8 @@ public:
bool can_split_to_volumes() const;
bool can_arrange() const;
bool can_layers_editing() const;
- bool can_copy() const;
- bool can_paste() const;
+ bool can_paste_from_clipboard() const;
+ bool can_copy_to_clipboard() const;
void msw_rescale();
diff --git a/src/slic3r/GUI/ProgressStatusBar.hpp b/src/slic3r/GUI/ProgressStatusBar.hpp
index 7d624af90..413c6ffee 100644
--- a/src/slic3r/GUI/ProgressStatusBar.hpp
+++ b/src/slic3r/GUI/ProgressStatusBar.hpp
@@ -2,6 +2,7 @@
#define PROGRESSSTATUSBAR_HPP
#include <memory>
+#include <string>
#include <functional>
#include <string>
diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp
index 480b59ba0..97168ee04 100644
--- a/src/slic3r/GUI/Selection.cpp
+++ b/src/slic3r/GUI/Selection.cpp
@@ -47,6 +47,26 @@ Selection::VolumeCache::VolumeCache(const Geometry::Transformation& volume_trans
{
}
+bool Selection::Clipboard::is_sla_compliant() const
+{
+ if (m_mode == Selection::Volume)
+ return false;
+
+ for (const ModelObject* o : m_model.objects)
+ {
+ if (o->is_multiparts())
+ return false;
+
+ for (const ModelVolume* v : o->volumes)
+ {
+ if (v->is_modifier())
+ return false;
+ }
+ }
+
+ return true;
+}
+
Selection::Selection()
: m_volumes(nullptr)
, m_model(nullptr)
@@ -385,6 +405,20 @@ bool Selection::is_from_single_object() const
return (0 <= idx) && (idx < 1000);
}
+bool Selection::is_sla_compliant() const
+{
+ if (m_mode == Volume)
+ return false;
+
+ for (unsigned int i : m_list)
+ {
+ if ((*m_volumes)[i]->is_modifier)
+ return false;
+ }
+
+ return true;
+}
+
bool Selection::requires_uniform_scale() const
{
if (is_single_full_instance() || is_single_modifier() || is_single_volume())
@@ -1893,25 +1927,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);
}
}
diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp
index 5da1e477b..802f8d284 100644
--- a/src/slic3r/GUI/Selection.hpp
+++ b/src/slic3r/GUI/Selection.hpp
@@ -152,6 +152,8 @@ public:
void reset() { m_model.clear_objects(); }
bool is_empty() const { return m_model.objects.empty(); }
+ bool is_sla_compliant() const;
+
ModelObject* add_object() { return m_model.add_object(); }
ModelObject* get_object(unsigned int id) { return (id < (unsigned int)m_model.objects.size()) ? m_model.objects[id] : nullptr; }
const ModelObjectPtrs& get_objects() const { return m_model.objects; }
@@ -257,6 +259,7 @@ public:
bool is_mixed() const { return m_type == Mixed; }
bool is_from_single_instance() const { return get_instance_idx() != -1; }
bool is_from_single_object() const;
+ bool is_sla_compliant() const;
bool contains_volume(unsigned int volume_idx) const { return m_list.find(volume_idx) != m_list.end(); }
bool requires_uniform_scale() const;
diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp
index 06cf9b7d5..aed423674 100644
--- a/src/slic3r/GUI/wxExtensions.cpp
+++ b/src/slic3r/GUI/wxExtensions.cpp
@@ -1583,10 +1583,14 @@ DoubleSlider::DoubleSlider( wxWindow *parent,
m_bmp_one_layer_unlock_off = ScalableBitmap(this, "one_layer_unlock_off.png");
m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x;
+ m_bmp_revert = ScalableBitmap(this, "undo");
+ m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x;
+
m_selection = ssUndef;
// slider events
Bind(wxEVT_PAINT, &DoubleSlider::OnPaint, this);
+ Bind(wxEVT_CHAR, &DoubleSlider::OnChar, this);
Bind(wxEVT_LEFT_DOWN, &DoubleSlider::OnLeftDown, this);
Bind(wxEVT_MOTION, &DoubleSlider::OnMotion, this);
Bind(wxEVT_LEFT_UP, &DoubleSlider::OnLeftUp, this);
@@ -1637,6 +1641,9 @@ void DoubleSlider::msw_rescale()
m_bmp_one_layer_unlock_off.msw_rescale();
m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x;
+ m_bmp_revert.msw_rescale();
+ m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x;
+
SLIDER_MARGIN = 4 + Slic3r::GUI::wxGetApp().em_unit();
SetMinSize(get_min_size());
@@ -1873,8 +1880,11 @@ void DoubleSlider::render()
//draw color print ticks
draw_ticks(dc);
- //draw color print ticks
+ //draw lock/unlock
draw_one_layer_icon(dc);
+
+ //draw revert bitmap (if it's shown)
+ draw_revert_icon(dc);
}
void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end)
@@ -2101,6 +2111,24 @@ void DoubleSlider::draw_one_layer_icon(wxDC& dc)
m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim);
}
+void DoubleSlider::draw_revert_icon(wxDC& dc)
+{
+ if (m_ticks.empty())
+ return;
+
+ int width, height;
+ get_size(&width, &height);
+
+ wxCoord x_draw, y_draw;
+ is_horizontal() ? x_draw = width-2 : x_draw = 0.25*SLIDER_MARGIN;
+ is_horizontal() ? y_draw = 0.25*SLIDER_MARGIN: y_draw = height-2;
+
+ dc.DrawBitmap(m_bmp_revert.bmp(), x_draw, y_draw);
+
+ //update rect of the lock/unlock icon
+ m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim);
+}
+
void DoubleSlider::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection)
{
const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, m_thumb_size.y);
@@ -2117,8 +2145,8 @@ int DoubleSlider::get_value_from_position(const wxCoord x, const wxCoord y)
if (is_horizontal())
return int(double(x - SLIDER_MARGIN) / step + 0.5);
- else
- return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5);
+
+ return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5);
}
void DoubleSlider::detect_selected_slider(const wxPoint& pt)
@@ -2168,7 +2196,10 @@ void DoubleSlider::ChangeOneLayerLock()
void DoubleSlider::OnLeftDown(wxMouseEvent& event)
{
+ if (HasCapture())
+ return;
this->CaptureMouse();
+
wxClientDC dc(this);
wxPoint pos = event.GetLogicalPosition(dc);
if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) {
@@ -2178,6 +2209,7 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event)
m_is_left_down = true;
if (is_point_in_rect(pos, m_rect_one_layer_icon)) {
+ // switch on/off one layer mode
m_is_one_layer = !m_is_one_layer;
if (!m_is_one_layer) {
SetLowerValue(m_min_value);
@@ -2186,20 +2218,36 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event)
m_selection == ssLower ? correct_lower_value() : correct_higher_value();
if (!m_selection) m_selection = ssHigher;
}
+ else if (is_point_in_rect(pos, m_rect_revert_icon)) {
+ // discard all color changes
+ SetLowerValue(m_min_value);
+ SetHigherValue(m_max_value);
+
+ m_selection == ssLower ? correct_lower_value() : correct_higher_value();
+ if (!m_selection) m_selection = ssHigher;
+
+ m_ticks.clear();
+ wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
+ }
else
detect_selected_slider(pos);
- if (!m_selection && m_is_enabled_tick_manipulation) {
- const auto tick = is_point_near_tick(pos);
- if (tick >= 0)
+ if (!m_selection) {
+ const int tick_val = is_point_near_tick(pos);
+ /* Set current thumb position to the nearest tick (if it is)
+ * OR to a value corresponding to the mouse click
+ * */
+ const int mouse_val = tick_val >= 0 && m_is_enabled_tick_manipulation ? tick_val :
+ get_value_from_position(pos.x, pos.y);
+ if (mouse_val >= 0)
{
- if (abs(tick - m_lower_value) < abs(tick - m_higher_value)) {
- SetLowerValue(tick);
+ if (abs(mouse_val - m_lower_value) < abs(mouse_val - m_higher_value)) {
+ SetLowerValue(mouse_val);
correct_lower_value();
m_selection = ssLower;
}
else {
- SetHigherValue(tick);
+ SetHigherValue(mouse_val);
correct_higher_value();
m_selection = ssHigher;
}
@@ -2239,9 +2287,13 @@ void DoubleSlider::OnMotion(wxMouseEvent& event)
const wxClientDC dc(this);
const wxPoint pos = event.GetLogicalPosition(dc);
+
m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon);
+ bool is_revert_icon_focused = false;
+
if (!m_is_left_down && !m_is_one_layer) {
m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action);
+ is_revert_icon_focused = !m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon);
}
else if (m_is_left_down || m_is_right_down) {
if (m_selection == ssLower) {
@@ -2261,6 +2313,12 @@ void DoubleSlider::OnMotion(wxMouseEvent& event)
Update();
event.Skip();
+ // Set tooltips with information for each icon
+ const wxString tooltip = m_is_one_layer_icon_focesed ? _(L("One layer mode")) :
+ m_is_action_icon_focesed ? _(L("Add/Del color change")) :
+ is_revert_icon_focused ? _(L("Discard all color changes")) : "";
+ this->SetToolTip(tooltip);
+
if (action)
{
wxCommandEvent e(wxEVT_SCROLL_CHANGED);
@@ -2366,9 +2424,9 @@ void DoubleSlider::OnWheel(wxMouseEvent& event)
void DoubleSlider::OnKeyDown(wxKeyEvent &event)
{
const int key = event.GetKeyCode();
- if (key == '+' || key == WXK_NUMPAD_ADD)
+ if (key == WXK_NUMPAD_ADD)
action_tick(taAdd);
- else if (key == '-' || key == 390 || key == WXK_DELETE || key == WXK_BACK)
+ else if (key == 390 || key == WXK_DELETE || key == WXK_BACK)
action_tick(taDel);
else if (is_horizontal())
{
@@ -2387,6 +2445,8 @@ void DoubleSlider::OnKeyDown(wxKeyEvent &event)
else if (key == WXK_UP || key == WXK_DOWN)
move_current_thumb(key == WXK_UP);
}
+
+ event.Skip(); // !Needed to have EVT_CHAR generated as well
}
void DoubleSlider::OnKeyUp(wxKeyEvent &event)
@@ -2398,9 +2458,20 @@ void DoubleSlider::OnKeyUp(wxKeyEvent &event)
event.Skip();
}
+void DoubleSlider::OnChar(wxKeyEvent& event)
+{
+ const int key = event.GetKeyCode();
+ if (key == '+')
+ action_tick(taAdd);
+ else if (key == '-')
+ action_tick(taDel);
+}
+
void DoubleSlider::OnRightDown(wxMouseEvent& event)
{
+ if (HasCapture()) return;
this->CaptureMouse();
+
const wxClientDC dc(this);
detect_selected_slider(event.GetLogicalPosition(dc));
if (!m_selection)
@@ -2460,6 +2531,9 @@ LockButton::LockButton( wxWindow *parent,
void LockButton::OnButton(wxCommandEvent& event)
{
+ if (m_disabled)
+ return;
+
m_is_pushed = !m_is_pushed;
enter_button(true);
diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp
index 78fb7be55..081c0d48f 100644
--- a/src/slic3r/GUI/wxExtensions.hpp
+++ b/src/slic3r/GUI/wxExtensions.hpp
@@ -727,6 +727,7 @@ public:
void OnWheel(wxMouseEvent& event);
void OnKeyDown(wxKeyEvent &event);
void OnKeyUp(wxKeyEvent &event);
+ void OnChar(wxKeyEvent &event);
void OnRightDown(wxMouseEvent& event);
void OnRightUp(wxMouseEvent& event);
@@ -741,6 +742,7 @@ protected:
void draw_ticks(wxDC& dc);
void draw_colored_band(wxDC& dc);
void draw_one_layer_icon(wxDC& dc);
+ void draw_revert_icon(wxDC& dc);
void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection);
void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection);
void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const;
@@ -782,6 +784,7 @@ private:
ScalableBitmap m_bmp_one_layer_lock_off;
ScalableBitmap m_bmp_one_layer_unlock_on;
ScalableBitmap m_bmp_one_layer_unlock_off;
+ ScalableBitmap m_bmp_revert;
SelectedSlider m_selection;
bool m_is_left_down = false;
bool m_is_right_down = false;
@@ -795,9 +798,11 @@ private:
wxRect m_rect_higher_thumb;
wxRect m_rect_tick_action;
wxRect m_rect_one_layer_icon;
+ wxRect m_rect_revert_icon;
wxSize m_thumb_size;
int m_tick_icon_dim;
int m_lock_icon_dim;
+ int m_revert_icon_dim;
long m_style;
float m_label_koef = 1.0;
@@ -837,9 +842,13 @@ public:
void OnEnterBtn(wxMouseEvent& event) { enter_button(true); event.Skip(); }
void OnLeaveBtn(wxMouseEvent& event) { enter_button(false); event.Skip(); }
- bool IsLocked() const { return m_is_pushed; }
+ bool IsLocked() const { return m_is_pushed; }
void SetLock(bool lock);
+ // create its own Enable/Disable functions to not really disabled button because of tooltip enabling
+ void enable() { m_disabled = false; }
+ void disable() { m_disabled = true; }
+
void msw_rescale();
protected:
@@ -847,6 +856,7 @@ protected:
private:
bool m_is_pushed = false;
+ bool m_disabled = false;
ScalableBitmap m_bmp_lock_on;
ScalableBitmap m_bmp_lock_off;
diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp
index f34cd8db1..8c3ced31a 100644
--- a/src/slic3r/Utils/PresetUpdater.cpp
+++ b/src/slic3r/Utils/PresetUpdater.cpp
@@ -4,6 +4,7 @@
#include <thread>
#include <unordered_map>
#include <ostream>
+#include <utility>
#include <stdexcept>
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
@@ -45,10 +46,25 @@ static const char *INDEX_FILENAME = "index.idx";
static const char *TMP_EXTENSION = ".download";
+void copy_file_fix(const fs::path &source, const fs::path &target)
+{
+ static const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read; // aka 644
+
+ BOOST_LOG_TRIVIAL(debug) << boost::format("PresetUpdater: Copying %1% -> %2%") % source % target;
+
+ // Make sure the file has correct permission both before and after we copy over it
+ if (fs::exists(target)) {
+ fs::permissions(target, perms);
+ }
+ fs::copy_file(source, target, fs::copy_option::overwrite_if_exists);
+ fs::permissions(target, perms);
+}
+
struct Update
{
fs::path source;
fs::path target;
+
Version version;
std::string vendor;
std::string changelog_url;
@@ -61,7 +77,13 @@ struct Update
, changelog_url(std::move(changelog_url))
{}
- friend std::ostream& operator<<(std::ostream& os , const Update &self) {
+ void install() const
+ {
+ copy_file_fix(source, target);
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const Update &self)
+ {
os << "Update(" << self.source.string() << " -> " << self.target.string() << ')';
return os;
}
@@ -115,7 +137,6 @@ struct PresetUpdater::priv
bool enabled_version_check;
bool enabled_config_update;
std::string version_check_url;
- bool had_config_update;
fs::path cache_path;
fs::path rsrc_path;
@@ -135,13 +156,10 @@ struct PresetUpdater::priv
void check_install_indices() const;
Updates get_config_updates() const;
void perform_updates(Updates &&updates, bool snapshot = true) const;
-
- static void copy_file(const fs::path &from, const fs::path &to);
};
PresetUpdater::priv::priv()
: ver_slic3r(get_slic3r_version())
- , had_config_update(false)
, cache_path(fs::path(Slic3r::data_dir()) / "cache")
, rsrc_path(fs::path(resources_dir()) / "profiles")
, vendor_path(fs::path(Slic3r::data_dir()) / "vendor")
@@ -273,7 +291,7 @@ void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors)
try {
new_index.load(idx_path_temp);
} catch (const std::exception & /* err */) {
- BOOST_LOG_TRIVIAL(error) << boost::format("Failed loading a downloaded index %1% for vendor %2%: invalid index?") % idx_path_temp % vendor.name;
+ BOOST_LOG_TRIVIAL(error) << boost::format("Could not load downloaded index %1% for vendor %2%: invalid index?") % idx_path_temp % vendor.name;
continue;
}
if (new_index.version() < index.version()) {
@@ -323,7 +341,7 @@ void PresetUpdater::priv::check_install_indices() const
if (! fs::exists(path_in_cache)) {
BOOST_LOG_TRIVIAL(info) << "Install index from resources: " << path.filename();
- copy_file(path, path_in_cache);
+ copy_file_fix(path, path_in_cache);
} else {
Index idx_rsrc, idx_cache;
idx_rsrc.load(path);
@@ -331,7 +349,7 @@ void PresetUpdater::priv::check_install_indices() const
if (idx_cache.version() < idx_rsrc.version()) {
BOOST_LOG_TRIVIAL(info) << "Update index from resources: " << path.filename();
- copy_file(path, path_in_cache);
+ copy_file_fix(path, path_in_cache);
}
}
}
@@ -346,6 +364,7 @@ Updates PresetUpdater::priv::get_config_updates() const
for (const auto idx : index_db) {
auto bundle_path = vendor_path / (idx.vendor() + ".ini");
+ auto bundle_path_idx = vendor_path / idx.path().filename();
if (! fs::exists(bundle_path)) {
BOOST_LOG_TRIVIAL(info) << "Bundle not present for index, skipping: " << idx.vendor();
@@ -360,8 +379,31 @@ Updates PresetUpdater::priv::get_config_updates() const
const auto recommended = idx.recommended();
if (recommended == idx.end()) {
BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % idx.vendor();
+ // XXX: what should be done here?
+ continue;
+ }
+
+ // Load 'installed' idx, if any.
+ // 'Installed' indices are kept alongside the bundle in the `vendor` subdir
+ // for bookkeeping to remember a cancelled update and not offer it again.
+ if (fs::exists(bundle_path_idx)) {
+ Index existing_idx;
+ try {
+ existing_idx.load(bundle_path_idx);
+
+ const auto existing_recommended = existing_idx.recommended();
+ if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) {
+ // The user has already seen (and presumably rejected) this update
+ BOOST_LOG_TRIVIAL(info) << boost::format("Downloaded index for `%1%` is the same as installed one, not offering an update.") % idx.vendor();
+ continue;
+ }
+ } catch (const std::exception & /* err */) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("Could nto load installed index %1%") % bundle_path_idx;
+ }
}
+ copy_file_fix(idx.path(), bundle_path_idx);
+
const auto ver_current = idx.find(vp.config_version);
const bool ver_current_found = ver_current != idx.end();
if (! ver_current_found) {
@@ -453,10 +495,10 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
for (const auto &update : updates.updates) {
BOOST_LOG_TRIVIAL(info) << '\t' << update;
- copy_file(update.source, update.target);
+ update.install();
PresetBundle bundle;
- bundle.load_configbundle(update.target.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM);
+ bundle.load_configbundle(update.source.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM);
BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% conflicting presets")
% (bundle.prints.size() + bundle.filaments.size() + bundle.printers.size());
@@ -491,19 +533,6 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
}
}
-void PresetUpdater::priv::copy_file(const fs::path &source, const fs::path &target)
-{
- static const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read; // aka 644
-
- // Make sure the file has correct permission both before and after we copy over it
- if (fs::exists(target)) {
- fs::permissions(target, perms);
- }
- fs::copy_file(source, target, fs::copy_option::overwrite_if_exists);
- fs::permissions(target, perms);
-}
-
-
PresetUpdater::PresetUpdater() :
p(new priv())
{}
@@ -542,11 +571,6 @@ void PresetUpdater::slic3r_update_notify()
{
if (! p->enabled_version_check) { return; }
- if (p->had_config_update) {
- BOOST_LOG_TRIVIAL(info) << "New Slic3r version available, but there was a configuration update, notification won't be displayed";
- return;
- }
-
auto* app_config = GUI::wxGetApp().app_config;
const auto ver_online_str = app_config->get("version_online");
const auto ver_online = Semver::parse(ver_online_str);
@@ -594,8 +618,6 @@ PresetUpdater::UpdateResult PresetUpdater::config_update() const
incompats_map.emplace(std::make_pair(incompat.vendor, std::move(restrictions)));
}
- p->had_config_update = true; // This needs to be done before a dialog is shown because of OnIdle() + CallAfter() in Perl
-
GUI::MsgDataIncompatible dlg(std::move(incompats_map));
const auto res = dlg.ShowModal();
if (res == wxID_REPLACE) {
@@ -620,8 +642,6 @@ PresetUpdater::UpdateResult PresetUpdater::config_update() const
updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url));
}
- p->had_config_update = true; // Ditto, see above
-
GUI::MsgUpdateConfig dlg(updates_msg);
const auto res = dlg.ShowModal();
@@ -631,7 +651,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update() const
// Reload global configuration
auto *app_config = GUI::wxGetApp().app_config;
- GUI::wxGetApp().preset_bundle->load_presets(*app_config);
+ GUI::wxGetApp().preset_bundle->load_presets(*app_config);
GUI::wxGetApp().load_current_presets();
return R_UPDATE_INSTALLED;
} else {
diff --git a/src/slic3r/Utils/Semver.hpp b/src/slic3r/Utils/Semver.hpp
index 2fb4e3f4b..a755becaa 100644
--- a/src/slic3r/Utils/Semver.hpp
+++ b/src/slic3r/Utils/Semver.hpp
@@ -137,6 +137,11 @@ public:
Semver operator-(const Minor &b) const { Semver res(*this); return res -= b; }
Semver operator-(const Patch &b) const { Semver res(*this); return res -= b; }
+ // Stream output
+ friend std::ostream& operator<<(std::ostream& os, const Semver &self) {
+ os << self.to_string();
+ return os;
+ }
private:
semver_t ver;
diff --git a/src/slic3r/Utils/Serial.hpp b/src/slic3r/Utils/Serial.hpp
index e4a28de09..67d64b4ec 100644
--- a/src/slic3r/Utils/Serial.hpp
+++ b/src/slic3r/Utils/Serial.hpp
@@ -17,6 +17,9 @@ struct SerialPortInfo {
std::string friendly_name;
bool is_printer = false;
+ SerialPortInfo() {}
+ SerialPortInfo(std::string port) : port(port), friendly_name(std::move(port)) {}
+
bool id_match(unsigned id_vendor, unsigned id_product) const { return id_vendor == this->id_vendor && id_product == this->id_product; }
};