Merge remote-tracking branch 'origin/master' into ys_new_features
This commit is contained in:
commit
daac165a44
44 changed files with 2572 additions and 646 deletions
|
@ -18,7 +18,7 @@ AllowShortLoopsOnASingleLine: true
|
|||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
|
@ -37,18 +37,18 @@ BraceWrapping:
|
|||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
BreakBeforeBinaryOperators: All
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 75
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
CompactNamespaces: true
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
|
|
|
@ -125,8 +125,8 @@ namespace Slic3r {
|
|||
trapezoid.distance = distance;
|
||||
trapezoid.feedrate = feedrate;
|
||||
|
||||
float accelerate_distance = estimate_acceleration_distance(feedrate.entry, feedrate.cruise, acceleration);
|
||||
float decelerate_distance = estimate_acceleration_distance(feedrate.cruise, feedrate.exit, -acceleration);
|
||||
float accelerate_distance = std::max(0.0f, estimate_acceleration_distance(feedrate.entry, feedrate.cruise, acceleration));
|
||||
float decelerate_distance = std::max(0.0f, estimate_acceleration_distance(feedrate.cruise, feedrate.exit, -acceleration));
|
||||
float cruise_distance = distance - accelerate_distance - decelerate_distance;
|
||||
|
||||
// Not enough space to reach the nominal feedrate.
|
||||
|
|
|
@ -1409,6 +1409,63 @@ Transformation Transformation::operator * (const Transformation& other) const
|
|||
return Transformation(get_matrix() * other.get_matrix());
|
||||
}
|
||||
|
||||
Transformation Transformation::volume_to_bed_transformation(const Transformation& instance_transformation, const BoundingBoxf3& bbox)
|
||||
{
|
||||
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 (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;
|
||||
}
|
||||
|
||||
Eigen::Quaterniond rotation_xyz_diff(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
|
||||
{
|
||||
return
|
||||
|
|
|
@ -258,6 +258,11 @@ public:
|
|||
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const;
|
||||
|
||||
Transformation operator * (const Transformation& other) const;
|
||||
|
||||
// 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.
|
||||
static Transformation volume_to_bed_transformation(const Transformation& instance_transformation, const BoundingBoxf3& bbox);
|
||||
};
|
||||
|
||||
// Rotation when going from the first coordinate system with rotation rot_xyz_from applied
|
||||
|
|
|
@ -1,172 +1,218 @@
|
|||
#ifndef MTUTILS_HPP
|
||||
#define MTUTILS_HPP
|
||||
|
||||
#include <atomic> // for std::atomic_flag and memory orders
|
||||
#include <mutex> // for std::lock_guard
|
||||
#include <functional> // for std::function
|
||||
#include <utility> // for std::forward
|
||||
#include <atomic> // for std::atomic_flag and memory orders
|
||||
#include <mutex> // for std::lock_guard
|
||||
#include <functional> // for std::function
|
||||
#include <utility> // for std::forward
|
||||
#include <algorithm>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
/// Handy little spin mutex for the cached meshes.
|
||||
/// Implements the "Lockable" concept
|
||||
class SpinMutex {
|
||||
std::atomic_flag m_flg;
|
||||
class SpinMutex
|
||||
{
|
||||
std::atomic_flag m_flg;
|
||||
static const /*constexpr*/ auto MO_ACQ = std::memory_order_acquire;
|
||||
static const /*constexpr*/ auto MO_REL = std::memory_order_release;
|
||||
|
||||
public:
|
||||
inline SpinMutex() { m_flg.clear(MO_REL); }
|
||||
inline void lock() { while(m_flg.test_and_set(MO_ACQ)); }
|
||||
inline void lock() { while (m_flg.test_and_set(MO_ACQ)) ; }
|
||||
inline bool try_lock() { return !m_flg.test_and_set(MO_ACQ); }
|
||||
inline void unlock() { m_flg.clear(MO_REL); }
|
||||
};
|
||||
|
||||
/// A wrapper class around arbitrary object that needs thread safe caching.
|
||||
template<class T> class CachedObject {
|
||||
template<class T> class CachedObject
|
||||
{
|
||||
public:
|
||||
// Method type which refreshes the object when it has been invalidated
|
||||
using Setter = std::function<void(T&)>;
|
||||
using Setter = std::function<void(T &)>;
|
||||
|
||||
private:
|
||||
T m_obj; // the object itself
|
||||
bool m_valid; // invalidation flag
|
||||
SpinMutex m_lck; // to make the caching thread safe
|
||||
T m_obj; // the object itself
|
||||
bool m_valid; // invalidation flag
|
||||
SpinMutex m_lck; // to make the caching thread safe
|
||||
|
||||
// the setter will be called just before the object's const value is
|
||||
// about to be retrieved.
|
||||
std::function<void(T &)> m_setter;
|
||||
|
||||
// the setter will be called just before the object's const value is about
|
||||
// to be retrieved.
|
||||
std::function<void(T&)> m_setter;
|
||||
public:
|
||||
|
||||
// Forwarded constructor
|
||||
template<class...Args> inline CachedObject(Setter fn, Args&&...args):
|
||||
m_obj(std::forward<Args>(args)...), m_valid(false), m_setter(fn) {}
|
||||
template<class... Args>
|
||||
inline CachedObject(Setter fn, Args &&... args)
|
||||
: m_obj(std::forward<Args>(args)...), m_valid(false), m_setter(fn)
|
||||
{}
|
||||
|
||||
// invalidate the value of the object. The object will be refreshed at the
|
||||
// next retrieval (Setter will be called). The data that is used in
|
||||
// the setter function should be guarded as well during modification so the
|
||||
// modification has to take place in fn.
|
||||
inline void invalidate(std::function<void()> fn) {
|
||||
std::lock_guard<SpinMutex> lck(m_lck); fn(); m_valid = false;
|
||||
// invalidate the value of the object. The object will be refreshed at
|
||||
// the next retrieval (Setter will be called). The data that is used in
|
||||
// the setter function should be guarded as well during modification so
|
||||
// the modification has to take place in fn.
|
||||
inline void invalidate(std::function<void()> fn)
|
||||
{
|
||||
std::lock_guard<SpinMutex> lck(m_lck);
|
||||
fn();
|
||||
m_valid = false;
|
||||
}
|
||||
|
||||
// Get the const object properly updated.
|
||||
inline const T& get() {
|
||||
inline const T &get()
|
||||
{
|
||||
std::lock_guard<SpinMutex> lck(m_lck);
|
||||
if(!m_valid) { m_setter(m_obj); m_valid = true; }
|
||||
if (!m_valid) {
|
||||
m_setter(m_obj);
|
||||
m_valid = true;
|
||||
}
|
||||
return m_obj;
|
||||
}
|
||||
};
|
||||
|
||||
/// An std compatible random access iterator which uses indices to the source
|
||||
/// vector thus resistant to invalidation caused by relocations. It also "knows"
|
||||
/// its container. No comparison is neccesary to the container "end()" iterator.
|
||||
/// The template can be instantiated with a different value type than that of
|
||||
/// the container's but the types must be compatible. E.g. a base class of the
|
||||
/// contained objects is compatible.
|
||||
/// An std compatible random access iterator which uses indices to the
|
||||
/// source vector thus resistant to invalidation caused by relocations. It
|
||||
/// also "knows" its container. No comparison is neccesary to the container
|
||||
/// "end()" iterator. The template can be instantiated with a different
|
||||
/// value type than that of the container's but the types must be
|
||||
/// compatible. E.g. a base class of the contained objects is compatible.
|
||||
///
|
||||
/// For a constant iterator, one can instantiate this template with a value
|
||||
/// type preceded with 'const'.
|
||||
template<class Vector, // The container type, must be random access...
|
||||
template<class Vector, // The container type, must be random access...
|
||||
class Value = typename Vector::value_type // The value type
|
||||
>
|
||||
class IndexBasedIterator {
|
||||
class IndexBasedIterator
|
||||
{
|
||||
static const size_t NONE = size_t(-1);
|
||||
|
||||
std::reference_wrapper<Vector> m_index_ref;
|
||||
size_t m_idx = NONE;
|
||||
public:
|
||||
size_t m_idx = NONE;
|
||||
|
||||
using value_type = Value;
|
||||
using pointer = Value *;
|
||||
using reference = Value &;
|
||||
using difference_type = long;
|
||||
public:
|
||||
using value_type = Value;
|
||||
using pointer = Value *;
|
||||
using reference = Value &;
|
||||
using difference_type = long;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
inline explicit
|
||||
IndexBasedIterator(Vector& index, size_t idx):
|
||||
m_index_ref(index), m_idx(idx) {}
|
||||
inline explicit IndexBasedIterator(Vector &index, size_t idx)
|
||||
: m_index_ref(index), m_idx(idx)
|
||||
{}
|
||||
|
||||
// Post increment
|
||||
inline IndexBasedIterator operator++(int) {
|
||||
IndexBasedIterator cpy(*this); ++m_idx; return cpy;
|
||||
inline IndexBasedIterator operator++(int)
|
||||
{
|
||||
IndexBasedIterator cpy(*this);
|
||||
++m_idx;
|
||||
return cpy;
|
||||
}
|
||||
|
||||
inline IndexBasedIterator operator--(int) {
|
||||
IndexBasedIterator cpy(*this); --m_idx; return cpy;
|
||||
inline IndexBasedIterator operator--(int)
|
||||
{
|
||||
IndexBasedIterator cpy(*this);
|
||||
--m_idx;
|
||||
return cpy;
|
||||
}
|
||||
|
||||
inline IndexBasedIterator& operator++() {
|
||||
++m_idx; return *this;
|
||||
inline IndexBasedIterator &operator++()
|
||||
{
|
||||
++m_idx;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline IndexBasedIterator& operator--() {
|
||||
--m_idx; return *this;
|
||||
inline IndexBasedIterator &operator--()
|
||||
{
|
||||
--m_idx;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline IndexBasedIterator& operator+=(difference_type l) {
|
||||
m_idx += size_t(l); return *this;
|
||||
inline IndexBasedIterator &operator+=(difference_type l)
|
||||
{
|
||||
m_idx += size_t(l);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline IndexBasedIterator operator+(difference_type l) {
|
||||
auto cpy = *this; cpy += l; return cpy;
|
||||
inline IndexBasedIterator operator+(difference_type l)
|
||||
{
|
||||
auto cpy = *this;
|
||||
cpy += l;
|
||||
return cpy;
|
||||
}
|
||||
|
||||
inline IndexBasedIterator& operator-=(difference_type l) {
|
||||
m_idx -= size_t(l); return *this;
|
||||
inline IndexBasedIterator &operator-=(difference_type l)
|
||||
{
|
||||
m_idx -= size_t(l);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline IndexBasedIterator operator-(difference_type l) {
|
||||
auto cpy = *this; cpy -= l; return cpy;
|
||||
inline IndexBasedIterator operator-(difference_type l)
|
||||
{
|
||||
auto cpy = *this;
|
||||
cpy -= l;
|
||||
return cpy;
|
||||
}
|
||||
|
||||
operator difference_type() { return difference_type(m_idx); }
|
||||
|
||||
/// Tesing the end of the container... this is not possible with std
|
||||
/// iterators.
|
||||
inline bool is_end() const { return m_idx >= m_index_ref.get().size();}
|
||||
inline bool is_end() const
|
||||
{
|
||||
return m_idx >= m_index_ref.get().size();
|
||||
}
|
||||
|
||||
inline Value & operator*() const {
|
||||
inline Value &operator*() const
|
||||
{
|
||||
assert(m_idx < m_index_ref.get().size());
|
||||
return m_index_ref.get().operator[](m_idx);
|
||||
}
|
||||
|
||||
inline Value * operator->() const {
|
||||
inline Value *operator->() const
|
||||
{
|
||||
assert(m_idx < m_index_ref.get().size());
|
||||
return &m_index_ref.get().operator[](m_idx);
|
||||
}
|
||||
|
||||
/// If both iterators point past the container, they are equal...
|
||||
inline bool operator ==(const IndexBasedIterator& other) {
|
||||
inline bool operator==(const IndexBasedIterator &other)
|
||||
{
|
||||
size_t e = m_index_ref.get().size();
|
||||
return m_idx == other.m_idx || (m_idx >= e && other.m_idx >= e);
|
||||
}
|
||||
|
||||
inline bool operator !=(const IndexBasedIterator& other) {
|
||||
inline bool operator!=(const IndexBasedIterator &other)
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
inline bool operator <=(const IndexBasedIterator& other) {
|
||||
inline bool operator<=(const IndexBasedIterator &other)
|
||||
{
|
||||
return (m_idx < other.m_idx) || (*this == other);
|
||||
}
|
||||
|
||||
inline bool operator <(const IndexBasedIterator& other) {
|
||||
inline bool operator<(const IndexBasedIterator &other)
|
||||
{
|
||||
return m_idx < other.m_idx && (*this != other);
|
||||
}
|
||||
|
||||
inline bool operator >=(const IndexBasedIterator& other) {
|
||||
inline bool operator>=(const IndexBasedIterator &other)
|
||||
{
|
||||
return m_idx > other.m_idx || *this == other;
|
||||
}
|
||||
|
||||
inline bool operator >(const IndexBasedIterator& other) {
|
||||
inline bool operator>(const IndexBasedIterator &other)
|
||||
{
|
||||
return m_idx > other.m_idx && *this != other;
|
||||
}
|
||||
};
|
||||
|
||||
/// A very simple range concept implementation with iterator-like objects.
|
||||
template<class It> class Range {
|
||||
template<class It> class Range
|
||||
{
|
||||
It from, to;
|
||||
public:
|
||||
|
||||
public:
|
||||
// The class is ready for range based for loops.
|
||||
It begin() const { return from; }
|
||||
It end() const { return to; }
|
||||
|
@ -175,15 +221,17 @@ public:
|
|||
using Type = It;
|
||||
|
||||
Range() = default;
|
||||
Range(It &&b, It &&e):
|
||||
from(std::forward<It>(b)), to(std::forward<It>(e)) {}
|
||||
Range(It &&b, It &&e)
|
||||
: from(std::forward<It>(b)), to(std::forward<It>(e))
|
||||
{}
|
||||
|
||||
// Some useful container-like methods...
|
||||
inline size_t size() const { return end() - begin(); }
|
||||
inline bool empty() const { return size() == 0; }
|
||||
inline bool empty() const { return size() == 0; }
|
||||
};
|
||||
|
||||
template<class C> bool all_of(const C &container) {
|
||||
template<class C> bool all_of(const C &container)
|
||||
{
|
||||
return std::all_of(container.begin(),
|
||||
container.end(),
|
||||
[](const typename C::value_type &v) {
|
||||
|
@ -191,6 +239,15 @@ template<class C> bool all_of(const C &container) {
|
|||
});
|
||||
}
|
||||
|
||||
template<class X, class Y> inline X ceil_i(X x, Y y)
|
||||
{
|
||||
static_assert(std::is_integral<X>::value &&
|
||||
std::is_integral<Y>::value && sizeof(X) >= sizeof(Y),
|
||||
"");
|
||||
|
||||
return (x % y) ? x / y + 1 : x / y;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // MTUTILS_HPP
|
||||
|
|
|
@ -489,21 +489,57 @@ void Model::convert_multipart_object(unsigned int max_extruders)
|
|||
|
||||
reset_auto_extruder_id();
|
||||
|
||||
bool is_single_object = (this->objects.size() == 1);
|
||||
|
||||
for (const ModelObject* o : this->objects)
|
||||
{
|
||||
for (const ModelVolume* v : o->volumes)
|
||||
{
|
||||
ModelVolume* new_v = object->add_volume(*v);
|
||||
if (new_v != nullptr)
|
||||
if (is_single_object)
|
||||
{
|
||||
new_v->name = o->name;
|
||||
new_v->config.set_deserialize("extruder", get_auto_extruder_id_as_string(max_extruders));
|
||||
new_v->translate(-o->origin_translation);
|
||||
// If there is only one object, just copy the volumes
|
||||
ModelVolume* new_v = object->add_volume(*v);
|
||||
if (new_v != nullptr)
|
||||
{
|
||||
new_v->name = o->name;
|
||||
new_v->config.set_deserialize("extruder", get_auto_extruder_id_as_string(max_extruders));
|
||||
new_v->translate(-o->origin_translation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there are more than one object, put all volumes together
|
||||
// Each object may contain any number of volumes and instances
|
||||
// The volumes transformations are relative to the object containing them...
|
||||
int counter = 1;
|
||||
for (const ModelInstance* i : o->instances)
|
||||
{
|
||||
ModelVolume* new_v = object->add_volume(*v);
|
||||
if (new_v != nullptr)
|
||||
{
|
||||
new_v->name = o->name + "_" + std::to_string(counter++);
|
||||
new_v->config.set_deserialize("extruder", get_auto_extruder_id_as_string(max_extruders));
|
||||
new_v->translate(-o->origin_translation);
|
||||
// ...so, transform everything to a common reference system (world)
|
||||
new_v->set_transformation(i->get_transformation() * v->get_transformation());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_single_object)
|
||||
{
|
||||
// If there is only one object, keep its instances
|
||||
for (const ModelInstance* i : this->objects.front()->instances)
|
||||
{
|
||||
object->add_instance(*i);
|
||||
}
|
||||
}
|
||||
else
|
||||
// If there are more than one object, create a single instance
|
||||
object->add_instance();
|
||||
|
||||
for (const ModelInstance* i : this->objects.front()->instances)
|
||||
object->add_instance(*i);
|
||||
|
||||
this->clear_objects();
|
||||
this->objects.push_back(object);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "Model.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "SVG.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
|
||||
#include <libnest2d.h>
|
||||
|
||||
|
@ -820,15 +821,13 @@ bool arrange(Model &model, // The model with the geometries
|
|||
BoundingBox bbb(bed);
|
||||
|
||||
auto& cfn = stopcondition;
|
||||
|
||||
coord_t md = ceil_i(min_obj_distance, 2) - SCALED_EPSILON;
|
||||
|
||||
auto binbb = Box({
|
||||
static_cast<libnest2d::Coord>(bbb.min(0)),
|
||||
static_cast<libnest2d::Coord>(bbb.min(1))
|
||||
},
|
||||
{
|
||||
static_cast<libnest2d::Coord>(bbb.max(0)),
|
||||
static_cast<libnest2d::Coord>(bbb.max(1))
|
||||
});
|
||||
auto binbb = Box({libnest2d::Coord{bbb.min(0)} - md,
|
||||
libnest2d::Coord{bbb.min(1)} - md},
|
||||
{libnest2d::Coord{bbb.max(0)} + md,
|
||||
libnest2d::Coord{bbb.max(1)} + md});
|
||||
|
||||
switch(bedhint.type) {
|
||||
case BedShapeType::BOX: {
|
||||
|
@ -916,15 +915,13 @@ void find_new_position(const Model &model,
|
|||
BedShapeHint bedhint = bedShape(bed);
|
||||
|
||||
BoundingBox bbb(bed);
|
||||
|
||||
auto binbb = Box({
|
||||
static_cast<libnest2d::Coord>(bbb.min(0)),
|
||||
static_cast<libnest2d::Coord>(bbb.min(1))
|
||||
},
|
||||
{
|
||||
static_cast<libnest2d::Coord>(bbb.max(0)),
|
||||
static_cast<libnest2d::Coord>(bbb.max(1))
|
||||
});
|
||||
|
||||
coord_t md = ceil_i(min_obj_distance, 2) - SCALED_EPSILON;
|
||||
|
||||
auto binbb = Box({libnest2d::Coord{bbb.min(0)} - md,
|
||||
libnest2d::Coord{bbb.min(1)} - md},
|
||||
{libnest2d::Coord{bbb.max(0)} + md,
|
||||
libnest2d::Coord{bbb.max(1)} + md});
|
||||
|
||||
for(auto it = shapemap.begin(); it != shapemap.end(); ++it) {
|
||||
if(std::find(toadd.begin(), toadd.end(), it->first) == toadd.end()) {
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#define ENABLE_RENDER_SELECTION_CENTER 0
|
||||
// Shows an imgui dialog with render related data
|
||||
#define ENABLE_RENDER_STATISTICS 0
|
||||
// Shows an imgui dialog with camera related data
|
||||
#define ENABLE_CAMERA_STATISTICS 0
|
||||
|
||||
|
||||
//====================
|
||||
|
|
|
@ -192,6 +192,7 @@ size_t Index::load(const boost::filesystem::path &path)
|
|||
{
|
||||
m_configs.clear();
|
||||
m_vendor = path.stem().string();
|
||||
m_path = path;
|
||||
|
||||
boost::nowide::ifstream ifs(path.string());
|
||||
std::string line;
|
||||
|
|
|
@ -72,6 +72,9 @@ public:
|
|||
// if the index is valid.
|
||||
const_iterator recommended() const;
|
||||
|
||||
// Returns the filesystem path from which this index has originally been loaded
|
||||
const boost::filesystem::path& path() const { return m_path; }
|
||||
|
||||
// Load all vendor specific indices.
|
||||
// Throws Slic3r::file_parser_error and the standard std file access exceptions.
|
||||
static std::vector<Index> load_db();
|
||||
|
@ -79,6 +82,7 @@ public:
|
|||
private:
|
||||
std::string m_vendor;
|
||||
std::vector<Version> m_configs;
|
||||
boost::filesystem::path m_path;
|
||||
};
|
||||
|
||||
} // namespace Config
|
||||
|
|
|
@ -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)
|
||||
|
@ -3508,7 +3488,7 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.tooltip = _utf8(L("Copy")) + " [" + GUI::shortkey_ctrl_prefix() + "C]";
|
||||
item.sprite_id = 4;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_copy(); };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
@ -3519,7 +3499,7 @@ bool GLCanvas3D::_init_toolbar()
|
|||
item.tooltip = _utf8(L("Paste")) + " [" + GUI::shortkey_ctrl_prefix() + "V]";
|
||||
item.sprite_id = 5;
|
||||
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_paste(); };
|
||||
item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); };
|
||||
if (!m_toolbar.add_item(item))
|
||||
return false;
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1581,66 +1581,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();
|
||||
|
@ -1694,7 +1634,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")));
|
||||
|
|
|
@ -505,10 +505,10 @@ void MainFrame::init_menubar()
|
|||
editMenu->AppendSeparator();
|
||||
append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C",
|
||||
_(L("Copy selection to clipboard")), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); },
|
||||
menu_icon("copy_menu"), nullptr, [this](){return m_plater->can_copy(); }, this);
|
||||
menu_icon("copy_menu"), nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this);
|
||||
append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "V",
|
||||
_(L("Paste clipboard")), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); },
|
||||
menu_icon("paste_menu"), nullptr, [this](){return m_plater->can_paste(); }, this);
|
||||
menu_icon("paste_menu"), nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this);
|
||||
}
|
||||
|
||||
// Window menu
|
||||
|
|
|
@ -1785,6 +1785,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)
|
||||
|
@ -4204,30 +4207,14 @@ void Plater::update_object_menu() { p->update_object_menu(); }
|
|||
|
||||
void Plater::copy_selection_to_clipboard()
|
||||
{
|
||||
p->view3D->get_canvas3d()->get_selection().copy_to_clipboard();
|
||||
if (can_copy_to_clipboard())
|
||||
p->view3D->get_canvas3d()->get_selection().copy_to_clipboard();
|
||||
}
|
||||
|
||||
void Plater::paste_from_clipboard()
|
||||
{
|
||||
p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
|
||||
}
|
||||
|
||||
bool Plater::can_paste_from_clipboard() const
|
||||
{
|
||||
const Selection& selection = p->view3D->get_canvas3d()->get_selection();
|
||||
const Selection::Clipboard& clipboard = selection.get_clipboard();
|
||||
Selection::EMode mode = clipboard.get_mode();
|
||||
|
||||
if (clipboard.is_empty())
|
||||
return false;
|
||||
|
||||
if ((mode == Selection::Volume) && !selection.is_from_single_instance())
|
||||
return false;
|
||||
|
||||
if ((mode == Selection::Instance) && (selection.get_mode() != Selection::Instance))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
if (can_paste_from_clipboard())
|
||||
p->view3D->get_canvas3d()->get_selection().paste_from_clipboard();
|
||||
}
|
||||
|
||||
void Plater::msw_rescale()
|
||||
|
@ -4254,7 +4241,37 @@ bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); }
|
|||
bool Plater::can_split_to_volumes() const { return p->can_split_to_volumes(); }
|
||||
bool Plater::can_arrange() const { return p->can_arrange(); }
|
||||
bool Plater::can_layers_editing() const { return p->can_layers_editing(); }
|
||||
bool Plater::can_copy() const { return !is_selection_empty(); }
|
||||
bool Plater::can_paste() const { return can_paste_from_clipboard(); }
|
||||
bool Plater::can_paste_from_clipboard() const
|
||||
{
|
||||
const Selection& selection = p->view3D->get_canvas3d()->get_selection();
|
||||
const Selection::Clipboard& clipboard = selection.get_clipboard();
|
||||
|
||||
if (clipboard.is_empty())
|
||||
return false;
|
||||
|
||||
if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) && !clipboard.is_sla_compliant())
|
||||
return false;
|
||||
|
||||
Selection::EMode mode = clipboard.get_mode();
|
||||
if ((mode == Selection::Volume) && !selection.is_from_single_instance())
|
||||
return false;
|
||||
|
||||
if ((mode == Selection::Instance) && (selection.get_mode() != Selection::Instance))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Plater::can_copy_to_clipboard() const
|
||||
{
|
||||
if (is_selection_empty())
|
||||
return false;
|
||||
|
||||
const Selection& selection = p->view3D->get_canvas3d()->get_selection();
|
||||
if ((wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA) && !selection.is_sla_compliant())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
|
|
@ -202,7 +202,6 @@ public:
|
|||
|
||||
void copy_selection_to_clipboard();
|
||||
void paste_from_clipboard();
|
||||
bool can_paste_from_clipboard() const;
|
||||
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
|
@ -214,8 +213,8 @@ public:
|
|||
bool can_split_to_volumes() const;
|
||||
bool can_arrange() const;
|
||||
bool can_layers_editing() const;
|
||||
bool can_copy() const;
|
||||
bool can_paste() const;
|
||||
bool can_paste_from_clipboard() const;
|
||||
bool can_copy_to_clipboard() const;
|
||||
|
||||
void msw_rescale();
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define PROGRESSSTATUSBAR_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
|
|
|
@ -47,6 +47,26 @@ Selection::VolumeCache::VolumeCache(const Geometry::Transformation& volume_trans
|
|||
{
|
||||
}
|
||||
|
||||
bool Selection::Clipboard::is_sla_compliant() const
|
||||
{
|
||||
if (m_mode == Selection::Volume)
|
||||
return false;
|
||||
|
||||
for (const ModelObject* o : m_model.objects)
|
||||
{
|
||||
if (o->is_multiparts())
|
||||
return false;
|
||||
|
||||
for (const ModelVolume* v : o->volumes)
|
||||
{
|
||||
if (v->is_modifier())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Selection::Selection()
|
||||
: m_volumes(nullptr)
|
||||
, m_model(nullptr)
|
||||
|
@ -385,6 +405,20 @@ bool Selection::is_from_single_object() const
|
|||
return (0 <= idx) && (idx < 1000);
|
||||
}
|
||||
|
||||
bool Selection::is_sla_compliant() const
|
||||
{
|
||||
if (m_mode == Volume)
|
||||
return false;
|
||||
|
||||
for (unsigned int i : m_list)
|
||||
{
|
||||
if ((*m_volumes)[i]->is_modifier)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Selection::requires_uniform_scale() const
|
||||
{
|
||||
if (is_single_full_instance() || is_single_modifier() || is_single_volume())
|
||||
|
@ -1893,25 +1927,59 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
|
|||
|
||||
void Selection::paste_volumes_from_clipboard()
|
||||
{
|
||||
int obj_idx = get_object_idx();
|
||||
if ((obj_idx < 0) || ((int)m_model->objects.size() <= obj_idx))
|
||||
int dst_obj_idx = get_object_idx();
|
||||
if ((dst_obj_idx < 0) || ((int)m_model->objects.size() <= dst_obj_idx))
|
||||
return;
|
||||
|
||||
ModelObject* dst_object = m_model->objects[dst_obj_idx];
|
||||
|
||||
int dst_inst_idx = get_instance_idx();
|
||||
if ((dst_inst_idx < 0) || ((int)dst_object->instances.size() <= dst_inst_idx))
|
||||
return;
|
||||
|
||||
ModelObject* src_object = m_clipboard.get_object(0);
|
||||
if (src_object != nullptr)
|
||||
{
|
||||
ModelObject* dst_object = m_model->objects[obj_idx];
|
||||
ModelInstance* dst_instance = dst_object->instances[dst_inst_idx];
|
||||
BoundingBoxf3 dst_instance_bb = dst_object->instance_bounding_box(dst_inst_idx);
|
||||
Transform3d src_matrix = src_object->instances[0]->get_transformation().get_matrix(true);
|
||||
Transform3d dst_matrix = dst_instance->get_transformation().get_matrix(true);
|
||||
bool from_same_object = (src_object->input_file == dst_object->input_file) && src_matrix.isApprox(dst_matrix);
|
||||
|
||||
// used to keep relative position of multivolume selections when pasting from another object
|
||||
BoundingBoxf3 total_bb;
|
||||
|
||||
ModelVolumePtrs volumes;
|
||||
for (ModelVolume* src_volume : src_object->volumes)
|
||||
{
|
||||
ModelVolume* dst_volume = dst_object->add_volume(*src_volume);
|
||||
dst_volume->set_new_unique_id();
|
||||
double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
|
||||
dst_volume->translate(offset, offset, 0.0);
|
||||
if (from_same_object)
|
||||
{
|
||||
// // if the volume comes from the same object, apply the offset in world system
|
||||
// double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
|
||||
// dst_volume->translate(dst_matrix.inverse() * Vec3d(offset, offset, 0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the volume comes from another object, apply the offset as done when adding modifiers
|
||||
// see ObjectList::load_generic_subobject()
|
||||
total_bb.merge(dst_volume->mesh().bounding_box().transformed(src_volume->get_matrix()));
|
||||
}
|
||||
|
||||
volumes.push_back(dst_volume);
|
||||
}
|
||||
wxGetApp().obj_list()->paste_volumes_into_list(obj_idx, volumes);
|
||||
|
||||
// keeps relative position of multivolume selections
|
||||
if (!from_same_object)
|
||||
{
|
||||
for (ModelVolume* v : volumes)
|
||||
{
|
||||
v->set_offset((v->get_offset() - total_bb.center()) + dst_matrix.inverse() * (Vec3d(dst_instance_bb.max(0), dst_instance_bb.min(1), dst_instance_bb.min(2)) + 0.5 * total_bb.size() - dst_instance->get_transformation().get_offset()));
|
||||
}
|
||||
}
|
||||
|
||||
wxGetApp().obj_list()->paste_volumes_into_list(dst_obj_idx, volumes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,6 +152,8 @@ public:
|
|||
void reset() { m_model.clear_objects(); }
|
||||
bool is_empty() const { return m_model.objects.empty(); }
|
||||
|
||||
bool is_sla_compliant() const;
|
||||
|
||||
ModelObject* add_object() { return m_model.add_object(); }
|
||||
ModelObject* get_object(unsigned int id) { return (id < (unsigned int)m_model.objects.size()) ? m_model.objects[id] : nullptr; }
|
||||
const ModelObjectPtrs& get_objects() const { return m_model.objects; }
|
||||
|
@ -257,6 +259,7 @@ public:
|
|||
bool is_mixed() const { return m_type == Mixed; }
|
||||
bool is_from_single_instance() const { return get_instance_idx() != -1; }
|
||||
bool is_from_single_object() const;
|
||||
bool is_sla_compliant() const;
|
||||
|
||||
bool contains_volume(unsigned int volume_idx) const { return m_list.find(volume_idx) != m_list.end(); }
|
||||
bool requires_uniform_scale() const;
|
||||
|
|
|
@ -1781,6 +1781,9 @@ DoubleSlider::DoubleSlider( wxWindow *parent,
|
|||
m_bmp_one_layer_unlock_off = ScalableBitmap(this, "one_layer_unlock_off.png");
|
||||
m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x;
|
||||
|
||||
m_bmp_revert = ScalableBitmap(this, "undo");
|
||||
m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x;
|
||||
|
||||
m_selection = ssUndef;
|
||||
|
||||
// slider events
|
||||
|
@ -1836,6 +1839,9 @@ void DoubleSlider::msw_rescale()
|
|||
m_bmp_one_layer_unlock_off.msw_rescale();
|
||||
m_lock_icon_dim = m_bmp_one_layer_lock_on.bmp().GetSize().x;
|
||||
|
||||
m_bmp_revert.msw_rescale();
|
||||
m_revert_icon_dim = m_bmp_revert.bmp().GetSize().x;
|
||||
|
||||
SLIDER_MARGIN = 4 + Slic3r::GUI::wxGetApp().em_unit();
|
||||
|
||||
SetMinSize(get_min_size());
|
||||
|
@ -2072,8 +2078,11 @@ void DoubleSlider::render()
|
|||
//draw color print ticks
|
||||
draw_ticks(dc);
|
||||
|
||||
//draw color print ticks
|
||||
//draw lock/unlock
|
||||
draw_one_layer_icon(dc);
|
||||
|
||||
//draw revert bitmap (if it's shown)
|
||||
draw_revert_icon(dc);
|
||||
}
|
||||
|
||||
void DoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end)
|
||||
|
@ -2300,6 +2309,24 @@ void DoubleSlider::draw_one_layer_icon(wxDC& dc)
|
|||
m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim);
|
||||
}
|
||||
|
||||
void DoubleSlider::draw_revert_icon(wxDC& dc)
|
||||
{
|
||||
if (m_ticks.empty())
|
||||
return;
|
||||
|
||||
int width, height;
|
||||
get_size(&width, &height);
|
||||
|
||||
wxCoord x_draw, y_draw;
|
||||
is_horizontal() ? x_draw = width-2 : x_draw = 0.25*SLIDER_MARGIN;
|
||||
is_horizontal() ? y_draw = 0.25*SLIDER_MARGIN: y_draw = height-2;
|
||||
|
||||
dc.DrawBitmap(m_bmp_revert.bmp(), x_draw, y_draw);
|
||||
|
||||
//update rect of the lock/unlock icon
|
||||
m_rect_revert_icon = wxRect(x_draw, y_draw, m_revert_icon_dim, m_revert_icon_dim);
|
||||
}
|
||||
|
||||
void DoubleSlider::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection)
|
||||
{
|
||||
const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, m_thumb_size.y);
|
||||
|
@ -2316,8 +2343,8 @@ int DoubleSlider::get_value_from_position(const wxCoord x, const wxCoord y)
|
|||
|
||||
if (is_horizontal())
|
||||
return int(double(x - SLIDER_MARGIN) / step + 0.5);
|
||||
else
|
||||
return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5);
|
||||
|
||||
return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5);
|
||||
}
|
||||
|
||||
void DoubleSlider::detect_selected_slider(const wxPoint& pt)
|
||||
|
@ -2367,7 +2394,10 @@ void DoubleSlider::ChangeOneLayerLock()
|
|||
|
||||
void DoubleSlider::OnLeftDown(wxMouseEvent& event)
|
||||
{
|
||||
if (HasCapture())
|
||||
return;
|
||||
this->CaptureMouse();
|
||||
|
||||
wxClientDC dc(this);
|
||||
wxPoint pos = event.GetLogicalPosition(dc);
|
||||
if (is_point_in_rect(pos, m_rect_tick_action) && m_is_enabled_tick_manipulation) {
|
||||
|
@ -2377,6 +2407,7 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event)
|
|||
|
||||
m_is_left_down = true;
|
||||
if (is_point_in_rect(pos, m_rect_one_layer_icon)) {
|
||||
// switch on/off one layer mode
|
||||
m_is_one_layer = !m_is_one_layer;
|
||||
if (!m_is_one_layer) {
|
||||
SetLowerValue(m_min_value);
|
||||
|
@ -2385,20 +2416,36 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event)
|
|||
m_selection == ssLower ? correct_lower_value() : correct_higher_value();
|
||||
if (!m_selection) m_selection = ssHigher;
|
||||
}
|
||||
else if (is_point_in_rect(pos, m_rect_revert_icon)) {
|
||||
// discard all color changes
|
||||
SetLowerValue(m_min_value);
|
||||
SetHigherValue(m_max_value);
|
||||
|
||||
m_selection == ssLower ? correct_lower_value() : correct_higher_value();
|
||||
if (!m_selection) m_selection = ssHigher;
|
||||
|
||||
m_ticks.clear();
|
||||
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
|
||||
}
|
||||
else
|
||||
detect_selected_slider(pos);
|
||||
|
||||
if (!m_selection && m_is_enabled_tick_manipulation) {
|
||||
const auto tick = is_point_near_tick(pos);
|
||||
if (tick >= 0)
|
||||
if (!m_selection) {
|
||||
const int tick_val = is_point_near_tick(pos);
|
||||
/* Set current thumb position to the nearest tick (if it is)
|
||||
* OR to a value corresponding to the mouse click
|
||||
* */
|
||||
const int mouse_val = tick_val >= 0 && m_is_enabled_tick_manipulation ? tick_val :
|
||||
get_value_from_position(pos.x, pos.y);
|
||||
if (mouse_val >= 0)
|
||||
{
|
||||
if (abs(tick - m_lower_value) < abs(tick - m_higher_value)) {
|
||||
SetLowerValue(tick);
|
||||
if (abs(mouse_val - m_lower_value) < abs(mouse_val - m_higher_value)) {
|
||||
SetLowerValue(mouse_val);
|
||||
correct_lower_value();
|
||||
m_selection = ssLower;
|
||||
}
|
||||
else {
|
||||
SetHigherValue(tick);
|
||||
SetHigherValue(mouse_val);
|
||||
correct_higher_value();
|
||||
m_selection = ssHigher;
|
||||
}
|
||||
|
@ -2438,9 +2485,13 @@ void DoubleSlider::OnMotion(wxMouseEvent& event)
|
|||
|
||||
const wxClientDC dc(this);
|
||||
const wxPoint pos = event.GetLogicalPosition(dc);
|
||||
|
||||
m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon);
|
||||
bool is_revert_icon_focused = false;
|
||||
|
||||
if (!m_is_left_down && !m_is_one_layer) {
|
||||
m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action);
|
||||
is_revert_icon_focused = !m_ticks.empty() && is_point_in_rect(pos, m_rect_revert_icon);
|
||||
}
|
||||
else if (m_is_left_down || m_is_right_down) {
|
||||
if (m_selection == ssLower) {
|
||||
|
@ -2460,6 +2511,12 @@ void DoubleSlider::OnMotion(wxMouseEvent& event)
|
|||
Update();
|
||||
event.Skip();
|
||||
|
||||
// Set tooltips with information for each icon
|
||||
const wxString tooltip = m_is_one_layer_icon_focesed ? _(L("One layer mode")) :
|
||||
m_is_action_icon_focesed ? _(L("Add/Del color change")) :
|
||||
is_revert_icon_focused ? _(L("Discard all color changes")) : "";
|
||||
this->SetToolTip(tooltip);
|
||||
|
||||
if (action)
|
||||
{
|
||||
wxCommandEvent e(wxEVT_SCROLL_CHANGED);
|
||||
|
@ -2610,7 +2667,9 @@ void DoubleSlider::OnChar(wxKeyEvent& event)
|
|||
|
||||
void DoubleSlider::OnRightDown(wxMouseEvent& event)
|
||||
{
|
||||
if (HasCapture()) return;
|
||||
this->CaptureMouse();
|
||||
|
||||
const wxClientDC dc(this);
|
||||
detect_selected_slider(event.GetLogicalPosition(dc));
|
||||
if (!m_selection)
|
||||
|
|
|
@ -766,6 +766,7 @@ protected:
|
|||
void draw_ticks(wxDC& dc);
|
||||
void draw_colored_band(wxDC& dc);
|
||||
void draw_one_layer_icon(wxDC& dc);
|
||||
void draw_revert_icon(wxDC& dc);
|
||||
void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection);
|
||||
void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection);
|
||||
void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const;
|
||||
|
@ -807,6 +808,7 @@ private:
|
|||
ScalableBitmap m_bmp_one_layer_lock_off;
|
||||
ScalableBitmap m_bmp_one_layer_unlock_on;
|
||||
ScalableBitmap m_bmp_one_layer_unlock_off;
|
||||
ScalableBitmap m_bmp_revert;
|
||||
SelectedSlider m_selection;
|
||||
bool m_is_left_down = false;
|
||||
bool m_is_right_down = false;
|
||||
|
@ -820,9 +822,11 @@ private:
|
|||
wxRect m_rect_higher_thumb;
|
||||
wxRect m_rect_tick_action;
|
||||
wxRect m_rect_one_layer_icon;
|
||||
wxRect m_rect_revert_icon;
|
||||
wxSize m_thumb_size;
|
||||
int m_tick_icon_dim;
|
||||
int m_lock_icon_dim;
|
||||
int m_revert_icon_dim;
|
||||
long m_style;
|
||||
float m_label_koef = 1.0;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <ostream>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
@ -45,10 +46,25 @@ static const char *INDEX_FILENAME = "index.idx";
|
|||
static const char *TMP_EXTENSION = ".download";
|
||||
|
||||
|
||||
void copy_file_fix(const fs::path &source, const fs::path &target)
|
||||
{
|
||||
static const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read; // aka 644
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("PresetUpdater: Copying %1% -> %2%") % source % target;
|
||||
|
||||
// Make sure the file has correct permission both before and after we copy over it
|
||||
if (fs::exists(target)) {
|
||||
fs::permissions(target, perms);
|
||||
}
|
||||
fs::copy_file(source, target, fs::copy_option::overwrite_if_exists);
|
||||
fs::permissions(target, perms);
|
||||
}
|
||||
|
||||
struct Update
|
||||
{
|
||||
fs::path source;
|
||||
fs::path target;
|
||||
|
||||
Version version;
|
||||
std::string vendor;
|
||||
std::string changelog_url;
|
||||
|
@ -61,7 +77,13 @@ struct Update
|
|||
, changelog_url(std::move(changelog_url))
|
||||
{}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os , const Update &self) {
|
||||
void install() const
|
||||
{
|
||||
copy_file_fix(source, target);
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Update &self)
|
||||
{
|
||||
os << "Update(" << self.source.string() << " -> " << self.target.string() << ')';
|
||||
return os;
|
||||
}
|
||||
|
@ -115,7 +137,6 @@ struct PresetUpdater::priv
|
|||
bool enabled_version_check;
|
||||
bool enabled_config_update;
|
||||
std::string version_check_url;
|
||||
bool had_config_update;
|
||||
|
||||
fs::path cache_path;
|
||||
fs::path rsrc_path;
|
||||
|
@ -135,13 +156,10 @@ struct PresetUpdater::priv
|
|||
void check_install_indices() const;
|
||||
Updates get_config_updates() const;
|
||||
void perform_updates(Updates &&updates, bool snapshot = true) const;
|
||||
|
||||
static void copy_file(const fs::path &from, const fs::path &to);
|
||||
};
|
||||
|
||||
PresetUpdater::priv::priv()
|
||||
: ver_slic3r(get_slic3r_version())
|
||||
, had_config_update(false)
|
||||
, cache_path(fs::path(Slic3r::data_dir()) / "cache")
|
||||
, rsrc_path(fs::path(resources_dir()) / "profiles")
|
||||
, vendor_path(fs::path(Slic3r::data_dir()) / "vendor")
|
||||
|
@ -273,7 +291,7 @@ void PresetUpdater::priv::sync_config(const std::set<VendorProfile> vendors)
|
|||
try {
|
||||
new_index.load(idx_path_temp);
|
||||
} catch (const std::exception & /* err */) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Failed loading a downloaded index %1% for vendor %2%: invalid index?") % idx_path_temp % vendor.name;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Could not load downloaded index %1% for vendor %2%: invalid index?") % idx_path_temp % vendor.name;
|
||||
continue;
|
||||
}
|
||||
if (new_index.version() < index.version()) {
|
||||
|
@ -323,7 +341,7 @@ void PresetUpdater::priv::check_install_indices() const
|
|||
|
||||
if (! fs::exists(path_in_cache)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Install index from resources: " << path.filename();
|
||||
copy_file(path, path_in_cache);
|
||||
copy_file_fix(path, path_in_cache);
|
||||
} else {
|
||||
Index idx_rsrc, idx_cache;
|
||||
idx_rsrc.load(path);
|
||||
|
@ -331,7 +349,7 @@ void PresetUpdater::priv::check_install_indices() const
|
|||
|
||||
if (idx_cache.version() < idx_rsrc.version()) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Update index from resources: " << path.filename();
|
||||
copy_file(path, path_in_cache);
|
||||
copy_file_fix(path, path_in_cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -346,6 +364,7 @@ Updates PresetUpdater::priv::get_config_updates() const
|
|||
|
||||
for (const auto idx : index_db) {
|
||||
auto bundle_path = vendor_path / (idx.vendor() + ".ini");
|
||||
auto bundle_path_idx = vendor_path / idx.path().filename();
|
||||
|
||||
if (! fs::exists(bundle_path)) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Bundle not present for index, skipping: " << idx.vendor();
|
||||
|
@ -360,8 +379,31 @@ Updates PresetUpdater::priv::get_config_updates() const
|
|||
const auto recommended = idx.recommended();
|
||||
if (recommended == idx.end()) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % idx.vendor();
|
||||
// XXX: what should be done here?
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load 'installed' idx, if any.
|
||||
// 'Installed' indices are kept alongside the bundle in the `vendor` subdir
|
||||
// for bookkeeping to remember a cancelled update and not offer it again.
|
||||
if (fs::exists(bundle_path_idx)) {
|
||||
Index existing_idx;
|
||||
try {
|
||||
existing_idx.load(bundle_path_idx);
|
||||
|
||||
const auto existing_recommended = existing_idx.recommended();
|
||||
if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) {
|
||||
// The user has already seen (and presumably rejected) this update
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Downloaded index for `%1%` is the same as installed one, not offering an update.") % idx.vendor();
|
||||
continue;
|
||||
}
|
||||
} catch (const std::exception & /* err */) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Could nto load installed index %1%") % bundle_path_idx;
|
||||
}
|
||||
}
|
||||
|
||||
copy_file_fix(idx.path(), bundle_path_idx);
|
||||
|
||||
const auto ver_current = idx.find(vp.config_version);
|
||||
const bool ver_current_found = ver_current != idx.end();
|
||||
if (! ver_current_found) {
|
||||
|
@ -453,10 +495,10 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
|
|||
for (const auto &update : updates.updates) {
|
||||
BOOST_LOG_TRIVIAL(info) << '\t' << update;
|
||||
|
||||
copy_file(update.source, update.target);
|
||||
update.install();
|
||||
|
||||
PresetBundle bundle;
|
||||
bundle.load_configbundle(update.target.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM);
|
||||
bundle.load_configbundle(update.source.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% conflicting presets")
|
||||
% (bundle.prints.size() + bundle.filaments.size() + bundle.printers.size());
|
||||
|
@ -491,19 +533,6 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
|
|||
}
|
||||
}
|
||||
|
||||
void PresetUpdater::priv::copy_file(const fs::path &source, const fs::path &target)
|
||||
{
|
||||
static const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read; // aka 644
|
||||
|
||||
// Make sure the file has correct permission both before and after we copy over it
|
||||
if (fs::exists(target)) {
|
||||
fs::permissions(target, perms);
|
||||
}
|
||||
fs::copy_file(source, target, fs::copy_option::overwrite_if_exists);
|
||||
fs::permissions(target, perms);
|
||||
}
|
||||
|
||||
|
||||
PresetUpdater::PresetUpdater() :
|
||||
p(new priv())
|
||||
{}
|
||||
|
@ -542,11 +571,6 @@ void PresetUpdater::slic3r_update_notify()
|
|||
{
|
||||
if (! p->enabled_version_check) { return; }
|
||||
|
||||
if (p->had_config_update) {
|
||||
BOOST_LOG_TRIVIAL(info) << "New Slic3r version available, but there was a configuration update, notification won't be displayed";
|
||||
return;
|
||||
}
|
||||
|
||||
auto* app_config = GUI::wxGetApp().app_config;
|
||||
const auto ver_online_str = app_config->get("version_online");
|
||||
const auto ver_online = Semver::parse(ver_online_str);
|
||||
|
@ -594,8 +618,6 @@ PresetUpdater::UpdateResult PresetUpdater::config_update() const
|
|||
incompats_map.emplace(std::make_pair(incompat.vendor, std::move(restrictions)));
|
||||
}
|
||||
|
||||
p->had_config_update = true; // This needs to be done before a dialog is shown because of OnIdle() + CallAfter() in Perl
|
||||
|
||||
GUI::MsgDataIncompatible dlg(std::move(incompats_map));
|
||||
const auto res = dlg.ShowModal();
|
||||
if (res == wxID_REPLACE) {
|
||||
|
@ -620,8 +642,6 @@ PresetUpdater::UpdateResult PresetUpdater::config_update() const
|
|||
updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url));
|
||||
}
|
||||
|
||||
p->had_config_update = true; // Ditto, see above
|
||||
|
||||
GUI::MsgUpdateConfig dlg(updates_msg);
|
||||
|
||||
const auto res = dlg.ShowModal();
|
||||
|
@ -631,7 +651,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update() const
|
|||
|
||||
// Reload global configuration
|
||||
auto *app_config = GUI::wxGetApp().app_config;
|
||||
GUI::wxGetApp().preset_bundle->load_presets(*app_config);
|
||||
GUI::wxGetApp().preset_bundle->load_presets(*app_config);
|
||||
GUI::wxGetApp().load_current_presets();
|
||||
return R_UPDATE_INSTALLED;
|
||||
} else {
|
||||
|
|
|
@ -137,6 +137,11 @@ public:
|
|||
Semver operator-(const Minor &b) const { Semver res(*this); return res -= b; }
|
||||
Semver operator-(const Patch &b) const { Semver res(*this); return res -= b; }
|
||||
|
||||
// Stream output
|
||||
friend std::ostream& operator<<(std::ostream& os, const Semver &self) {
|
||||
os << self.to_string();
|
||||
return os;
|
||||
}
|
||||
private:
|
||||
semver_t ver;
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@ struct SerialPortInfo {
|
|||
std::string friendly_name;
|
||||
bool is_printer = false;
|
||||
|
||||
SerialPortInfo() {}
|
||||
SerialPortInfo(std::string port) : port(port), friendly_name(std::move(port)) {}
|
||||
|
||||
bool id_match(unsigned id_vendor, unsigned id_product) const { return id_vendor == this->id_vendor && id_product == this->id_product; }
|
||||
};
|
||||
|
||||
|
|
1049
src/stb_dxt/stb_dxt.h
Normal file
1049
src/stb_dxt/stb_dxt.h
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue