mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-25 15:44:12 -06:00
Merge remote-tracking branch 'origin/master' into ys_bugfixing
This commit is contained in:
commit
0f35a95d75
1134 changed files with 2647 additions and 613 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -300,22 +300,10 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
|
|||
const Rect& bar_rect = get_bar_rect_viewport(canvas);
|
||||
const Rect& reset_rect = get_reset_rect_viewport(canvas);
|
||||
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
|
||||
// The viewport and camera are set to complete view and glOrtho(-$x / 2, $x / 2, -$y / 2, $y / 2, -$depth, $depth),
|
||||
// where x, y is the window size divided by $self->_zoom.
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glLoadIdentity());
|
||||
|
||||
_render_tooltip_texture(canvas, bar_rect, reset_rect);
|
||||
_render_reset_texture(reset_rect);
|
||||
_render_active_object_annotations(canvas, bar_rect);
|
||||
_render_profile(bar_rect);
|
||||
|
||||
// Revert the matrices.
|
||||
glsafe(::glPopMatrix());
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
}
|
||||
|
||||
float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas)
|
||||
|
@ -370,7 +358,7 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
|
|||
float half_w = 0.5f * (float)cnv_size.get_width();
|
||||
float half_h = 0.5f * (float)cnv_size.get_height();
|
||||
|
||||
float zoom = canvas.get_camera().zoom;
|
||||
float zoom = (float)canvas.get_camera().get_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
|
||||
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom);
|
||||
|
@ -382,7 +370,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas
|
|||
float half_w = 0.5f * (float)cnv_size.get_width();
|
||||
float half_h = 0.5f * (float)cnv_size.get_height();
|
||||
|
||||
float zoom = canvas.get_camera().zoom;
|
||||
float zoom = (float)canvas.get_camera().get_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
|
||||
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
|
||||
|
@ -401,7 +389,7 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas
|
|||
if (m_tooltip_texture.get_id() == 0)
|
||||
{
|
||||
std::string filename = resources_dir() + "/icons/variable_layer_height_tooltip.png";
|
||||
if (!m_tooltip_texture.load_from_file(filename, false))
|
||||
if (!m_tooltip_texture.load_from_file(filename, false, true))
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -413,7 +401,7 @@ void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas
|
|||
const float width = (float)m_tooltip_texture.get_width() * scale;
|
||||
const float height = (float)m_tooltip_texture.get_height() * scale;
|
||||
|
||||
float zoom = canvas.get_camera().zoom;
|
||||
float zoom = (float)canvas.get_camera().get_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
float gap = 10.0f * inv_zoom;
|
||||
|
||||
|
@ -433,7 +421,7 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co
|
|||
if (m_reset_texture.get_id() == 0)
|
||||
{
|
||||
std::string filename = resources_dir() + "/icons/variable_layer_height_reset.png";
|
||||
if (!m_reset_texture.load_from_file(filename, false))
|
||||
if (!m_reset_texture.load_from_file(filename, false, true))
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -728,7 +716,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
|
|||
}
|
||||
}
|
||||
|
||||
_generate(text, canvas, red_colored); // GUI::GLTexture::reset() is called at the beginning of generate(...)
|
||||
generate(text, canvas, true, red_colored); // GUI::GLTexture::reset() is called at the beginning of generate(...)
|
||||
|
||||
// save information for rescaling
|
||||
m_msg_text = text;
|
||||
|
@ -789,7 +777,7 @@ static void msw_disable_cleartype(wxFont &font)
|
|||
}
|
||||
#endif /* __WXMSW__ */
|
||||
|
||||
bool GLCanvas3D::WarningTexture::_generate(const std::string& msg_utf8, const GLCanvas3D& canvas, const bool red_colored/* = false*/)
|
||||
bool GLCanvas3D::WarningTexture::generate(const std::string& msg_utf8, const GLCanvas3D& canvas, bool compress, bool red_colored/* = false*/)
|
||||
{
|
||||
reset();
|
||||
|
||||
|
@ -863,7 +851,10 @@ bool GLCanvas3D::WarningTexture::_generate(const std::string& msg_utf8, const GL
|
|||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &m_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
if (compress && GLEW_EXT_texture_compression_s3tc)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
|
@ -879,12 +870,8 @@ void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const
|
|||
|
||||
if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0))
|
||||
{
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glLoadIdentity());
|
||||
|
||||
const Size& cnv_size = canvas.get_canvas_size();
|
||||
float zoom = canvas.get_camera().zoom;
|
||||
float zoom = (float)canvas.get_camera().get_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
float left = (-0.5f * (float)m_original_width) * inv_zoom;
|
||||
float top = (-0.5f * (float)cnv_size.get_height() + (float)m_original_height + 2.0f) * inv_zoom;
|
||||
|
@ -903,9 +890,6 @@ void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const
|
|||
uvs.right_top = { uv_right, uv_top };
|
||||
|
||||
GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs);
|
||||
|
||||
glsafe(::glPopMatrix());
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -914,7 +898,7 @@ void GLCanvas3D::WarningTexture::msw_rescale(const GLCanvas3D& canvas)
|
|||
if (m_msg_text.empty())
|
||||
return;
|
||||
|
||||
_generate(m_msg_text, canvas, m_is_colored_red);
|
||||
generate(m_msg_text, canvas, true, m_is_colored_red);
|
||||
}
|
||||
|
||||
const unsigned char GLCanvas3D::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
|
||||
|
@ -957,7 +941,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePrevie
|
|||
}
|
||||
}
|
||||
|
||||
bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas)
|
||||
bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas, bool compress)
|
||||
{
|
||||
reset();
|
||||
|
||||
|
@ -1146,7 +1130,10 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
|
|||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glGenTextures(1, &m_id));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id));
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
if (compress && GLEW_EXT_texture_compression_s3tc)
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
else
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
|
@ -1159,12 +1146,8 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const
|
|||
{
|
||||
if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0))
|
||||
{
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glLoadIdentity());
|
||||
|
||||
const Size& cnv_size = canvas.get_canvas_size();
|
||||
float zoom = canvas.get_camera().zoom;
|
||||
float zoom = (float)canvas.get_camera().get_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
float left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom;
|
||||
float top = (0.5f * (float)cnv_size.get_height()) * inv_zoom;
|
||||
|
@ -1183,9 +1166,6 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const
|
|||
uvs.right_top = { uv_right, uv_top };
|
||||
|
||||
GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs);
|
||||
|
||||
glsafe(::glPopMatrix());
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1229,6 +1209,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
|
|||
#endif // ENABLE_SVG_ICONS
|
||||
, m_use_clipping_planes(false)
|
||||
, m_sidebar_field("")
|
||||
, m_keep_dirty(false)
|
||||
, m_config(nullptr)
|
||||
, m_process(nullptr)
|
||||
, m_model(nullptr)
|
||||
|
@ -1446,6 +1427,8 @@ void GLCanvas3D::bed_shape_changed()
|
|||
m_camera.set_scene_box(scene_bounding_box());
|
||||
m_camera.requires_zoom_to_bed = true;
|
||||
m_dirty = true;
|
||||
if (m_bed.is_prusa())
|
||||
start_keeping_dirty();
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_color_by(const std::string& value)
|
||||
|
@ -1467,7 +1450,7 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const
|
|||
BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3 bb = volumes_bounding_box();
|
||||
bb.merge(m_bed.get_bounding_box());
|
||||
bb.merge(m_bed.get_bounding_box(false));
|
||||
|
||||
if (m_config != nullptr)
|
||||
{
|
||||
|
@ -1475,6 +1458,7 @@ BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
|
|||
bb.min(2) = std::min(bb.min(2), -h);
|
||||
bb.max(2) = std::max(bb.max(2), h);
|
||||
}
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
|
@ -1550,20 +1534,20 @@ void GLCanvas3D::allow_multisample(bool allow)
|
|||
|
||||
void GLCanvas3D::zoom_to_bed()
|
||||
{
|
||||
_zoom_to_bounding_box(m_bed.get_bounding_box());
|
||||
_zoom_to_box(m_bed.get_bounding_box(false));
|
||||
}
|
||||
|
||||
void GLCanvas3D::zoom_to_volumes()
|
||||
{
|
||||
m_apply_zoom_to_volumes_filter = true;
|
||||
_zoom_to_bounding_box(volumes_bounding_box());
|
||||
_zoom_to_box(volumes_bounding_box());
|
||||
m_apply_zoom_to_volumes_filter = false;
|
||||
}
|
||||
|
||||
void GLCanvas3D::zoom_to_selection()
|
||||
{
|
||||
if (!m_selection.is_empty())
|
||||
_zoom_to_bounding_box(m_selection.get_bounding_box());
|
||||
_zoom_to_box(m_selection.get_bounding_box());
|
||||
}
|
||||
|
||||
void GLCanvas3D::select_view(const std::string& direction)
|
||||
|
@ -1624,6 +1608,7 @@ void GLCanvas3D::render()
|
|||
}
|
||||
|
||||
m_camera.apply_view_matrix();
|
||||
m_camera.apply_projection(_max_bounding_box(true));
|
||||
|
||||
GLfloat position_cam[4] = { 1.0f, 0.0f, 1.0f, 0.0f };
|
||||
glsafe(::glLightfv(GL_LIGHT1, GL_POSITION, position_cam));
|
||||
|
@ -1685,29 +1670,31 @@ void GLCanvas3D::render()
|
|||
m_rectangle_selection.render(*this);
|
||||
|
||||
// draw overlays
|
||||
_render_gizmos_overlay();
|
||||
_render_warning_texture();
|
||||
_render_legend_texture();
|
||||
#if !ENABLE_SVG_ICONS
|
||||
_resize_toolbars();
|
||||
#endif // !ENABLE_SVG_ICONS
|
||||
_render_toolbar();
|
||||
_render_view_toolbar();
|
||||
if ((m_layers_editing.last_object_id >= 0) && (m_layers_editing.object_max_z() > 0.0f))
|
||||
m_layers_editing.render_overlay(*this);
|
||||
_render_overlays();
|
||||
|
||||
#if ENABLE_RENDER_STATISTICS
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
imgui.set_next_window_bg_alpha(0.5f);
|
||||
imgui.begin(std::string("Render statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||
imgui.text(_(L("Last frame")) +": ");
|
||||
imgui.text("Last frame: ");
|
||||
ImGui::SameLine();
|
||||
imgui.text(std::to_string(m_render_stats.last_frame));
|
||||
ImGui::SameLine();
|
||||
imgui.text(" "+_(L("ms")));
|
||||
imgui.text(" ms");
|
||||
ImGui::Separator();
|
||||
imgui.text("Compressed textures: ");
|
||||
ImGui::SameLine();
|
||||
imgui.text(GLCanvas3DManager::are_compressed_textures_supported() ? "supported" : "not supported");
|
||||
imgui.text("Max texture size: ");
|
||||
ImGui::SameLine();
|
||||
imgui.text(std::to_string(GLCanvas3DManager::get_gl_info().get_max_tex_size()));
|
||||
imgui.end();
|
||||
#endif // ENABLE_RENDER_STATISTICS
|
||||
|
||||
#if ENABLE_CAMERA_STATISTICS
|
||||
m_camera.debug_render();
|
||||
#endif // ENABLE_CAMERA_STATISTICS
|
||||
|
||||
wxGetApp().imgui()->render();
|
||||
|
||||
m_canvas->SwapBuffers();
|
||||
|
@ -2311,6 +2298,9 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
|||
return;
|
||||
|
||||
_refresh_if_shown_on_screen();
|
||||
|
||||
if (m_keep_dirty)
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
|
@ -2407,9 +2397,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
|||
case 'B':
|
||||
case 'b': { zoom_to_bed(); break; }
|
||||
case 'I':
|
||||
case 'i': { set_camera_zoom(1.0f); break; }
|
||||
case 'i': { set_camera_zoom(1.0); break; }
|
||||
case 'K':
|
||||
case 'k': { m_camera.select_next_type(); m_dirty = true; break; }
|
||||
case 'O':
|
||||
case 'o': { set_camera_zoom(-1.0f); break; }
|
||||
case 'o': { set_camera_zoom(-1.0); break; }
|
||||
case 'Z':
|
||||
case 'z': { m_selection.is_empty() ? zoom_to_volumes() : zoom_to_selection(); break; }
|
||||
default: { evt.Skip(); break; }
|
||||
|
@ -2530,7 +2522,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
|||
m_layers_editing.band_width = std::max(std::min(m_layers_editing.band_width * (1.0f + 0.1f * (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta()), 10.0f), 1.5f);
|
||||
if (m_canvas != nullptr)
|
||||
m_canvas->Refresh();
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2541,8 +2533,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
|||
return;
|
||||
|
||||
// Calculate the zoom delta and apply it to the current zoom factor
|
||||
float zoom = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta();
|
||||
set_camera_zoom(zoom);
|
||||
set_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta());
|
||||
}
|
||||
|
||||
void GLCanvas3D::on_timer(wxTimerEvent& evt)
|
||||
|
@ -3294,21 +3285,11 @@ void GLCanvas3D::do_mirror()
|
|||
post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_camera_zoom(float zoom)
|
||||
void GLCanvas3D::set_camera_zoom(double zoom)
|
||||
{
|
||||
zoom = std::max(std::min(zoom, 4.0f), -4.0f) / 10.0f;
|
||||
zoom = m_camera.zoom / (1.0f - zoom);
|
||||
|
||||
// Don't allow to zoom too far outside the scene.
|
||||
float zoom_min = _get_zoom_to_bounding_box_factor(_max_bounding_box());
|
||||
if (zoom_min > 0.0f)
|
||||
zoom = std::max(zoom, zoom_min * 0.7f);
|
||||
|
||||
// Don't allow to zoom too close to the scene.
|
||||
zoom = std::min(zoom, 100.0f);
|
||||
|
||||
m_camera.zoom = zoom;
|
||||
_refresh_if_shown_on_screen();
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
m_camera.set_zoom(zoom, _max_bounding_box(false), cnv_size.get_width(), cnv_size.get_height());
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::update_gizmos_on_off_state()
|
||||
|
@ -3342,8 +3323,7 @@ void GLCanvas3D::update_ui_from_settings()
|
|||
if (new_scaling != orig_scaling) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "GLCanvas3D: Scaling factor: " << new_scaling;
|
||||
|
||||
m_camera.zoom /= orig_scaling;
|
||||
m_camera.zoom *= new_scaling;
|
||||
m_camera.set_zoom(m_camera.get_zoom() * new_scaling / orig_scaling);
|
||||
_refresh_if_shown_on_screen();
|
||||
}
|
||||
#endif
|
||||
|
@ -3390,7 +3370,7 @@ Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos)
|
|||
|
||||
double GLCanvas3D::get_size_proportional_to_max_bed_size(double factor) const
|
||||
{
|
||||
return factor * m_bed.get_bounding_box().max_size();
|
||||
return factor * m_bed.get_bounding_box(false).max_size();
|
||||
}
|
||||
|
||||
void GLCanvas3D::set_cursor(ECursorType type)
|
||||
|
@ -3621,143 +3601,25 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
|
|||
|
||||
// ensures that this canvas is current
|
||||
_set_current();
|
||||
|
||||
// updates camera
|
||||
m_camera.apply_viewport(0, 0, w, h);
|
||||
|
||||
const BoundingBoxf3& bbox = _max_bounding_box();
|
||||
|
||||
switch (m_camera.type)
|
||||
{
|
||||
case Camera::Ortho:
|
||||
{
|
||||
float w2 = w;
|
||||
float h2 = h;
|
||||
float two_zoom = 2.0f * m_camera.zoom;
|
||||
if (two_zoom != 0.0f)
|
||||
{
|
||||
float inv_two_zoom = 1.0f / two_zoom;
|
||||
w2 *= inv_two_zoom;
|
||||
h2 *= inv_two_zoom;
|
||||
}
|
||||
|
||||
// FIXME: calculate a tighter value for depth will improve z-fighting
|
||||
// Set at least some minimum depth in case the bounding box is empty to avoid an OpenGL driver error.
|
||||
float depth = std::max(1.f, 5.0f * (float)bbox.max_size());
|
||||
m_camera.apply_ortho_projection(-w2, w2, -h2, h2, -depth, depth);
|
||||
|
||||
break;
|
||||
}
|
||||
// case Camera::Perspective:
|
||||
// {
|
||||
// float bbox_r = (float)bbox.radius();
|
||||
// float fov = PI * 45.0f / 180.0f;
|
||||
// float fov_tan = tan(0.5f * fov);
|
||||
// float cam_distance = 0.5f * bbox_r / fov_tan;
|
||||
// m_camera.distance = cam_distance;
|
||||
//
|
||||
// float nr = cam_distance - bbox_r * 1.1f;
|
||||
// float fr = cam_distance + bbox_r * 1.1f;
|
||||
// if (nr < 1.0f)
|
||||
// nr = 1.0f;
|
||||
//
|
||||
// if (fr < nr + 1.0f)
|
||||
// fr = nr + 1.0f;
|
||||
//
|
||||
// float h2 = fov_tan * nr;
|
||||
// float w2 = h2 * w / h;
|
||||
// ::glFrustum(-w2, w2, -h2, h2, nr, fr);
|
||||
//
|
||||
// break;
|
||||
// }
|
||||
default:
|
||||
{
|
||||
throw std::runtime_error("Invalid camera type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
BoundingBoxf3 GLCanvas3D::_max_bounding_box() const
|
||||
BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_bed_model) const
|
||||
{
|
||||
BoundingBoxf3 bb = volumes_bounding_box();
|
||||
bb.merge(m_bed.get_bounding_box());
|
||||
bb.merge(m_bed.get_bounding_box(include_bed_model));
|
||||
return bb;
|
||||
}
|
||||
|
||||
void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox)
|
||||
void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box)
|
||||
{
|
||||
// Calculate the zoom factor needed to adjust viewport to bounding box.
|
||||
float zoom = _get_zoom_to_bounding_box_factor(bbox);
|
||||
if (zoom > 0.0f)
|
||||
{
|
||||
m_camera.zoom = zoom;
|
||||
// center view around bounding box center
|
||||
m_camera.set_target(bbox.center());
|
||||
m_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const
|
||||
{
|
||||
float max_bb_size = bbox.max_size();
|
||||
if (max_bb_size == 0.0f)
|
||||
return -1.0f;
|
||||
|
||||
// project the bbox vertices on a plane perpendicular to the camera forward axis
|
||||
// then calculates the vertices coordinate on this plane along the camera xy axes
|
||||
|
||||
// we need the view matrix, we let opengl calculate it (same as done in render())
|
||||
m_camera.apply_view_matrix();
|
||||
|
||||
Vec3d right = m_camera.get_dir_right();
|
||||
Vec3d up = m_camera.get_dir_up();
|
||||
Vec3d forward = m_camera.get_dir_forward();
|
||||
|
||||
Vec3d bb_min = bbox.min;
|
||||
Vec3d bb_max = bbox.max;
|
||||
Vec3d bb_center = bbox.center();
|
||||
|
||||
// bbox vertices in world space
|
||||
std::vector<Vec3d> vertices;
|
||||
vertices.reserve(8);
|
||||
vertices.push_back(bb_min);
|
||||
vertices.emplace_back(bb_max(0), bb_min(1), bb_min(2));
|
||||
vertices.emplace_back(bb_max(0), bb_max(1), bb_min(2));
|
||||
vertices.emplace_back(bb_min(0), bb_max(1), bb_min(2));
|
||||
vertices.emplace_back(bb_min(0), bb_min(1), bb_max(2));
|
||||
vertices.emplace_back(bb_max(0), bb_min(1), bb_max(2));
|
||||
vertices.push_back(bb_max);
|
||||
vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2));
|
||||
|
||||
double max_x = 0.0;
|
||||
double max_y = 0.0;
|
||||
|
||||
// margin factor to give some empty space around the bbox
|
||||
double margin_factor = 1.25;
|
||||
|
||||
for (const Vec3d& v : vertices)
|
||||
{
|
||||
// project vertex on the plane perpendicular to camera forward axis
|
||||
Vec3d pos(v(0) - bb_center(0), v(1) - bb_center(1), v(2) - bb_center(2));
|
||||
Vec3d proj_on_plane = pos - pos.dot(forward) * forward;
|
||||
|
||||
// calculates vertex coordinate along camera xy axes
|
||||
double x_on_plane = proj_on_plane.dot(right);
|
||||
double y_on_plane = proj_on_plane.dot(up);
|
||||
|
||||
max_x = std::max(max_x, margin_factor * std::abs(x_on_plane));
|
||||
max_y = std::max(max_y, margin_factor * std::abs(y_on_plane));
|
||||
}
|
||||
|
||||
if ((max_x == 0.0) || (max_y == 0.0))
|
||||
return -1.0f;
|
||||
|
||||
max_x *= 2.0;
|
||||
max_y *= 2.0;
|
||||
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
return (float)std::min((double)cnv_size.get_width() / max_x, (double)cnv_size.get_height() / max_y);
|
||||
m_camera.zoom_to_box(box, cnv_size.get_width(), cnv_size.get_height());
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void GLCanvas3D::_refresh_if_shown_on_screen()
|
||||
|
@ -3943,7 +3805,7 @@ void GLCanvas3D::_render_bed(float theta) const
|
|||
#if ENABLE_RETINA_GL
|
||||
scale_factor = m_retina_helper->get_scale_factor();
|
||||
#endif // ENABLE_RETINA_GL
|
||||
m_bed.render(theta, m_use_VBOs, scale_factor);
|
||||
m_bed.render(const_cast<GLCanvas3D*>(this), theta, m_use_VBOs, scale_factor);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_axes() const
|
||||
|
@ -3972,7 +3834,7 @@ void GLCanvas3D::_render_objects() const
|
|||
|
||||
if (m_config != nullptr)
|
||||
{
|
||||
const BoundingBoxf3& bed_bb = m_bed.get_bounding_box();
|
||||
const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(false);
|
||||
m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height"));
|
||||
m_volumes.check_outside_state(m_config, nullptr);
|
||||
}
|
||||
|
@ -4054,6 +3916,32 @@ void GLCanvas3D::_render_selection_center() const
|
|||
}
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
|
||||
void GLCanvas3D::_render_overlays() const
|
||||
{
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glLoadIdentity());
|
||||
// ensure that the textures are renderered inside the frustrum
|
||||
glsafe(::glTranslated(0.0, 0.0, -(m_camera.get_near_z() + 0.5)));
|
||||
// ensure that the overlay fits the frustrum near z plane
|
||||
double gui_scale = m_camera.get_gui_scale();
|
||||
glsafe(::glScaled(gui_scale, gui_scale, 1.0));
|
||||
|
||||
_render_gizmos_overlay();
|
||||
_render_warning_texture();
|
||||
_render_legend_texture();
|
||||
#if !ENABLE_SVG_ICONS
|
||||
_resize_toolbars();
|
||||
#endif // !ENABLE_SVG_ICONS
|
||||
_render_toolbar();
|
||||
_render_view_toolbar();
|
||||
|
||||
if ((m_layers_editing.last_object_id >= 0) && (m_layers_editing.object_max_z() > 0.0f))
|
||||
m_layers_editing.render_overlay(*this);
|
||||
|
||||
glsafe(::glPopMatrix());
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_warning_texture() const
|
||||
{
|
||||
m_warning_texture.render(*this);
|
||||
|
@ -4150,7 +4038,7 @@ void GLCanvas3D::_render_toolbar() const
|
|||
#endif // ENABLE_RETINA_GL
|
||||
|
||||
Size cnv_size = get_canvas_size();
|
||||
float zoom = m_camera.zoom;
|
||||
float zoom = (float)m_camera.get_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
|
||||
GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation();
|
||||
|
@ -4218,7 +4106,7 @@ void GLCanvas3D::_render_view_toolbar() const
|
|||
#endif // ENABLE_RETINA_GL
|
||||
|
||||
Size cnv_size = get_canvas_size();
|
||||
float zoom = m_camera.zoom;
|
||||
float zoom = (float)m_camera.get_zoom();
|
||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||
|
||||
// places the toolbar on the bottom-left corner of the 3d scene
|
||||
|
@ -5699,7 +5587,7 @@ std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& col
|
|||
|
||||
void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
|
||||
{
|
||||
m_legend_texture.generate(preview_data, tool_colors, *this);
|
||||
m_legend_texture.generate(preview_data, tool_colors, *this, true);
|
||||
}
|
||||
|
||||
void GLCanvas3D::_set_warning_texture(WarningTexture::Warning warning, bool state)
|
||||
|
|
|
@ -376,7 +376,7 @@ class GLCanvas3D
|
|||
std::vector<Warning> m_warnings;
|
||||
|
||||
// Generates the texture with given text.
|
||||
bool _generate(const std::string& msg, const GLCanvas3D& canvas, const bool red_colored = false);
|
||||
bool generate(const std::string& msg, const GLCanvas3D& canvas, bool compress, bool red_colored = false);
|
||||
};
|
||||
|
||||
class LegendTexture : public GUI::GLTexture
|
||||
|
@ -399,7 +399,7 @@ class GLCanvas3D
|
|||
void fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas,
|
||||
std::vector<std::pair<double, double>>& cp_legend_values);
|
||||
|
||||
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas);
|
||||
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas, bool compress);
|
||||
|
||||
void render(const GLCanvas3D& canvas) const;
|
||||
};
|
||||
|
@ -443,6 +443,7 @@ private:
|
|||
bool m_use_clipping_planes;
|
||||
mutable SlaCap m_sla_caps[2];
|
||||
std::string m_sidebar_field;
|
||||
bool m_keep_dirty;
|
||||
|
||||
mutable GLVolumeCollection m_volumes;
|
||||
Selection m_selection;
|
||||
|
@ -599,7 +600,7 @@ public:
|
|||
void do_flatten();
|
||||
void do_mirror();
|
||||
|
||||
void set_camera_zoom(float zoom);
|
||||
void set_camera_zoom(double zoom);
|
||||
|
||||
void update_gizmos_on_off_state();
|
||||
void reset_all_gizmos() { m_gizmos.reset_all_states(); }
|
||||
|
@ -629,6 +630,9 @@ public:
|
|||
void set_cursor(ECursorType type);
|
||||
void msw_rescale();
|
||||
|
||||
void start_keeping_dirty() { m_keep_dirty = true; }
|
||||
void stop_keeping_dirty() { m_keep_dirty = false; }
|
||||
|
||||
private:
|
||||
bool _is_shown_on_screen() const;
|
||||
|
||||
|
@ -637,10 +641,9 @@ private:
|
|||
bool _set_current();
|
||||
void _resize(unsigned int w, unsigned int h);
|
||||
|
||||
BoundingBoxf3 _max_bounding_box() const;
|
||||
BoundingBoxf3 _max_bounding_box(bool include_bed_model) const;
|
||||
|
||||
void _zoom_to_bounding_box(const BoundingBoxf3& bbox);
|
||||
float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const;
|
||||
void _zoom_to_box(const BoundingBoxf3& box);
|
||||
|
||||
void _refresh_if_shown_on_screen();
|
||||
|
||||
|
@ -654,6 +657,7 @@ private:
|
|||
#if ENABLE_RENDER_SELECTION_CENTER
|
||||
void _render_selection_center() const;
|
||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||
void _render_overlays() const;
|
||||
void _render_warning_texture() const;
|
||||
void _render_legend_texture() const;
|
||||
void _render_volumes_for_picking() const;
|
||||
|
|
|
@ -15,40 +15,112 @@
|
|||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "../Utils/MacDarkMode.hpp"
|
||||
#endif // __APPLE__
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
GLCanvas3DManager::GLInfo::GLInfo()
|
||||
: version("")
|
||||
, glsl_version("")
|
||||
, vendor("")
|
||||
, renderer("")
|
||||
: m_detected(false)
|
||||
, m_version("")
|
||||
, m_glsl_version("")
|
||||
, m_vendor("")
|
||||
, m_renderer("")
|
||||
, m_max_tex_size(0)
|
||||
, m_max_anisotropy(0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
void GLCanvas3DManager::GLInfo::detect()
|
||||
const std::string& GLCanvas3DManager::GLInfo::get_version() const
|
||||
{
|
||||
if (!m_detected)
|
||||
detect();
|
||||
|
||||
return m_version;
|
||||
}
|
||||
|
||||
const std::string& GLCanvas3DManager::GLInfo::get_glsl_version() const
|
||||
{
|
||||
if (!m_detected)
|
||||
detect();
|
||||
|
||||
return m_glsl_version;
|
||||
}
|
||||
|
||||
const std::string& GLCanvas3DManager::GLInfo::get_vendor() const
|
||||
{
|
||||
if (!m_detected)
|
||||
detect();
|
||||
|
||||
return m_vendor;
|
||||
}
|
||||
|
||||
const std::string& GLCanvas3DManager::GLInfo::get_renderer() const
|
||||
{
|
||||
if (!m_detected)
|
||||
detect();
|
||||
|
||||
return m_renderer;
|
||||
}
|
||||
|
||||
int GLCanvas3DManager::GLInfo::get_max_tex_size() const
|
||||
{
|
||||
if (!m_detected)
|
||||
detect();
|
||||
|
||||
// clamp to avoid the texture generation become too slow and use too much GPU memory
|
||||
#ifdef __APPLE__
|
||||
// and use smaller texture for non retina systems
|
||||
return (Slic3r::GUI::mac_max_scaling_factor() > 1.0) ? std::min(m_max_tex_size, 8192) : std::min(m_max_tex_size / 2, 4096);
|
||||
#else
|
||||
// and use smaller texture for older OpenGL versions
|
||||
return is_version_greater_or_equal_to(3, 0) ? std::min(m_max_tex_size, 8192) : std::min(m_max_tex_size / 2, 4096);
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
float GLCanvas3DManager::GLInfo::get_max_anisotropy() const
|
||||
{
|
||||
if (!m_detected)
|
||||
detect();
|
||||
|
||||
return m_max_anisotropy;
|
||||
}
|
||||
|
||||
void GLCanvas3DManager::GLInfo::detect() const
|
||||
{
|
||||
const char* data = (const char*)::glGetString(GL_VERSION);
|
||||
if (data != nullptr)
|
||||
version = data;
|
||||
m_version = data;
|
||||
|
||||
data = (const char*)::glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||
if (data != nullptr)
|
||||
glsl_version = data;
|
||||
m_glsl_version = data;
|
||||
|
||||
data = (const char*)::glGetString(GL_VENDOR);
|
||||
if (data != nullptr)
|
||||
vendor = data;
|
||||
m_vendor = data;
|
||||
|
||||
data = (const char*)::glGetString(GL_RENDERER);
|
||||
if (data != nullptr)
|
||||
renderer = data;
|
||||
m_renderer = data;
|
||||
|
||||
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_tex_size));
|
||||
|
||||
if (GLEW_EXT_texture_filter_anisotropic)
|
||||
glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &m_max_anisotropy));
|
||||
|
||||
m_detected = true;
|
||||
}
|
||||
|
||||
bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
|
||||
{
|
||||
if (!m_detected)
|
||||
detect();
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on);
|
||||
boost::split(tokens, m_version, boost::is_any_of(" "), boost::token_compress_on);
|
||||
|
||||
if (tokens.empty())
|
||||
return false;
|
||||
|
@ -75,6 +147,9 @@ bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int majo
|
|||
|
||||
std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool extensions) const
|
||||
{
|
||||
if (!m_detected)
|
||||
detect();
|
||||
|
||||
std::stringstream out;
|
||||
|
||||
std::string h2_start = format_as_html ? "<b>" : "";
|
||||
|
@ -84,10 +159,10 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
|
|||
std::string line_end = format_as_html ? "<br>" : "\n";
|
||||
|
||||
out << h2_start << "OpenGL installation" << h2_end << line_end;
|
||||
out << b_start << "GL version: " << b_end << (version.empty() ? "N/A" : version) << line_end;
|
||||
out << b_start << "Vendor: " << b_end << (vendor.empty() ? "N/A" : vendor) << line_end;
|
||||
out << b_start << "Renderer: " << b_end << (renderer.empty() ? "N/A" : renderer) << line_end;
|
||||
out << b_start << "GLSL version: " << b_end << (glsl_version.empty() ? "N/A" : glsl_version) << line_end;
|
||||
out << b_start << "GL version: " << b_end << (m_version.empty() ? "N/A" : m_version) << line_end;
|
||||
out << b_start << "Vendor: " << b_end << (m_vendor.empty() ? "N/A" : m_vendor) << line_end;
|
||||
out << b_start << "Renderer: " << b_end << (m_renderer.empty() ? "N/A" : m_renderer) << line_end;
|
||||
out << b_start << "GLSL version: " << b_end << (m_glsl_version.empty() ? "N/A" : m_glsl_version) << line_end;
|
||||
|
||||
if (extensions)
|
||||
{
|
||||
|
@ -111,6 +186,8 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
|
|||
}
|
||||
|
||||
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown;
|
||||
bool GLCanvas3DManager::s_compressed_textures_supported = false;
|
||||
GLCanvas3DManager::GLInfo GLCanvas3DManager::s_gl_info;
|
||||
|
||||
GLCanvas3DManager::GLCanvas3DManager()
|
||||
: m_context(nullptr)
|
||||
|
@ -134,7 +211,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLTo
|
|||
if (canvas == nullptr)
|
||||
return false;
|
||||
|
||||
if (_get_canvas(canvas) != m_canvases.end())
|
||||
if (do_get_canvas(canvas) != m_canvases.end())
|
||||
return false;
|
||||
|
||||
GLCanvas3D* canvas3D = new GLCanvas3D(canvas, bed, camera, view_toolbar);
|
||||
|
@ -159,7 +236,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLTo
|
|||
|
||||
bool GLCanvas3DManager::remove(wxGLCanvas* canvas)
|
||||
{
|
||||
CanvasesMap::iterator it = _get_canvas(canvas);
|
||||
CanvasesMap::iterator it = do_get_canvas(canvas);
|
||||
if (it == m_canvases.end())
|
||||
return false;
|
||||
|
||||
|
@ -190,31 +267,29 @@ void GLCanvas3DManager::init_gl()
|
|||
if (!m_gl_initialized)
|
||||
{
|
||||
glewInit();
|
||||
m_gl_info.detect();
|
||||
const AppConfig* config = GUI::get_app_config();
|
||||
m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1");
|
||||
m_use_VBOs = !m_use_legacy_opengl && m_gl_info.is_version_greater_or_equal_to(2, 0);
|
||||
m_use_VBOs = !m_use_legacy_opengl && s_gl_info.is_version_greater_or_equal_to(2, 0);
|
||||
m_gl_initialized = true;
|
||||
if (GLEW_EXT_texture_compression_s3tc)
|
||||
s_compressed_textures_supported = true;
|
||||
else
|
||||
s_compressed_textures_supported = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string GLCanvas3DManager::get_gl_info(bool format_as_html, bool extensions) const
|
||||
{
|
||||
return m_gl_info.to_string(format_as_html, extensions);
|
||||
}
|
||||
|
||||
bool GLCanvas3DManager::init(wxGLCanvas* canvas)
|
||||
{
|
||||
CanvasesMap::const_iterator it = _get_canvas(canvas);
|
||||
CanvasesMap::const_iterator it = do_get_canvas(canvas);
|
||||
if (it != m_canvases.end())
|
||||
return (it->second != nullptr) ? _init(*it->second) : false;
|
||||
return (it->second != nullptr) ? init(*it->second) : false;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
GLCanvas3D* GLCanvas3DManager::get_canvas(wxGLCanvas* canvas)
|
||||
{
|
||||
CanvasesMap::const_iterator it = _get_canvas(canvas);
|
||||
CanvasesMap::const_iterator it = do_get_canvas(canvas);
|
||||
return (it != m_canvases.end()) ? it->second : nullptr;
|
||||
}
|
||||
|
||||
|
@ -224,29 +299,28 @@ wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent)
|
|||
|
||||
if (s_multisample == MS_Unknown)
|
||||
{
|
||||
_detect_multisample(attribList);
|
||||
// debug output
|
||||
std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl;
|
||||
detect_multisample(attribList);
|
||||
// // debug output
|
||||
// std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl;
|
||||
}
|
||||
|
||||
if (! can_multisample()) {
|
||||
if (! can_multisample())
|
||||
attribList[4] = 0;
|
||||
}
|
||||
|
||||
return new wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
|
||||
}
|
||||
|
||||
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas)
|
||||
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::do_get_canvas(wxGLCanvas* canvas)
|
||||
{
|
||||
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);
|
||||
}
|
||||
|
||||
GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) const
|
||||
GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::do_get_canvas(wxGLCanvas* canvas) const
|
||||
{
|
||||
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);
|
||||
}
|
||||
|
||||
bool GLCanvas3DManager::_init(GLCanvas3D& canvas)
|
||||
bool GLCanvas3DManager::init(GLCanvas3D& canvas)
|
||||
{
|
||||
if (!m_gl_initialized)
|
||||
init_gl();
|
||||
|
@ -254,7 +328,7 @@ bool GLCanvas3DManager::_init(GLCanvas3D& canvas)
|
|||
return canvas.init(m_use_VBOs, m_use_legacy_opengl);
|
||||
}
|
||||
|
||||
void GLCanvas3DManager::_detect_multisample(int* attribList)
|
||||
void GLCanvas3DManager::detect_multisample(int* attribList)
|
||||
{
|
||||
int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER;
|
||||
const AppConfig* app_config = GUI::get_app_config();
|
||||
|
|
|
@ -29,21 +29,39 @@ struct Camera;
|
|||
|
||||
class GLCanvas3DManager
|
||||
{
|
||||
struct GLInfo
|
||||
public:
|
||||
class GLInfo
|
||||
{
|
||||
std::string version;
|
||||
std::string glsl_version;
|
||||
std::string vendor;
|
||||
std::string renderer;
|
||||
mutable bool m_detected;
|
||||
|
||||
mutable std::string m_version;
|
||||
mutable std::string m_glsl_version;
|
||||
mutable std::string m_vendor;
|
||||
mutable std::string m_renderer;
|
||||
|
||||
mutable int m_max_tex_size;
|
||||
mutable float m_max_anisotropy;
|
||||
|
||||
public:
|
||||
GLInfo();
|
||||
|
||||
void detect();
|
||||
const std::string& get_version() const;
|
||||
const std::string& get_glsl_version() const;
|
||||
const std::string& get_vendor() const;
|
||||
const std::string& get_renderer() const;
|
||||
|
||||
int get_max_tex_size() const;
|
||||
float get_max_anisotropy() const;
|
||||
|
||||
bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const;
|
||||
|
||||
std::string to_string(bool format_as_html, bool extensions) const;
|
||||
|
||||
private:
|
||||
void detect() const;
|
||||
};
|
||||
|
||||
private:
|
||||
enum EMultisampleState : unsigned char
|
||||
{
|
||||
MS_Unknown,
|
||||
|
@ -55,11 +73,12 @@ class GLCanvas3DManager
|
|||
|
||||
CanvasesMap m_canvases;
|
||||
wxGLContext* m_context;
|
||||
GLInfo m_gl_info;
|
||||
static GLInfo s_gl_info;
|
||||
bool m_gl_initialized;
|
||||
bool m_use_legacy_opengl;
|
||||
bool m_use_VBOs;
|
||||
static EMultisampleState s_multisample;
|
||||
static bool s_compressed_textures_supported;
|
||||
|
||||
public:
|
||||
GLCanvas3DManager();
|
||||
|
@ -72,21 +91,24 @@ public:
|
|||
unsigned int count() const;
|
||||
|
||||
void init_gl();
|
||||
std::string get_gl_info(bool format_as_html, bool extensions) const;
|
||||
|
||||
bool init(wxGLCanvas* canvas);
|
||||
|
||||
GLCanvas3D* get_canvas(wxGLCanvas* canvas);
|
||||
|
||||
static bool can_multisample() { return s_multisample == MS_Enabled; }
|
||||
static bool are_compressed_textures_supported() { return s_compressed_textures_supported; }
|
||||
|
||||
static wxGLCanvas* create_wxglcanvas(wxWindow *parent);
|
||||
|
||||
private:
|
||||
CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas);
|
||||
CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const;
|
||||
static const GLInfo& get_gl_info() { return s_gl_info; }
|
||||
|
||||
bool _init(GLCanvas3D& canvas);
|
||||
static void _detect_multisample(int* attribList);
|
||||
private:
|
||||
CanvasesMap::iterator do_get_canvas(wxGLCanvas* canvas);
|
||||
CanvasesMap::const_iterator do_get_canvas(wxGLCanvas* canvas) const;
|
||||
|
||||
bool init(GLCanvas3D& canvas);
|
||||
static void detect_multisample(int* attribList);
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")));
|
||||
|
|
|
@ -1772,6 +1772,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
q->Layout();
|
||||
|
||||
set_current_panel(view3D);
|
||||
|
||||
// updates camera type from .ini file
|
||||
camera.set_type(get_config("camera_type"));
|
||||
}
|
||||
|
||||
void Plater::priv::update(bool force_full_scene_refresh)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define PROGRESSSTATUSBAR_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
|
|
|
@ -1893,25 +1893,59 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
|
|||
|
||||
void Selection::paste_volumes_from_clipboard()
|
||||
{
|
||||
int obj_idx = get_object_idx();
|
||||
if ((obj_idx < 0) || ((int)m_model->objects.size() <= obj_idx))
|
||||
int dst_obj_idx = get_object_idx();
|
||||
if ((dst_obj_idx < 0) || ((int)m_model->objects.size() <= dst_obj_idx))
|
||||
return;
|
||||
|
||||
ModelObject* dst_object = m_model->objects[dst_obj_idx];
|
||||
|
||||
int dst_inst_idx = get_instance_idx();
|
||||
if ((dst_inst_idx < 0) || ((int)dst_object->instances.size() <= dst_inst_idx))
|
||||
return;
|
||||
|
||||
ModelObject* src_object = m_clipboard.get_object(0);
|
||||
if (src_object != nullptr)
|
||||
{
|
||||
ModelObject* dst_object = m_model->objects[obj_idx];
|
||||
ModelInstance* dst_instance = dst_object->instances[dst_inst_idx];
|
||||
BoundingBoxf3 dst_instance_bb = dst_object->instance_bounding_box(dst_inst_idx);
|
||||
Transform3d src_matrix = src_object->instances[0]->get_transformation().get_matrix(true);
|
||||
Transform3d dst_matrix = dst_instance->get_transformation().get_matrix(true);
|
||||
bool from_same_object = (src_object->input_file == dst_object->input_file) && src_matrix.isApprox(dst_matrix);
|
||||
|
||||
// used to keep relative position of multivolume selections when pasting from another object
|
||||
BoundingBoxf3 total_bb;
|
||||
|
||||
ModelVolumePtrs volumes;
|
||||
for (ModelVolume* src_volume : src_object->volumes)
|
||||
{
|
||||
ModelVolume* dst_volume = dst_object->add_volume(*src_volume);
|
||||
dst_volume->set_new_unique_id();
|
||||
double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
|
||||
dst_volume->translate(offset, offset, 0.0);
|
||||
if (from_same_object)
|
||||
{
|
||||
// // if the volume comes from the same object, apply the offset in world system
|
||||
// double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
|
||||
// dst_volume->translate(dst_matrix.inverse() * Vec3d(offset, offset, 0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the volume comes from another object, apply the offset as done when adding modifiers
|
||||
// see ObjectList::load_generic_subobject()
|
||||
total_bb.merge(dst_volume->mesh().bounding_box().transformed(src_volume->get_matrix()));
|
||||
}
|
||||
|
||||
volumes.push_back(dst_volume);
|
||||
}
|
||||
wxGetApp().obj_list()->paste_volumes_into_list(obj_idx, volumes);
|
||||
|
||||
// keeps relative position of multivolume selections
|
||||
if (!from_same_object)
|
||||
{
|
||||
for (ModelVolume* v : volumes)
|
||||
{
|
||||
v->set_offset((v->get_offset() - total_bb.center()) + dst_matrix.inverse() * (Vec3d(dst_instance_bb.max(0), dst_instance_bb.min(1), dst_instance_bb.min(2)) + 0.5 * total_bb.size() - dst_instance->get_transformation().get_offset()));
|
||||
}
|
||||
}
|
||||
|
||||
wxGetApp().obj_list()->paste_volumes_into_list(dst_obj_idx, volumes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue