This commit is contained in:
YuSanka 2019-07-31 09:49:45 +02:00
commit 8d3d4b3ae7
35 changed files with 1174 additions and 1111 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -5,16 +5,30 @@ const vec3 back_color_light = vec3(0.365, 0.365, 0.365);
uniform sampler2D texture;
uniform bool transparent_background;
uniform bool svg_source;
varying vec2 tex_coords;
void main()
vec4 svg_color()
{
// takes foreground from texture
vec4 fore_color = texture2D(texture, tex_coords);
// calculates radial gradient
vec3 back_color = vec3(mix(back_color_light, back_color_dark, smoothstep(0.0, 0.5, length(abs(tex_coords.xy) - vec2(0.5)))));
vec4 fore_color = texture2D(texture, tex_coords);
// blends foreground with background
gl_FragColor = vec4(mix(back_color, fore_color.rgb, fore_color.a), transparent_background ? fore_color.a : 1.0);
return vec4(mix(back_color, fore_color.rgb, fore_color.a), transparent_background ? fore_color.a : 1.0);
}
vec4 non_svg_color()
{
// takes foreground from texture
vec4 color = texture2D(texture, tex_coords);
return vec4(color.rgb, transparent_background ? color.a * 0.25 : color.a);
}
void main()
{
gl_FragColor = svg_source ? svg_color() : non_svg_color();
}

View File

@ -165,6 +165,7 @@ add_library(libslic3r STATIC
MinAreaBoundingBox.cpp
miniz_extension.hpp
miniz_extension.cpp
SLA/SLACommon.hpp
SLA/SLABoilerPlate.hpp
SLA/SLABasePool.hpp
SLA/SLABasePool.cpp

View File

@ -51,6 +51,16 @@ void PrintConfigDef::init_common_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionPoints{ Vec2d(0, 0), Vec2d(200, 0), Vec2d(200, 200), Vec2d(0, 200) });
def = this->add("bed_custom_texture", coString);
def->label = L("Bed custom texture");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("bed_custom_model", coString);
def->label = L("Bed custom model");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("layer_height", coFloat);
def->label = L("Layer height");
def->category = L("Layers and Perimeters");

View File

@ -24,37 +24,51 @@ enum class PointsStatus {
UserModified // User has done some edits.
};
struct SupportPoint {
struct SupportPoint
{
Vec3f pos;
float head_front_radius;
bool is_new_island;
bool is_new_island;
SupportPoint() :
pos(Vec3f::Zero()), head_front_radius(0.f), is_new_island(false) {}
SupportPoint()
: pos(Vec3f::Zero()), head_front_radius(0.f), is_new_island(false)
{}
SupportPoint(float pos_x, float pos_y, float pos_z, float head_radius, bool new_island) :
pos(pos_x, pos_y, pos_z), head_front_radius(head_radius), is_new_island(new_island) {}
SupportPoint(float pos_x,
float pos_y,
float pos_z,
float head_radius,
bool new_island)
: pos(pos_x, pos_y, pos_z)
, head_front_radius(head_radius)
, is_new_island(new_island)
{}
SupportPoint(Vec3f position, float head_radius, bool new_island) :
pos(position), head_front_radius(head_radius), is_new_island(new_island) {}
SupportPoint(Vec3f position, float head_radius, bool new_island)
: pos(position)
, head_front_radius(head_radius)
, is_new_island(new_island)
{}
SupportPoint(Eigen::Matrix<float, 5, 1, Eigen::DontAlign> data) :
pos(data(0), data(1), data(2)), head_front_radius(data(3)), is_new_island(data(4) != 0.f) {}
SupportPoint(Eigen::Matrix<float, 5, 1, Eigen::DontAlign> data)
: pos(data(0), data(1), data(2))
, head_front_radius(data(3))
, is_new_island(data(4) != 0.f)
{}
bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; }
bool operator!=(const SupportPoint& sp) const { return !(sp == (*this)); }
bool operator==(const SupportPoint &sp) const
{
return (pos == sp.pos) && head_front_radius == sp.head_front_radius &&
is_new_island == sp.is_new_island;
}
bool operator!=(const SupportPoint &sp) const { return !(sp == (*this)); }
template<class Archive> void serialize(Archive &ar) { ar(pos, head_front_radius, is_new_island); }
template<class Archive> void serialize(Archive &ar)
{
ar(pos, head_front_radius, is_new_island);
}
};
/// An index-triangle structure for libIGL functions. Also serves as an
/// alternative (raw) input format for the SLASupportTree
/*struct EigenMesh3D {
Eigen::MatrixXd V;
Eigen::MatrixXi F;
double ground_level = 0;
};*/
/// An index-triangle structure for libIGL functions. Also serves as an
/// alternative (raw) input format for the SLASupportTree
class EigenMesh3D {
@ -161,9 +175,6 @@ public:
}
};
} // namespace sla
} // namespace Slic3r

View File

@ -83,6 +83,42 @@ const unsigned SupportConfig::max_bridges_on_pillar = 3;
using Coordf = double;
using Portion = std::tuple<double, double>;
// Set this to true to enable full parallelism in this module.
// Only the well tested parts will be concurrent if this is set to false.
const constexpr bool USE_FULL_CONCURRENCY = false;
template<bool> struct _ccr {};
template<> struct _ccr<true>
{
using Mutex = SpinMutex;
template<class It, class Fn>
static inline void enumerate(It from, It to, Fn fn)
{
using TN = size_t;
auto iN = to - from;
TN N = iN < 0 ? 0 : TN(iN);
tbb::parallel_for(TN(0), N, [from, fn](TN n) { fn(*(from + n), n); });
}
};
template<> struct _ccr<false>
{
struct Mutex { inline void lock() {} inline void unlock() {} };
template<class It, class Fn>
static inline void enumerate(It from, It to, Fn fn)
{
for (auto it = from; it != to; ++it) fn(*it, it - from);
}
};
using ccr = _ccr<USE_FULL_CONCURRENCY>;
using ccr_seq = _ccr<false>;
using ccr_par = _ccr<true>;
inline Portion make_portion(double a, double b) {
return std::make_tuple(a, b);
}
@ -677,6 +713,7 @@ struct Pad {
}
tmesh.translate(0, 0, float(zlevel));
tmesh.require_shared_vertices();
}
bool empty() const { return tmesh.facets_count() == 0; }
@ -735,7 +772,10 @@ ClusteredPoints cluster(
// The support pad is considered an auxiliary geometry and is not part of the
// merged mesh. It can be retrieved using a dedicated method (pad())
class SLASupportTree::Impl {
std::map<unsigned, Head> m_heads;
// For heads it is beneficial to use the same IDs as for the support points.
std::vector<Head> m_heads;
std::vector<size_t> m_head_indices;
std::vector<Pillar> m_pillars;
std::vector<Junction> m_junctions;
std::vector<Bridge> m_bridges;
@ -743,8 +783,13 @@ class SLASupportTree::Impl {
Controller m_ctl;
Pad m_pad;
using Mutex = ccr::Mutex;
mutable Mutex m_mutex;
mutable TriangleMesh meshcache; mutable bool meshcache_valid = false;
mutable double model_height = 0; // the full height of the model
public:
double ground_level = 0;
@ -753,37 +798,49 @@ public:
const Controller& ctl() const { return m_ctl; }
template<class...Args> Head& add_head(unsigned id, Args&&... args) {
auto el = m_heads.emplace(std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(std::forward<Args>(args)...));
el.first->second.id = id;
template<class...Args> Head& add_head(unsigned id, Args&&... args)
{
std::lock_guard<Mutex> lk(m_mutex);
m_heads.emplace_back(std::forward<Args>(args)...);
m_heads.back().id = id;
if (id >= m_head_indices.size()) m_head_indices.resize(id + 1);
m_head_indices[id] = m_heads.size() - 1;
meshcache_valid = false;
return el.first->second;
return m_heads.back();
}
template<class...Args> Pillar& add_pillar(unsigned headid, Args&&... args) {
auto it = m_heads.find(headid);
assert(it != m_heads.end());
Head& head = it->second;
template<class...Args> Pillar& add_pillar(unsigned headid, Args&&... args)
{
std::lock_guard<Mutex> lk(m_mutex);
assert(headid < m_head_indices.size());
Head &head = m_heads[m_head_indices[headid]];
m_pillars.emplace_back(head, std::forward<Args>(args)...);
Pillar& pillar = m_pillars.back();
pillar.id = long(m_pillars.size() - 1);
head.pillar_id = pillar.id;
pillar.start_junction_id = head.id;
pillar.starts_from_head = true;
meshcache_valid = false;
return m_pillars.back();
}
void increment_bridges(const Pillar& pillar) {
void increment_bridges(const Pillar& pillar)
{
std::lock_guard<Mutex> lk(m_mutex);
assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size())
m_pillars[size_t(pillar.id)].bridges++;
}
void increment_links(const Pillar& pillar) {
void increment_links(const Pillar& pillar)
{
std::lock_guard<Mutex> lk(m_mutex);
assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
if(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size())
@ -792,6 +849,7 @@ public:
template<class...Args> Pillar& add_pillar(Args&&...args)
{
std::lock_guard<Mutex> lk(m_mutex);
m_pillars.emplace_back(std::forward<Args>(args)...);
Pillar& pillar = m_pillars.back();
pillar.id = long(m_pillars.size() - 1);
@ -800,113 +858,125 @@ public:
return m_pillars.back();
}
const Head& pillar_head(long pillar_id) const {
const Head& pillar_head(long pillar_id) const
{
std::lock_guard<Mutex> lk(m_mutex);
assert(pillar_id >= 0 && pillar_id < long(m_pillars.size()));
const Pillar& p = m_pillars[size_t(pillar_id)];
assert(p.starts_from_head && p.start_junction_id >= 0);
auto it = m_heads.find(unsigned(p.start_junction_id));
assert(it != m_heads.end());
return it->second;
assert(size_t(p.start_junction_id) < m_head_indices.size());
return m_heads[m_head_indices[p.start_junction_id]];
}
const Pillar& head_pillar(unsigned headid) const {
auto it = m_heads.find(headid);
assert(it != m_heads.end());
const Head& h = it->second;
const Pillar& head_pillar(unsigned headid) const
{
std::lock_guard<Mutex> lk(m_mutex);
assert(headid < m_head_indices.size());
const Head& h = m_heads[m_head_indices[headid]];
assert(h.pillar_id >= 0 && h.pillar_id < long(m_pillars.size()));
return pillar(h.pillar_id);
return m_pillars[size_t(h.pillar_id)];
}
template<class...Args> const Junction& add_junction(Args&&... args) {
template<class...Args> const Junction& add_junction(Args&&... args)
{
std::lock_guard<Mutex> lk(m_mutex);
m_junctions.emplace_back(std::forward<Args>(args)...);
m_junctions.back().id = long(m_junctions.size() - 1);
meshcache_valid = false;
return m_junctions.back();
}
template<class...Args> const Bridge& add_bridge(Args&&... args) {
template<class...Args> const Bridge& add_bridge(Args&&... args)
{
std::lock_guard<Mutex> lk(m_mutex);
m_bridges.emplace_back(std::forward<Args>(args)...);
m_bridges.back().id = long(m_bridges.size() - 1);
meshcache_valid = false;
return m_bridges.back();
}
template<class...Args>
const CompactBridge& add_compact_bridge(Args&&...args) {
template<class...Args> const CompactBridge& add_compact_bridge(Args&&...args)
{
std::lock_guard<Mutex> lk(m_mutex);
m_compact_bridges.emplace_back(std::forward<Args>(args)...);
m_compact_bridges.back().id = long(m_compact_bridges.size() - 1);
meshcache_valid = false;
return m_compact_bridges.back();
}
const std::map<unsigned, Head>& heads() const { return m_heads; }
Head& head(unsigned idx) {
Head &head(unsigned id)
{
std::lock_guard<Mutex> lk(m_mutex);
assert(id < m_head_indices.size());
meshcache_valid = false;
auto it = m_heads.find(idx);
assert(it != m_heads.end());
return it->second;
}
const std::vector<Pillar>& pillars() const { return m_pillars; }
const std::vector<Bridge>& bridges() const { return m_bridges; }
const std::vector<Junction>& junctions() const { return m_junctions; }
const std::vector<CompactBridge>& compact_bridges() const {
return m_compact_bridges;
return m_heads[m_head_indices[id]];
}
template<class T> inline const Pillar& pillar(T id) const {
static_assert(std::is_integral<T>::value, "Invalid index type");
inline size_t pillarcount() const {
std::lock_guard<Mutex> lk(m_mutex);
return m_pillars.size();
}
template<class T> inline IntegerOnly<T, const Pillar&> pillar(T id) const
{
std::lock_guard<Mutex> lk(m_mutex);
assert(id >= 0 && size_t(id) < m_pillars.size() &&
size_t(id) < std::numeric_limits<size_t>::max());
return m_pillars[size_t(id)];
}
const Pad& create_pad(const TriangleMesh& object_supports,
const ExPolygons& modelbase,
const PoolConfig& cfg) {
const Pad &create_pad(const TriangleMesh &object_supports,
const ExPolygons & modelbase,
const PoolConfig & cfg)
{
m_pad = Pad(object_supports, modelbase, ground_level, cfg);
return m_pad;
}
void remove_pad() {
m_pad = Pad();
}
void remove_pad() { m_pad = Pad(); }
const Pad& pad() const { return m_pad; }
// WITHOUT THE PAD!!!
const TriangleMesh& merged_mesh() const {
if(meshcache_valid) return meshcache;
const TriangleMesh &merged_mesh() const
{
if (meshcache_valid) return meshcache;
Contour3D merged;
for(auto& headel : heads()) {
if(m_ctl.stopcondition()) break;
if(headel.second.is_valid())
merged.merge(headel.second.mesh);
for (auto &head : m_heads) {
if (m_ctl.stopcondition()) break;
if (head.is_valid()) merged.merge(head.mesh);
}
for(auto& stick : pillars()) {
if(m_ctl.stopcondition()) break;
for (auto &stick : m_pillars) {
if (m_ctl.stopcondition()) break;
merged.merge(stick.mesh);
merged.merge(stick.base);
}
for(auto& j : junctions()) {
if(m_ctl.stopcondition()) break;
for (auto &j : m_junctions) {
if (m_ctl.stopcondition()) break;
merged.merge(j.mesh);
}
for(auto& cb : compact_bridges()) {
if(m_ctl.stopcondition()) break;
for (auto &cb : m_compact_bridges) {
if (m_ctl.stopcondition()) break;
merged.merge(cb.mesh);
}
for(auto& bs : bridges()) {
if(m_ctl.stopcondition()) break;
for (auto &bs : m_bridges) {
if (m_ctl.stopcondition()) break;
merged.merge(bs.mesh);
}
if(m_ctl.stopcondition()) {
if (m_ctl.stopcondition()) {
// In case of failure we have to return an empty mesh
meshcache = TriangleMesh();
return meshcache;
@ -918,42 +988,44 @@ public:
// which will need this.
if (!meshcache.empty()) meshcache.require_shared_vertices();
// TODO: Is this necessary?
//meshcache.repair();
BoundingBoxf3&& bb = meshcache.bounding_box();
model_height = bb.max(Z) - bb.min(Z);
BoundingBoxf3 &&bb = meshcache.bounding_box();
model_height = bb.max(Z) - bb.min(Z);
meshcache_valid = true;
return meshcache;
}
// WITH THE PAD
double full_height() const {
if(merged_mesh().empty() && !pad().empty())
double full_height() const
{
if (merged_mesh().empty() && !pad().empty())
return get_pad_fullheight(pad().cfg);
double h = mesh_height();
if(!pad().empty()) h += sla::get_pad_elevation(pad().cfg);
if (!pad().empty()) h += sla::get_pad_elevation(pad().cfg);
return h;
}
// WITHOUT THE PAD!!!
double mesh_height() const {
if(!meshcache_valid) merged_mesh();
double mesh_height() const
{
if (!meshcache_valid) merged_mesh();
return model_height;
}
// Intended to be called after the generation is fully complete
void clear_support_data() {
void merge_and_cleanup()
{
merged_mesh(); // in case the mesh is not generated, it should be...
m_heads.clear();
m_pillars.clear();
m_junctions.clear();
m_bridges.clear();
m_compact_bridges.clear();
}
// Doing clear() does not garantee to release the memory.
m_heads = {};
m_head_indices = {};
m_pillars = {};
m_junctions = {};
m_bridges = {};
m_compact_bridges = {};
}
};
// This function returns the position of the centroid in the input 'clust'
@ -1122,11 +1194,10 @@ class SLASupportTree::Algorithm {
// Now a and b vectors are perpendicular to v and to each other.
// Together they define the plane where we have to iterate with the
// given angles in the 'phis' vector
tbb::parallel_for(size_t(0), phis.size(),
[&phis, &hits, &m, sd, r_pin, r_back, s, a, b, c]
(size_t i)
ccr_par::enumerate(phis.begin(), phis.end(),
[&hits, &m, sd, r_pin, r_back, s, a, b, c]
(double phi, size_t i)
{
double& phi = phis[i];
double sinphi = std::sin(phi);
double cosphi = std::cos(phi);
@ -1226,11 +1297,10 @@ class SLASupportTree::Algorithm {
// Hit results
std::array<HitResult, SAMPLES> hits;
tbb::parallel_for(size_t(0), phis.size(),
[&m, &phis, a, b, sd, dir, r, s, ins_check, &hits]
(size_t i)
ccr_par::enumerate(phis.begin(), phis.end(),
[&m, a, b, sd, dir, r, s, ins_check, &hits]
(double phi, size_t i)
{
double& phi = phis[i];
double sinphi = std::sin(phi);
double cosphi = std::cos(phi);
@ -1458,7 +1528,7 @@ class SLASupportTree::Algorithm {
if(nearest_id >= 0) {
auto nearpillarID = unsigned(nearest_id);
if(nearpillarID < m_result.pillars().size()) {
if(nearpillarID < m_result.pillarcount()) {
if(!connect_to_nearpillar(head, nearpillarID)) {
nearest_id = -1; // continue searching
spindex.remove(ne); // without the current pillar
@ -1647,11 +1717,17 @@ public:
using libnest2d::opt::GeneticOptimizer;
using libnest2d::opt::StopCriteria;
for(unsigned i = 0, fidx = 0; i < filtered_indices.size(); ++i)
ccr::Mutex mutex;
auto addfn = [&mutex](PtIndices &container, unsigned val) {
std::lock_guard<ccr::Mutex> lk(mutex);
container.emplace_back(val);
};
ccr::enumerate(filtered_indices.begin(), filtered_indices.end(),
[this, &nmls, addfn](unsigned fidx, size_t i)
{
m_thr();
fidx = filtered_indices[i];
auto n = nmls.row(i);
// for all normals we generate the spherical coordinates and
@ -1709,20 +1785,20 @@ public:
auto oresult = solver.optimize_max(
[this, pin_r, w, hp](double plr, double azm)
{
auto n = Vec3d(std::cos(azm) * std::sin(plr),
std::sin(azm) * std::sin(plr),
std::cos(plr)).normalized();
{
auto n = Vec3d(std::cos(azm) * std::sin(plr),
std::sin(azm) * std::sin(plr),
std::cos(plr)).normalized();
double score = pinhead_mesh_intersect( hp, n, pin_r,
m_cfg.head_back_radius_mm, w);
double score = pinhead_mesh_intersect(
hp, n, pin_r, m_cfg.head_back_radius_mm, w);
return score;
},
initvals(polar, azimuth), // start with what we have
bound(3*PI/4, PI), // Must not exceed the tilt limit
bound(-PI, PI) // azimuth can be a full search
);
return score;
},
initvals(polar, azimuth), // start with what we have
bound(3*PI/4, PI), // Must not exceed the tilt limit
bound(-PI, PI) // azimuth can be a full search
);
if(oresult.score > w) {
polar = std::get<0>(oresult.optimum);
@ -1740,18 +1816,18 @@ public:
if (t.distance() > w) {
// Check distance from ground, we might have zero elevation.
if (hp(Z) + w * nn(Z) < m_result.ground_level) {
m_iheadless.emplace_back(fidx);
addfn(m_iheadless, fidx);
} else {
// mark the point for needing a head.
m_iheads.emplace_back(fidx);
addfn(m_iheads, fidx);
}
} else if (polar >= 3 * PI / 4) {
// Headless supports do not tilt like the headed ones
// so the normal should point almost to the ground.
m_iheadless.emplace_back(fidx);
addfn(m_iheadless, fidx);
}
}
}
});
m_thr();
}
@ -1939,11 +2015,17 @@ public:
};
std::vector<unsigned> modelpillars;
ccr::Mutex mutex;
// TODO: connect these to the ground pillars if possible
for(auto item : m_iheads_onmodel) { m_thr();
unsigned idx = item.first;
EigenMesh3D::hit_result hit = item.second;
ccr::enumerate(m_iheads_onmodel.begin(), m_iheads_onmodel.end(),
[this, routedown, &modelpillars, &mutex]
(const std::pair<unsigned, EigenMesh3D::hit_result> &el,
size_t)
{
m_thr();
unsigned idx = el.first;
EigenMesh3D::hit_result hit = el.second;
auto& head = m_result.head(idx);
Vec3d hjp = head.junction_point();
@ -1952,7 +2034,7 @@ public:
// Search nearby pillar
// /////////////////////////////////////////////////////////////////
if(search_pillar_and_connect(head)) { head.transform(); continue; }
if(search_pillar_and_connect(head)) { head.transform(); return; }
// /////////////////////////////////////////////////////////////////
// Try straight path
@ -1974,7 +2056,7 @@ public:
}
if(std::isinf(tdown)) { // we heave found a route to the ground
routedown(head, head.dir, d); continue;
routedown(head, head.dir, d); return;
}
// /////////////////////////////////////////////////////////////////
@ -2036,7 +2118,7 @@ public:
}
if(std::isinf(tdown)) { // we heave found a route to the ground
routedown(head, bridgedir, d); continue;
routedown(head, bridgedir, d); return;
}
// /////////////////////////////////////////////////////////////////
@ -2079,8 +2161,9 @@ public:
pill.base = tailhead.mesh;
// Experimental: add the pillar to the index for cascading
std::lock_guard<ccr::Mutex> lk(mutex);
modelpillars.emplace_back(unsigned(pill.id));
continue;
return;
}
// We have failed to route this head.
@ -2088,7 +2171,7 @@ public:
<< "Failed to route model facing support point."
<< " ID: " << idx;
head.invalidate();
}
});
for(auto pillid : modelpillars) {
auto& pillar = m_result.pillar(pillid);
@ -2176,7 +2259,7 @@ public:
// Search for the pair amongst the remembered pairs
if(pairs.find(hashval) != pairs.end()) continue;
const Pillar& neighborpillar = m_result.pillars()[re.second];
const Pillar& neighborpillar = m_result.pillar(re.second);
// this neighbor is occupied, skip
if(neighborpillar.links >= neighbors) continue;
@ -2212,7 +2295,7 @@ public:
// lonely pillars. One or even two additional pillar might get inserted
// depending on the length of the lonely pillar.
size_t pillarcount = m_result.pillars().size();
size_t pillarcount = m_result.pillarcount();
// Again, go through all pillars, this time in the whole support tree
// not just the index.
@ -2364,6 +2447,8 @@ public:
m_result.add_compact_bridge(sp, ej, n, R, !std::isinf(dist));
}
}
void merge_result() { m_result.merge_and_cleanup(); }
};
bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
@ -2386,6 +2471,7 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
ROUTING_NONGROUND,
CASCADE_PILLARS,
HEADLESS,
MERGE_RESULT,
DONE,
ABORT,
NUM_STEPS
@ -2413,6 +2499,8 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
std::bind(&Algorithm::routing_headless, &alg),
std::bind(&Algorithm::merge_result, &alg),
[] () {
// Done
},
@ -2427,7 +2515,7 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
if(cfg.ground_facing_only) {
program[ROUTING_NONGROUND] = []() {
BOOST_LOG_TRIVIAL(info)
<< "Skipping model-facing supports as requested.";
<< "Skipping model-facing supports as requested.";
};
program[HEADLESS] = []() {
BOOST_LOG_TRIVIAL(info) << "Skipping headless stick generation as"
@ -2446,6 +2534,7 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
"Routing supports to model surface",
"Interconnecting pillars",
"Processing small holes",
"Merging support mesh",
"Done",
"Abort"
};
@ -2458,7 +2547,8 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
60,
70,
80,
90,
85,
99,
100,
0
};
@ -2473,11 +2563,13 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
case ROUTING_GROUND: pc = ROUTING_NONGROUND; break;
case ROUTING_NONGROUND: pc = CASCADE_PILLARS; break;
case CASCADE_PILLARS: pc = HEADLESS; break;
case HEADLESS: pc = DONE; break;
case HEADLESS: pc = MERGE_RESULT; break;
case MERGE_RESULT: pc = DONE; break;
case DONE:
case ABORT: break;
default: ;
}
ctl.statuscb(stepstate[pc], stepstr[pc]);
};
@ -2504,44 +2596,40 @@ void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const {
outmesh.merge(get_pad());
}
std::vector<ExPolygons> SLASupportTree::slice(float layerh, float init_layerh) const
std::vector<ExPolygons> SLASupportTree::slice(
const std::vector<float> &heights, float cr) const
{
if(init_layerh < 0) init_layerh = layerh;
auto& stree = get();
const TriangleMesh &sup_mesh = m_impl->merged_mesh();
const TriangleMesh &pad_mesh = get_pad();
const auto modelh = float(stree.full_height());
auto gndlvl = float(this->m_impl->ground_level);
const Pad& pad = m_impl->pad();
if(!pad.empty()) gndlvl -= float(get_pad_elevation(pad.cfg));
std::vector<float> heights;
heights.reserve(size_t(modelh/layerh) + 1);
for(float h = gndlvl + init_layerh; h < gndlvl + modelh; h += layerh) {
heights.emplace_back(h);
std::vector<ExPolygons> sup_slices;
if (!sup_mesh.empty()) {
TriangleMeshSlicer sup_slicer(&sup_mesh);
sup_slicer.slice(heights, cr, &sup_slices, m_impl->ctl().cancelfn);
}
TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad());
if (!fullmesh.empty()) fullmesh.require_shared_vertices();
TriangleMeshSlicer slicer(&fullmesh);
std::vector<ExPolygons> ret;
slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn);
auto bb = pad_mesh.bounding_box();
auto maxzit = std::upper_bound(heights.begin(), heights.end(), bb.max.z());
return ret;
}
auto padgrid = reserve_vector<float>(heights.end() - maxzit);
std::copy(heights.begin(), maxzit, std::back_inserter(padgrid));
std::vector<ExPolygons> SLASupportTree::slice(const std::vector<float> &heights,
float cr) const
{
TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad());
if (!fullmesh.empty()) fullmesh.require_shared_vertices();
TriangleMeshSlicer slicer(&fullmesh);
std::vector<ExPolygons> ret;
slicer.slice(heights, cr, &ret, get().ctl().cancelfn);
std::vector<ExPolygons> pad_slices;
if (!pad_mesh.empty()) {
TriangleMeshSlicer pad_slicer(&pad_mesh);
pad_slicer.slice(padgrid, cr, &pad_slices, m_impl->ctl().cancelfn);
}
return ret;
size_t len = std::min(heights.size(), pad_slices.size());
len = std::min(len, sup_slices.size());
for (size_t i = 0; i < len; ++i) {
std::copy(pad_slices[i].begin(), pad_slices[i].end(),
std::back_inserter(sup_slices[i]));
pad_slices[i] = {};
}
return sup_slices;
}
const TriangleMesh &SLASupportTree::add_pad(const ExPolygons& modelbase,
@ -2568,16 +2656,6 @@ SLASupportTree::SLASupportTree(const std::vector<SupportPoint> &points,
{
m_impl->ground_level = emesh.ground_level() - cfg.object_elevation_mm;
generate(points, emesh, cfg, ctl);
m_impl->clear_support_data();
}
SLASupportTree::SLASupportTree(const SLASupportTree &c):
m_impl(new Impl(*c.m_impl)) {}
SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c)
{
m_impl = make_unique<Impl>(*c.m_impl);
return *this;
}
SLASupportTree::~SLASupportTree() {}

View File

@ -172,8 +172,8 @@ public:
const SupportConfig& cfg = {},
const Controller& ctl = {});
SLASupportTree(const SLASupportTree&);
SLASupportTree& operator=(const SLASupportTree&);
SLASupportTree(const SLASupportTree&) = delete;
SLASupportTree& operator=(const SLASupportTree&) = delete;
~SLASupportTree();
@ -183,9 +183,6 @@ public:
void merged_mesh_with_pad(TriangleMesh&) const;
/// Get the sliced 2d layers of the support geometry.
std::vector<ExPolygons> slice(float layerh, float init_layerh = -1.0) const;
std::vector<ExPolygons> slice(const std::vector<float> &,
float closing_radius) const;

View File

@ -928,12 +928,14 @@ void SLAPrint::process()
double d = ostepd * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0;
double init = m_report_status.status();
ctl.statuscb = [this, d, init](unsigned st, const std::string&)
ctl.statuscb = [this, d, init](unsigned st, const std::string &logmsg)
{
double current = init + st * d;
if(std::round(m_report_status.status()) < std::round(current))
m_report_status(*this, current,
OBJ_STEP_LABELS(slaposSupportTree));
OBJ_STEP_LABELS(slaposSupportTree),
SlicingStatus::DEFAULT,
logmsg);
};
@ -1914,11 +1916,17 @@ std::string SLAPrintStatistics::finalize_output_path(const std::string &path_in)
return final_path;
}
void SLAPrint::StatusReporter::operator()(
SLAPrint &p, double st, const std::string &msg, unsigned flags)
void SLAPrint::StatusReporter::operator()(SLAPrint & p,
double st,
const std::string &msg,
unsigned flags,
const std::string &logmsg)
{
m_st = st;
BOOST_LOG_TRIVIAL(info) << st << "% " << msg << log_memory_info();
BOOST_LOG_TRIVIAL(info)
<< st << "% " << msg << (logmsg.empty() ? "" : ": ") << logmsg
<< log_memory_info();
p.set_status(int(std::round(st)), msg, flags);
}

View File

@ -448,15 +448,20 @@ private:
// Estimated print time, material consumed.
SLAPrintStatistics m_print_statistics;
class StatusReporter {
class StatusReporter
{
double m_st = 0;
public:
void operator() (SLAPrint& p, double st, const std::string& msg,
unsigned flags = SlicingStatus::DEFAULT);
void operator()(SLAPrint & p,
double st,
const std::string &msg,
unsigned flags = SlicingStatus::DEFAULT,
const std::string &logmsg = "");
double status() const { return m_st; }
} m_report_status;
friend SLAPrintObject;
};

View File

@ -32,13 +32,4 @@
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1)
//====================
// 1.42.0.alpha7 techs
//====================
#define ENABLE_1_42_0_ALPHA7 1
// Printbed textures generated from svg files
#define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7)
#endif // _technologies_h_

View File

@ -18,10 +18,9 @@ wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -
#ifdef __APPLE__
m_user_drawn_background = false;
#endif /*__APPLE__*/
Bind(wxEVT_PAINT, ([this](wxPaintEvent &/* e */) { repaint(); }));
Bind(wxEVT_SIZE, ([this](wxSizeEvent & /* e */) { Refresh(); }));
}
void Bed_2D::repaint()
void Bed_2D::repaint(const std::vector<Vec2d>& shape)
{
wxAutoBufferedPaintDC dc(this);
auto cw = GetSize().GetWidth();
@ -41,7 +40,7 @@ void Bed_2D::repaint()
dc.DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
}
if (m_bed_shape.empty())
if (shape.empty())
return;
// reduce size to have some space around the drawn shape
@ -52,10 +51,9 @@ void Bed_2D::repaint()
auto ccenter = cbb.center();
// get bounding box of bed shape in G - code coordinates
auto bed_shape = m_bed_shape;
auto bed_polygon = Polygon::new_scale(m_bed_shape);
auto bb = BoundingBoxf(m_bed_shape);
bb.merge(Vec2d(0, 0)); // origin needs to be in the visible area
auto bed_polygon = Polygon::new_scale(shape);
auto bb = BoundingBoxf(shape);
bb.merge(Vec2d(0, 0)); // origin needs to be in the visible area
auto bw = bb.size()(0);
auto bh = bb.size()(1);
auto bcenter = bb.center();
@ -73,8 +71,8 @@ void Bed_2D::repaint()
// draw bed fill
dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxBRUSHSTYLE_SOLID));
wxPointList pt_list;
for (auto pt: m_bed_shape)
{
for (auto pt : shape)
{
Point pt_pix = to_pixels(pt, ch);
pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1)));
}
@ -155,13 +153,13 @@ void Bed_2D::repaint()
// convert G - code coordinates into pixels
Point Bed_2D::to_pixels(Vec2d point, int height)
Point Bed_2D::to_pixels(const Vec2d& point, int height)
{
auto p = point * m_scale_factor + m_shift;
return Point(p(0) + Border, height - p(1) + Border);
}
void Bed_2D::set_pos(Vec2d pos)
void Bed_2D::set_pos(const Vec2d& pos)
{
m_pos = pos;
Refresh();

View File

@ -17,16 +17,13 @@ class Bed_2D : public wxPanel
Vec2d m_shift = Vec2d::Zero();
Vec2d m_pos = Vec2d::Zero();
Point to_pixels(Vec2d point, int height);
void repaint();
void set_pos(Vec2d pos);
Point to_pixels(const Vec2d& point, int height);
void set_pos(const Vec2d& pos);
public:
Bed_2D(wxWindow* parent);
~Bed_2D() {}
std::vector<Vec2d> m_bed_shape;
explicit Bed_2D(wxWindow* parent);
void repaint(const std::vector<Vec2d>& shape);
};

View File

@ -14,6 +14,7 @@
#include <GL/glew.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem/operations.hpp>
static const float GROUND_Z = -0.02f;
@ -22,7 +23,6 @@ namespace GUI {
bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords)
{
#if ENABLE_TEXTURES_FROM_SVG
m_vertices.clear();
unsigned int v_size = 3 * (unsigned int)triangles.size();
@ -82,75 +82,12 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool
}
}
}
#else
m_vertices.clear();
m_tex_coords.clear();
unsigned int v_size = 9 * (unsigned int)triangles.size();
unsigned int t_size = 6 * (unsigned int)triangles.size();
if (v_size == 0)
return false;
m_vertices = std::vector<float>(v_size, 0.0f);
if (generate_tex_coords)
m_tex_coords = std::vector<float>(t_size, 0.0f);
float min_x = unscale<float>(triangles[0].points[0](0));
float min_y = unscale<float>(triangles[0].points[0](1));
float max_x = min_x;
float max_y = min_y;
unsigned int v_coord = 0;
unsigned int t_coord = 0;
for (const Polygon& t : triangles)
{
for (unsigned int v = 0; v < 3; ++v)
{
const Point& p = t.points[v];
float x = unscale<float>(p(0));
float y = unscale<float>(p(1));
m_vertices[v_coord++] = x;
m_vertices[v_coord++] = y;
m_vertices[v_coord++] = z;
if (generate_tex_coords)
{
m_tex_coords[t_coord++] = x;
m_tex_coords[t_coord++] = y;
min_x = std::min(min_x, x);
max_x = std::max(max_x, x);
min_y = std::min(min_y, y);
max_y = std::max(max_y, y);
}
}
}
if (generate_tex_coords)
{
float size_x = max_x - min_x;
float size_y = max_y - min_y;
if ((size_x != 0.0f) && (size_y != 0.0f))
{
float inv_size_x = 1.0f / size_x;
float inv_size_y = -1.0f / size_y;
for (unsigned int i = 0; i < m_tex_coords.size(); i += 2)
{
m_tex_coords[i] = (m_tex_coords[i] - min_x) * inv_size_x;
m_tex_coords[i + 1] = (m_tex_coords[i + 1] - min_y) * inv_size_y;
}
}
}
#endif // ENABLE_TEXTURES_FROM_SVG
return true;
}
bool GeometryBuffer::set_from_lines(const Lines& lines, float z)
{
#if ENABLE_TEXTURES_FROM_SVG
m_vertices.clear();
unsigned int v_size = 2 * (unsigned int)lines.size();
@ -174,37 +111,14 @@ bool GeometryBuffer::set_from_lines(const Lines& lines, float z)
v2.position[2] = z;
++v_count;
}
#else
m_vertices.clear();
m_tex_coords.clear();
unsigned int size = 6 * (unsigned int)lines.size();
if (size == 0)
return false;
m_vertices = std::vector<float>(size, 0.0f);
unsigned int coord = 0;
for (const Line& l : lines)
{
m_vertices[coord++] = unscale<float>(l.a(0));
m_vertices[coord++] = unscale<float>(l.a(1));
m_vertices[coord++] = z;
m_vertices[coord++] = unscale<float>(l.b(0));
m_vertices[coord++] = unscale<float>(l.b(1));
m_vertices[coord++] = z;
}
#endif // ENABLE_TEXTURES_FROM_SVG
return true;
}
#if ENABLE_TEXTURES_FROM_SVG
const float* GeometryBuffer::get_vertices_data() const
{
return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr;
}
#endif // ENABLE_TEXTURES_FROM_SVG
const double Bed3D::Axes::Radius = 0.5;
const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius;
@ -274,22 +188,41 @@ void Bed3D::Axes::render_axis(double length) const
Bed3D::Bed3D()
: m_type(Custom)
#if ENABLE_TEXTURES_FROM_SVG
, m_custom_texture("")
, m_custom_model("")
, m_requires_canvas_update(false)
, m_vbo_id(0)
#endif // ENABLE_TEXTURES_FROM_SVG
, m_scale_factor(1.0f)
{
}
bool Bed3D::set_shape(const Pointfs& shape)
bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
{
EType new_type = detect_type(shape);
if (m_shape == shape && m_type == new_type)
// check that the passed custom texture filename is valid
std::string cst_texture(custom_texture);
if (!cst_texture.empty())
{
if ((!boost::algorithm::iends_with(custom_texture, ".png") && !boost::algorithm::iends_with(custom_texture, ".svg")) || !boost::filesystem::exists(custom_texture))
cst_texture = "";
}
// check that the passed custom texture filename is valid
std::string cst_model(custom_model);
if (!cst_model.empty())
{
if (!boost::algorithm::iends_with(custom_model, ".stl") || !boost::filesystem::exists(custom_model))
cst_model = "";
}
if ((m_shape == shape) && (m_type == new_type) && (m_custom_texture == cst_texture) && (m_custom_model == cst_model))
// No change, no need to update the UI.
return false;
m_shape = shape;
m_custom_texture = cst_texture;
m_custom_model = cst_model;
m_type = new_type;
calc_bounding_boxes();
@ -307,9 +240,9 @@ bool Bed3D::set_shape(const Pointfs& shape)
m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour;
#if ENABLE_TEXTURES_FROM_SVG
reset();
#endif // ENABLE_TEXTURES_FROM_SVG
m_texture.reset();
m_model.reset();
// Set the origin and size for painting of the coordinate system axes.
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
@ -329,11 +262,12 @@ Point Bed3D::point_projection(const Point& point) const
return m_polygon.point_projection(point);
}
#if ENABLE_TEXTURES_FROM_SVG
void Bed3D::render(GLCanvas3D* canvas, float theta, float scale_factor) const
void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor) const
{
m_scale_factor = scale_factor;
render_axes();
switch (m_type)
{
case MK2:
@ -354,51 +288,11 @@ void Bed3D::render(GLCanvas3D* canvas, float theta, float scale_factor) const
default:
case Custom:
{
render_custom();
render_custom(canvas, theta > 90.0f);
break;
}
}
}
#else
void Bed3D::render(float theta, float scale_factor) const
{
m_scale_factor = scale_factor;
if (m_shape.empty())
return;
switch (m_type)
{
case MK2:
{
render_prusa("mk2", theta);
break;
}
case MK3:
{
render_prusa("mk3", theta);
break;
}
case SL1:
{
render_prusa("sl1", theta);
break;
}
default:
case Custom:
{
render_custom();
break;
}
}
}
#endif // ENABLE_TEXTURES_FROM_SVG
void Bed3D::render_axes() const
{
if (!m_shape.empty())
m_axes.render();
}
void Bed3D::calc_bounding_boxes() const
{
@ -423,7 +317,7 @@ void Bed3D::calc_triangles(const ExPolygon& poly)
Polygons triangles;
poly.triangulate(&triangles);
if (!m_triangles.set_from_triangles(triangles, GROUND_Z, m_type != Custom))
if (!m_triangles.set_from_triangles(triangles, GROUND_Z, true))
printf("Unable to create bed triangles\n");
}
@ -495,31 +389,76 @@ Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const
return type;
}
#if ENABLE_TEXTURES_FROM_SVG
void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom) const
void Bed3D::render_axes() const
{
std::string tex_path = resources_dir() + "/icons/bed/" + key;
if (!m_shape.empty())
m_axes.render();
}
std::string model_path = resources_dir() + "/models/" + key;
void Bed3D::render_prusa(GLCanvas3D& canvas, const std::string& key, bool bottom) const
{
if (!bottom)
render_model(m_custom_model.empty() ? resources_dir() + "/models/" + key + "_bed.stl" : m_custom_model);
// use higher resolution images if graphic card and opengl version allow
GLint max_tex_size = GLCanvas3DManager::get_gl_info().get_max_tex_size();
render_texture(m_custom_texture.empty() ? resources_dir() + "/icons/bed/" + key + ".svg" : m_custom_texture, bottom, canvas);
}
std::string filename = tex_path + ".svg";
void Bed3D::render_texture(const std::string& filename, bool bottom, GLCanvas3D& canvas) const
{
if (filename.empty())
{
m_texture.reset();
render_default(bottom);
return;
}
if ((m_texture.get_id() == 0) || (m_texture.get_source() != filename))
{
// 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;
}
m_texture.reset();
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_svg_file(filename, true, true, true, max_tex_size))
if (boost::algorithm::iends_with(filename, ".svg"))
{
render_custom();
// use higher resolution images if graphic card and opengl version allow
GLint max_tex_size = GLCanvas3DManager::get_gl_info().get_max_tex_size();
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != filename))
{
// 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_default(bottom);
return;
}
}
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_svg_file(filename, true, true, true, max_tex_size))
{
render_default(bottom);
return;
}
}
else if (boost::algorithm::iends_with(filename, ".png"))
{
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != filename))
{
if (!m_temp_texture.load_from_file(filename, false, GLTexture::None, false))
{
render_default(bottom);
return;
}
}
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_file(filename, true, GLTexture::MultiThreaded, true))
{
render_default(bottom);
return;
}
}
else
{
render_default(bottom);
return;
}
}
@ -536,292 +475,161 @@ void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom
}
else if (m_requires_canvas_update && m_texture.all_compressed_data_sent_to_gpu())
{
if (canvas != nullptr)
canvas->stop_keeping_dirty();
canvas.stop_keeping_dirty();
m_requires_canvas_update = false;
}
if (!bottom)
if (m_triangles.get_vertices_count() > 0)
{
filename = model_path + "_bed.stl";
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) {
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
if (key == "mk2")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 7.5, -0.03);
else if (key == "mk3")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 5.5, 2.43);
else if (key == "sl1")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 0.0, -0.03);
if (m_shader.get_shader_program_id() == 0)
m_shader.init("printbed.vs", "printbed.fs");
m_model.center_around(offset);
// update extended bounding box
calc_bounding_boxes();
}
if (!m_model.get_filename().empty())
if (m_shader.is_initialized())
{
glsafe(::glEnable(GL_LIGHTING));
m_model.render();
glsafe(::glDisable(GL_LIGHTING));
}
}
m_shader.start_using();
m_shader.set_uniform("transparent_background", bottom);
m_shader.set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg"));
unsigned int triangles_vcount = m_triangles.get_vertices_count();
if (triangles_vcount > 0)
{
if (m_vbo_id == 0)
{
glsafe(::glGenBuffers(1, &m_vbo_id));
if (m_vbo_id == 0)
{
glsafe(::glGenBuffers(1, &m_vbo_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
if (bottom)
glsafe(::glFrontFace(GL_CW));
unsigned int stride = m_triangles.get_vertex_data_size();
GLint position_id = m_shader.get_attrib_location("v_position");
GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords");
// 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));
glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW));
if (position_id != -1)
{
glsafe(::glEnableVertexAttribArray(position_id));
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset()));
}
if (tex_coords_id != -1)
{
glsafe(::glEnableVertexAttribArray(tex_coords_id));
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset()));
}
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));
if (tex_coords_id != -1)
glsafe(::glDisableVertexAttribArray(tex_coords_id));
if (position_id != -1)
glsafe(::glDisableVertexAttribArray(position_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
if (bottom)
glsafe(::glFrontFace(GL_CW));
render_prusa_shader(bottom);
if (bottom)
glsafe(::glFrontFace(GL_CCW));
glsafe(::glDisable(GL_BLEND));
glsafe(::glDepthMask(GL_TRUE));
}
}
void Bed3D::render_prusa_shader(bool transparent) const
{
if (m_shader.get_shader_program_id() == 0)
m_shader.init("printbed.vs", "printbed.fs");
if (m_shader.is_initialized())
{
m_shader.start_using();
m_shader.set_uniform("transparent_background", transparent);
unsigned int stride = m_triangles.get_vertex_data_size();
GLint position_id = m_shader.get_attrib_location("v_position");
GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords");
// 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)
{
glsafe(::glEnableVertexAttribArray(position_id));
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset()));
}
if (tex_coords_id != -1)
{
glsafe(::glEnableVertexAttribArray(tex_coords_id));
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset()));
}
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));
if (tex_coords_id != -1)
glsafe(::glDisableVertexAttribArray(tex_coords_id));
if (position_id != -1)
glsafe(::glDisableVertexAttribArray(position_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
m_shader.stop_using();
}
}
#else
void Bed3D::render_prusa(const std::string& key, float theta) const
{
std::string tex_path = resources_dir() + "/icons/bed/" + key;
// use higher resolution images if graphic card allows
GLint max_tex_size;
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size));
// temporary set to lowest resolution
max_tex_size = 2048;
if (max_tex_size >= 8192)
tex_path += "_8192";
else if (max_tex_size >= 4096)
tex_path += "_4096";
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));
std::string filename = tex_path + "_top.png";
if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename))
{
if (!m_top_texture.load_from_file(filename, true, true))
{
render_custom();
return;
}
if (max_anisotropy > 0.0f)
{
glsafe(::glBindTexture(GL_TEXTURE_2D, m_top_texture.get_id()));
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
if (bottom)
glsafe(::glFrontFace(GL_CCW));
glsafe(::glDisable(GL_BLEND));
glsafe(::glDepthMask(GL_TRUE));
m_shader.stop_using();
}
}
filename = tex_path + "_bottom.png";
if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename))
{
if (!m_bottom_texture.load_from_file(filename, true, true))
{
render_custom();
return;
}
if (max_anisotropy > 0.0f)
{
glsafe(::glBindTexture(GL_TEXTURE_2D, m_bottom_texture.get_id()));
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
}
}
if (theta <= 90.0f)
{
filename = model_path + "_bed.stl";
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) {
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
if (key == "mk2")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 7.5, -0.03);
else if (key == "mk3")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 5.5, 2.43);
else if (key == "sl1")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 0.0, -0.03);
m_model.center_around(offset);
}
if (!m_model.get_filename().empty())
{
glsafe(::glEnable(GL_LIGHTING));
m_model.render();
glsafe(::glDisable(GL_LIGHTING));
}
}
unsigned int triangles_vcount = m_triangles.get_vertices_count();
if (triangles_vcount > 0)
{
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glEnable(GL_TEXTURE_2D));
glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY));
if (theta > 90.0f)
glsafe(::glFrontFace(GL_CW));
glsafe(::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id()));
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()));
glsafe(::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords()));
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
if (theta > 90.0f)
glsafe(::glFrontFace(GL_CCW));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisable(GL_TEXTURE_2D));
glsafe(::glDisable(GL_BLEND));
glsafe(::glDepthMask(GL_TRUE));
}
}
#endif // ENABLE_TEXTURES_FROM_SVG
void Bed3D::render_custom() const
void Bed3D::render_model(const std::string& filename) const
{
#if ENABLE_TEXTURES_FROM_SVG
m_texture.reset();
#else
m_top_texture.reset();
m_bottom_texture.reset();
#endif // ENABLE_TEXTURES_FROM_SVG
if (filename.empty())
return;
unsigned int triangles_vcount = m_triangles.get_vertices_count();
if (triangles_vcount > 0)
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename))
{
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
Vec3d shift = m_bounding_box.center();
shift(2) = -0.03;
m_model.set_offset(shift);
// update extended bounding box
calc_bounding_boxes();
}
if (!m_model.get_filename().empty())
{
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f));
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
#if ENABLE_TEXTURES_FROM_SVG
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()));
#else
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()));
#endif // ENABLE_TEXTURES_FROM_SVG
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
// draw grid
unsigned int gridlines_vcount = m_gridlines.get_vertices_count();
// we need depth test for grid, otherwise it would disappear when looking the object from below
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glLineWidth(3.0f * m_scale_factor));
glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f));
#if ENABLE_TEXTURES_FROM_SVG
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data()));
#else
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices()));
#endif // ENABLE_TEXTURES_FROM_SVG
glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisable(GL_BLEND));
m_model.render();
glsafe(::glDisable(GL_LIGHTING));
}
}
#if ENABLE_TEXTURES_FROM_SVG
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const
{
if (m_custom_texture.empty() && m_custom_model.empty())
{
render_default(bottom);
return;
}
if (!bottom)
render_model(m_custom_model);
render_texture(m_custom_texture, bottom, canvas);
}
void Bed3D::render_default(bool bottom) const
{
m_texture.reset();
unsigned int triangles_vcount = m_triangles.get_vertices_count();
if (triangles_vcount > 0)
{
bool has_model = !m_model.get_filename().empty();
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
if (!has_model && !bottom)
{
// draw background
glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f));
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()));
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
}
// draw grid
glsafe(::glLineWidth(3.0f * m_scale_factor));
if (has_model && !bottom)
glsafe(::glColor4f(0.75f, 0.75f, 0.75f, 1.0f));
else
glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f));
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data()));
glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count()));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisable(GL_BLEND));
}
}
void Bed3D::reset()
{
if (m_vbo_id > 0)
@ -830,7 +638,6 @@ void Bed3D::reset()
m_vbo_id = 0;
}
}
#endif // ENABLE_TEXTURES_FROM_SVG
} // GUI
} // Slic3r

View File

@ -3,9 +3,7 @@
#include "GLTexture.hpp"
#include "3DScene.hpp"
#if ENABLE_TEXTURES_FROM_SVG
#include "GLShader.hpp"
#endif // ENABLE_TEXTURES_FROM_SVG
class GLUquadric;
typedef class GLUquadric GLUquadricObj;
@ -17,7 +15,6 @@ class GLCanvas3D;
class GeometryBuffer
{
#if ENABLE_TEXTURES_FROM_SVG
struct Vertex
{
float position[3];
@ -31,27 +28,17 @@ class GeometryBuffer
};
std::vector<Vertex> m_vertices;
#else
std::vector<float> m_vertices;
std::vector<float> m_tex_coords;
#endif // ENABLE_TEXTURES_FROM_SVG
public:
bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords);
bool set_from_lines(const Lines& lines, float z);
#if ENABLE_TEXTURES_FROM_SVG
const float* get_vertices_data() const;
unsigned int get_vertices_data_size() const { return (unsigned int)m_vertices.size() * get_vertex_data_size(); }
unsigned int get_vertex_data_size() const { return (unsigned int)(5 * sizeof(float)); }
size_t get_position_offset() const { return 0; }
size_t get_tex_coords_offset() const { return (size_t)(3 * sizeof(float)); }
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); }
#else
const float* get_vertices() const { return m_vertices.data(); }
const float* get_tex_coords() const { return m_tex_coords.data(); }
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size() / 3; }
#endif // ENABLE_TEXTURES_FROM_SVG
};
class Bed3D
@ -87,12 +74,13 @@ public:
private:
EType m_type;
Pointfs m_shape;
std::string m_custom_texture;
std::string m_custom_model;
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;
@ -100,10 +88,6 @@ private:
mutable bool m_requires_canvas_update;
mutable Shader m_shader;
mutable unsigned int m_vbo_id;
#else
mutable GLTexture m_top_texture;
mutable GLTexture m_bottom_texture;
#endif // ENABLE_TEXTURES_FROM_SVG
mutable GLBed m_model;
Axes m_axes;
@ -111,9 +95,7 @@ private:
public:
Bed3D();
#if ENABLE_TEXTURES_FROM_SVG
~Bed3D() { reset(); }
#endif // ENABLE_TEXTURES_FROM_SVG
EType get_type() const { return m_type; }
@ -122,34 +104,26 @@ public:
const Pointfs& get_shape() const { return m_shape; }
// Return true if the bed shape changed, so the calee will update the UI.
bool set_shape(const Pointfs& shape);
bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model);
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;
#if ENABLE_TEXTURES_FROM_SVG
void render(GLCanvas3D* canvas, float theta, float scale_factor) const;
#else
void render(float theta, float scale_factor) const;
#endif // ENABLE_TEXTURES_FROM_SVG
void render_axes() const;
void render(GLCanvas3D& canvas, float theta, float scale_factor) const;
private:
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(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) const;
#endif // ENABLE_TEXTURES_FROM_SVG
void render_custom() const;
#if ENABLE_TEXTURES_FROM_SVG
void render_axes() const;
void render_prusa(GLCanvas3D& canvas, const std::string& key, bool bottom) const;
void render_texture(const std::string& filename, bool bottom, GLCanvas3D& canvas) const;
void render_model(const std::string& filename) const;
void render_custom(GLCanvas3D& canvas, bool bottom) const;
void render_default(bool bottom) const;
void reset();
#endif // ENABLE_TEXTURES_FROM_SVG
};
} // GUI

View File

@ -1736,13 +1736,7 @@ bool GLBed::on_init_from_file(const std::string& filename)
m_filename = filename;
ModelObject* model_object = model.objects.front();
model_object->center_around_origin();
TriangleMesh mesh = model.mesh();
mesh.repair();
m_volume.indexed_vertex_array.load_mesh(mesh);
m_volume.indexed_vertex_array.load_mesh(model.mesh());
float color[4] = { 0.235f, 0.235f, 0.235f, 1.0f };
set_color(color, 4);

View File

@ -4,23 +4,25 @@
#include <wx/numformatter.h>
#include <wx/sizer.h>
#include <wx/statbox.h>
#include <wx/tooltip.h>
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Polygon.hpp"
#include "boost/nowide/iostream.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <algorithm>
namespace Slic3r {
namespace GUI {
void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt)
void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model)
{
SetFont(wxGetApp().normal_font());
m_panel = new BedShapePanel(this);
m_panel->build_panel(default_pt);
m_panel->build_panel(default_pt, custom_texture, custom_model);
auto main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(m_panel, 1, wxEXPAND);
@ -51,59 +53,69 @@ void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect)
Refresh();
}
void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
const std::string BedShapePanel::NONE = "None";
const std::string BedShapePanel::EMPTY_STRING = "";
void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model)
{
m_shape = default_pt.values;
m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value;
m_custom_model = custom_model.value.empty() ? NONE : custom_model.value;
auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape")));
sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font());
// shape options
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition,
wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
sbsizer->Add(m_shape_options_book);
auto optgroup = init_shape_options_page(_(L("Rectangular")));
ConfigOptionDef def;
def.type = coPoints;
def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) });
def.label = L("Size");
def.tooltip = L("Size in X and Y of the rectangular plate.");
Option option(def, "rect_size");
optgroup->append_single_option_line(option);
ConfigOptionDef def;
def.type = coPoints;
def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) });
def.label = L("Size");
def.tooltip = L("Size in X and Y of the rectangular plate.");
Option option(def, "rect_size");
optgroup->append_single_option_line(option);
def.type = coPoints;
def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) });
def.label = L("Origin");
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
option = Option(def, "rect_origin");
optgroup->append_single_option_line(option);
def.type = coPoints;
def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) });
def.label = L("Origin");
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
option = Option(def, "rect_origin");
optgroup->append_single_option_line(option);
optgroup = init_shape_options_page(_(L("Circular")));
def.type = coFloat;
def.set_default_value(new ConfigOptionFloat(200));
def.sidetext = L("mm");
def.label = L("Diameter");
def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
option = Option(def, "diameter");
optgroup->append_single_option_line(option);
optgroup = init_shape_options_page(_(L("Circular")));
def.type = coFloat;
def.set_default_value(new ConfigOptionFloat(200));
def.sidetext = L("mm");
def.label = L("Diameter");
def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
option = Option(def, "diameter");
optgroup->append_single_option_line(option);
optgroup = init_shape_options_page(_(L("Custom")));
Line line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
auto shape_btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL...")));
wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL);
shape_sizer->Add(shape_btn, 1, wxEXPAND);
optgroup = init_shape_options_page(_(L("Custom")));
Line line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
wxButton* shape_btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL...")));
wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL);
shape_sizer->Add(shape_btn, 1, wxEXPAND);
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(shape_sizer, 1, wxEXPAND);
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(shape_sizer, 1, wxEXPAND);
shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
load_stl();
}));
shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
load_stl();
}));
return sizer;
};
optgroup->append_line(line);
return sizer;
};
optgroup->append_line(line);
wxPanel* texture_panel = init_texture_panel();
wxPanel* model_panel = init_model_panel();
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent& e)
{
@ -112,13 +124,17 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
// right pane with preview canvas
m_canvas = new Bed_2D(this);
m_canvas->m_bed_shape = default_pt->values;
m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent& e) { m_canvas->repaint(m_shape); });
m_canvas->Bind(wxEVT_SIZE, [this](wxSizeEvent& e) { m_canvas->Refresh(); });
// main sizer
auto top_sizer = new wxBoxSizer(wxHORIZONTAL);
top_sizer->Add(sbsizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10);
if (m_canvas)
top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10) ;
wxSizer* left_sizer = new wxBoxSizer(wxVERTICAL);
left_sizer->Add(sbsizer, 0, wxEXPAND);
left_sizer->Add(texture_panel, 1, wxEXPAND);
left_sizer->Add(model_panel, 1, wxEXPAND);
wxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL);
top_sizer->Add(left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10);
top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10);
SetSizerAndFit(top_sizer);
@ -134,20 +150,155 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
// Create a panel for a rectangular / circular / custom bed shape.
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title)
{
auto panel = new wxPanel(m_shape_options_book);
ConfigOptionsGroupShp optgroup;
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings")));
wxPanel* panel = new wxPanel(m_shape_options_book);
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings")));
optgroup->label_width = 10;
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
update_shape();
};
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
update_shape();
};
m_optgroups.push_back(optgroup);
panel->SetSizerAndFit(optgroup->sizer);
m_shape_options_book->AddPage(panel, title);
m_optgroups.push_back(optgroup);
panel->SetSizerAndFit(optgroup->sizer);
m_shape_options_book->AddPage(panel, title);
return optgroup;
return optgroup;
}
wxPanel* BedShapePanel::init_texture_panel()
{
wxPanel* panel = new wxPanel(this);
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Texture")));
optgroup->label_width = 10;
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
update_shape();
};
Line line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load...")));
wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL);
load_sizer->Add(load_btn, 1, wxEXPAND);
wxStaticText* filename_lbl = new wxStaticText(parent, wxID_ANY, _(NONE));
wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL);
filename_sizer->Add(filename_lbl, 1, wxEXPAND);
wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove")));
wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL);
remove_sizer->Add(remove_btn, 1, wxEXPAND);
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(filename_sizer, 1, wxEXPAND);
sizer->Add(load_sizer, 1, wxEXPAND);
sizer->Add(remove_sizer, 1, wxEXPAND);
load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
load_texture();
}));
remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
m_custom_texture = NONE;
update_shape();
}));
filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
{
e.SetText(_(boost::filesystem::path(m_custom_texture).filename().string()));
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
if (lbl != nullptr)
{
wxString tooltip_text = (m_custom_texture == NONE) ? _(L("")) : _(m_custom_texture);
wxToolTip* tooltip = lbl->GetToolTip();
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
lbl->SetToolTip(tooltip_text);
}
}));
remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
{
e.Enable(m_custom_texture != NONE);
}));
return sizer;
};
optgroup->append_line(line);
panel->SetSizerAndFit(optgroup->sizer);
return panel;
}
wxPanel* BedShapePanel::init_model_panel()
{
wxPanel* panel = new wxPanel(this);
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Model")));
optgroup->label_width = 10;
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
update_shape();
};
Line line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load...")));
wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL);
load_sizer->Add(load_btn, 1, wxEXPAND);
wxStaticText* filename_lbl = new wxStaticText(parent, wxID_ANY, _(NONE));
wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL);
filename_sizer->Add(filename_lbl, 1, wxEXPAND);
wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove")));
wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL);
remove_sizer->Add(remove_btn, 1, wxEXPAND);
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(filename_sizer, 1, wxEXPAND);
sizer->Add(load_sizer, 1, wxEXPAND);
sizer->Add(remove_sizer, 1, wxEXPAND);
load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
load_model();
}));
remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
m_custom_model = NONE;
update_shape();
}));
filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
{
e.SetText(_(boost::filesystem::path(m_custom_model).filename().string()));
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
if (lbl != nullptr)
{
wxString tooltip_text = (m_custom_model == NONE) ? _(L("")) : _(m_custom_model);
wxToolTip* tooltip = lbl->GetToolTip();
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
lbl->SetToolTip(tooltip_text);
}
}));
remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
{
e.Enable(m_custom_model != NONE);
}));
return sizer;
};
optgroup->append_line(line);
panel->SetSizerAndFit(optgroup->sizer);
return panel;
}
// Called from the constructor.
@ -155,20 +306,20 @@ ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& tit
// Deduce the bed shape type(rect, circle, custom)
// This routine shall be smart enough if the user messes up
// with the list of points in the ini file directly.
void BedShapePanel::set_shape(ConfigOptionPoints* points)
void BedShapePanel::set_shape(const ConfigOptionPoints& points)
{
auto polygon = Polygon::new_scale(points->values);
auto polygon = Polygon::new_scale(points.values);
// is this a rectangle ?
if (points->size() == 4) {
auto lines = polygon.lines();
if (points.size() == 4) {
auto lines = polygon.lines();
if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) {
// okay, it's a rectangle
// find origin
coordf_t x_min, x_max, y_min, y_max;
x_max = x_min = points->values[0](0);
y_max = y_min = points->values[0](1);
for (auto pt : points->values)
x_max = x_min = points.values[0](0);
y_max = y_min = points.values[0](1);
for (auto pt : points.values)
{
x_min = std::min(x_min, pt(0));
x_max = std::max(x_max, pt(0));
@ -219,8 +370,8 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
}
}
if (points->size() < 3) {
// Invalid polygon.Revert to default bed dimensions.
if (points.size() < 3) {
// Invalid polygon.Revert to default bed dimensions.
m_shape_options_book->SetSelection(SHAPE_RECTANGULAR);
auto optgroup = m_optgroups[SHAPE_RECTANGULAR];
optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(200, 200) });
@ -232,7 +383,7 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
// This is a custom bed shape, use the polygon provided.
m_shape_options_book->SetSelection(SHAPE_CUSTOM);
// Copy the polygon to the canvas, make a copy of the array.
m_loaded_bed_shape = points->values;
m_loaded_shape = points.values;
update_shape();
}
@ -277,11 +428,11 @@ void BedShapePanel::update_shape()
x1 -= dx;
y0 -= dy;
y1 -= dy;
m_canvas->m_bed_shape = { Vec2d(x0, y0),
Vec2d(x1, y0),
Vec2d(x1, y1),
Vec2d(x0, y1)};
}
m_shape = { Vec2d(x0, y0),
Vec2d(x1, y0),
Vec2d(x1, y1),
Vec2d(x0, y1) };
}
else if(page_idx == SHAPE_CIRCULAR) {
double diameter;
try{
@ -293,16 +444,16 @@ void BedShapePanel::update_shape()
if (diameter == 0.0) return ;
auto r = diameter / 2;
auto twopi = 2 * PI;
auto edges = 60;
std::vector<Vec2d> points;
for (size_t i = 1; i <= 60; ++i) {
auto angle = i * twopi / edges;
auto edges = 72;
std::vector<Vec2d> points;
for (size_t i = 1; i <= edges; ++i) {
auto angle = i * twopi / edges;
points.push_back(Vec2d(r*cos(angle), r*sin(angle)));
}
m_canvas->m_bed_shape = points;
}
m_shape = points;
}
else if (page_idx == SHAPE_CUSTOM)
m_canvas->m_bed_shape = m_loaded_bed_shape;
m_shape = m_loaded_shape;
update_preview();
}
@ -310,27 +461,27 @@ void BedShapePanel::update_shape()
// Loads an stl file, projects it to the XY plane and calculates a polygon.
void BedShapePanel::load_stl()
{
auto dialog = new wxFileDialog(this, _(L("Choose a file to import bed shape from (STL/OBJ/AMF/3MF/PRUSA):")), "", "",
file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog->ShowModal() != wxID_OK) {
dialog->Destroy();
return;
}
wxArrayString input_file;
dialog->GetPaths(input_file);
dialog->Destroy();
wxFileDialog dialog(this, _(L("Choose an STL file to import bed shape from:")), "", "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK)
return;
std::string file_name = input_file[0].ToUTF8().data();
std::string file_name = dialog.GetPath().ToUTF8().data();
if (!boost::algorithm::iends_with(file_name, ".stl"))
{
show_error(this, _(L("Invalid file format.")));
return;
}
wxBusyCursor wait;
Model model;
try {
model = Model::read_from_file(file_name);
}
catch (std::exception &e) {
auto msg = _(L("Error!")) + " " + file_name + " : " + e.what() + ".";
show_error(this, msg);
exit(1);
model = Model::read_from_file(file_name);
}
catch (std::exception &) {
show_error(this, _(L("Error! Invalid model")));
return;
}
auto mesh = model.mesh();
auto expolygons = mesh.horizontal_projection();
@ -349,7 +500,53 @@ void BedShapePanel::load_stl()
for (auto pt : polygon.points)
points.push_back(unscale(pt));
m_loaded_bed_shape = points;
m_loaded_shape = points;
update_shape();
}
void BedShapePanel::load_texture()
{
wxFileDialog dialog(this, _(L("Choose a file to import bed texture from (PNG/SVG):")), "", "",
file_wildcards(FT_TEX), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK)
return;
m_custom_texture = NONE;
std::string file_name = dialog.GetPath().ToUTF8().data();
if (!boost::algorithm::iends_with(file_name, ".png") && !boost::algorithm::iends_with(file_name, ".svg"))
{
show_error(this, _(L("Invalid file format.")));
return;
}
wxBusyCursor wait;
m_custom_texture = file_name;
update_shape();
}
void BedShapePanel::load_model()
{
wxFileDialog dialog(this, _(L("Choose an STL file to import bed model from:")), "", "",
file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK)
return;
m_custom_model = NONE;
std::string file_name = dialog.GetPath().ToUTF8().data();
if (!boost::algorithm::iends_with(file_name, ".stl"))
{
show_error(this, _(L("Invalid file format.")));
return;
}
wxBusyCursor wait;
m_custom_model = file_name;
update_shape();
}

View File

@ -16,27 +16,40 @@ namespace GUI {
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
class BedShapePanel : public wxPanel
{
static const std::string NONE;
static const std::string EMPTY_STRING;
Bed_2D* m_canvas;
std::vector<Vec2d> m_loaded_bed_shape;
std::vector<Vec2d> m_shape;
std::vector<Vec2d> m_loaded_shape;
std::string m_custom_texture;
std::string m_custom_model;
public:
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {}
~BedShapePanel() {}
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY), m_custom_texture(NONE), m_custom_model(NONE) {}
void build_panel(ConfigOptionPoints* default_pt);
void build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model);
// Returns the resulting bed shape polygon. This value will be stored to the ini file.
const std::vector<Vec2d>& get_shape() const { return m_shape; }
const std::string& get_custom_texture() const { return (m_custom_texture != NONE) ? m_custom_texture : EMPTY_STRING; }
const std::string& get_custom_model() const { return (m_custom_model != NONE) ? m_custom_model : EMPTY_STRING; }
private:
ConfigOptionsGroupShp init_shape_options_page(const wxString& title);
void set_shape(ConfigOptionPoints* points);
void update_preview();
wxPanel* init_texture_panel();
wxPanel* init_model_panel();
void set_shape(const ConfigOptionPoints& points);
void update_preview();
void update_shape();
void load_stl();
// Returns the resulting bed shape polygon. This value will be stored to the ini file.
std::vector<Vec2d> GetValue() { return m_canvas->m_bed_shape; }
void load_texture();
void load_model();
wxChoicebook* m_shape_options_book;
std::vector <ConfigOptionsGroupShp> m_optgroups;
friend class BedShapeDialog;
};
class BedShapeDialog : public DPIDialog
@ -45,10 +58,12 @@ class BedShapeDialog : public DPIDialog
public:
BedShapeDialog(wxWindow* parent) : DPIDialog(parent, wxID_ANY, _(L("Bed Shape")),
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {}
~BedShapeDialog() {}
void build_dialog(ConfigOptionPoints* default_pt);
std::vector<Vec2d> GetValue() { return m_panel->GetValue(); }
void build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model);
const std::vector<Vec2d>& get_shape() const { return m_panel->get_shape(); }
const std::string& get_custom_texture() const { return m_panel->get_custom_texture(); }
const std::string& get_custom_model() const { return m_panel->get_custom_model(); }
protected:
void on_dpi_changed(const wxRect &suggested_rect) override;

View File

@ -532,15 +532,21 @@ PageBedShape::PageBedShape(ConfigWizard *parent)
{
append_text(_(L("Set the shape of your printer's bed.")));
shape_panel->build_panel(wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"));
shape_panel->build_panel(*wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"),
*wizard_p()->custom_config->option<ConfigOptionString>("bed_custom_texture"),
*wizard_p()->custom_config->option<ConfigOptionString>("bed_custom_model"));
append(shape_panel);
}
void PageBedShape::apply_custom_config(DynamicPrintConfig &config)
{
const auto points(shape_panel->GetValue());
auto *opt = new ConfigOptionPoints(points);
config.set_key_value("bed_shape", opt);
const std::vector<Vec2d>& points = shape_panel->get_shape();
const std::string& custom_texture = shape_panel->get_custom_texture();
const std::string& custom_model = shape_panel->get_custom_model();
config.set_key_value("bed_shape", new ConfigOptionPoints(points));
config.set_key_value("bed_custom_texture", new ConfigOptionString(custom_texture));
config.set_key_value("bed_custom_model", new ConfigOptionString(custom_model));
}
PageDiameters::PageDiameters(ConfigWizard *parent)
@ -1086,7 +1092,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason)
p->load_vendors();
p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({
"gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
"gcode_flavor", "bed_shape", "bed_custom_texture", "bed_custom_model", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
}));
p->index = new ConfigWizardIndex(this);

View File

@ -116,87 +116,6 @@ void Size::set_scale_factor(int scale_factor)
m_scale_factor = scale_factor;
}
#if !ENABLE_TEXTURES_FROM_SVG
GLCanvas3D::Shader::Shader()
: m_shader(nullptr)
{
}
GLCanvas3D::Shader::~Shader()
{
_reset();
}
bool GLCanvas3D::Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
{
if (is_initialized())
return true;
m_shader = new GLShader();
if (m_shader != nullptr)
{
if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str()))
{
std::cout << "Compilaton of shader failed:" << std::endl;
std::cout << m_shader->last_error << std::endl;
_reset();
return false;
}
}
return true;
}
bool GLCanvas3D::Shader::is_initialized() const
{
return (m_shader != nullptr);
}
bool GLCanvas3D::Shader::start_using() const
{
if (is_initialized())
{
m_shader->enable();
return true;
}
else
return false;
}
void GLCanvas3D::Shader::stop_using() const
{
if (m_shader != nullptr)
m_shader->disable();
}
void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const
{
if (m_shader != nullptr)
m_shader->set_uniform(name.c_str(), value);
}
void GLCanvas3D::Shader::set_uniform(const std::string& name, const float* matrix) const
{
if (m_shader != nullptr)
m_shader->set_uniform(name.c_str(), matrix);
}
const GLShader* GLCanvas3D::Shader::get_shader() const
{
return m_shader;
}
void GLCanvas3D::Shader::_reset()
{
if (m_shader != nullptr)
{
m_shader->release();
delete m_shader;
m_shader = nullptr;
}
}
#endif // !ENABLE_TEXTURES_FROM_SVG
GLCanvas3D::LayersEditing::LayersEditing()
: m_enabled(false)
, m_z_texture_id(0)
@ -383,7 +302,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, true))
if (!m_tooltip_texture.load_from_file(filename, false, GLTexture::SingleThreaded, false))
return;
}
@ -415,7 +334,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, true))
if (!m_reset_texture.load_from_file(filename, false, GLTexture::SingleThreaded, false))
return;
}
@ -1642,19 +1561,10 @@ void GLCanvas3D::render()
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
_render_background();
// textured bed needs to be rendered after objects if the texture is transparent
bool early_bed_render = m_bed.is_custom() || (theta <= 90.0f);
if (early_bed_render)
_render_bed(theta);
_render_objects();
_render_sla_slices();
_render_selection();
_render_axes();
if (!early_bed_render)
_render_bed(theta);
_render_bed(theta);
#if ENABLE_RENDER_SELECTION_CENTER
_render_selection_center();
@ -3986,16 +3896,7 @@ void GLCanvas3D::_render_bed(float theta) const
#if ENABLE_RETINA_GL
scale_factor = m_retina_helper->get_scale_factor();
#endif // ENABLE_RETINA_GL
#if ENABLE_TEXTURES_FROM_SVG
m_bed.render(const_cast<GLCanvas3D*>(this), theta, scale_factor);
#else
m_bed.render(theta, scale_factor);
#endif // ENABLE_TEXTURES_FROM_SVG
}
void GLCanvas3D::_render_axes() const
{
m_bed.render_axes();
m_bed.render(const_cast<GLCanvas3D&>(*this), theta, scale_factor);
}
void GLCanvas3D::_render_objects() const

View File

@ -158,32 +158,6 @@ class GLCanvas3D
void reset() { first_volumes.clear(); }
};
#if !ENABLE_TEXTURES_FROM_SVG
class Shader
{
GLShader* m_shader;
public:
Shader();
~Shader();
bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename);
bool is_initialized() const;
bool start_using() const;
void stop_using() const;
void set_uniform(const std::string& name, float value) const;
void set_uniform(const std::string& name, const float* matrix) const;
const GLShader* get_shader() const;
private:
void _reset();
};
#endif // !ENABLE_TEXTURES_FROM_SVG
class LayersEditing
{
public:
@ -688,7 +662,6 @@ private:
void _rectangular_selection_picking_pass() const;
void _render_background() const;
void _render_bed(float theta) const;
void _render_axes() const;
void _render_objects() const;
void _render_selection() const;
#if ENABLE_RENDER_SELECTION_CENTER

View File

@ -268,7 +268,6 @@ sub SetMatrix
}
*/
#if ENABLE_TEXTURES_FROM_SVG
Shader::Shader()
: m_shader(nullptr)
{
@ -363,6 +362,5 @@ void Shader::reset()
m_shader = nullptr;
}
}
#endif // ENABLE_TEXTURES_FROM_SVG
} // namespace Slic3r

View File

@ -37,7 +37,6 @@ public:
std::string last_error;
};
#if ENABLE_TEXTURES_FROM_SVG
class Shader
{
GLShader* m_shader;
@ -66,7 +65,6 @@ public:
private:
void reset();
};
#endif // ENABLE_TEXTURES_FROM_SVG
}

View File

@ -134,7 +134,7 @@ GLTexture::~GLTexture()
reset();
}
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, bool compress)
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
{
reset();
@ -142,7 +142,7 @@ bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, bo
return false;
if (boost::algorithm::iends_with(filename, ".png"))
return load_from_png(filename, use_mipmaps, compress);
return load_from_png(filename, use_mipmaps, compression_type, apply_anisotropy);
else
return false;
}
@ -354,49 +354,10 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
glsafe(::glDisable(GL_BLEND));
}
unsigned int GLTexture::generate_mipmaps(wxImage& image, bool compress)
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
{
int w = image.GetWidth();
int h = image.GetHeight();
GLint level = 0;
std::vector<unsigned char> data(w * h * 4, 0);
bool compression_enabled = (compression_type != None) && GLEW_EXT_texture_compression_s3tc;
while ((w > 1) || (h > 1))
{
++level;
w = std::max(w / 2, 1);
h = std::max(h / 2, 1);
int n_pixels = w * h;
image = image.ResampleBicubic(w, h);
unsigned char* img_rgb = image.GetData();
unsigned char* img_alpha = image.GetAlpha();
data.resize(n_pixels * 4);
for (int i = 0; i < n_pixels; ++i)
{
int data_id = i * 4;
int img_id = i * 3;
data[data_id + 0] = img_rgb[img_id + 0];
data[data_id + 1] = img_rgb[img_id + 1];
data[data_id + 2] = img_rgb[img_id + 2];
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
}
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 compress)
{
// Load a PNG with an alpha channel.
wxImage image;
if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG))
@ -407,8 +368,32 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, boo
m_width = image.GetWidth();
m_height = image.GetHeight();
int n_pixels = m_width * m_height;
bool requires_rescale = false;
if (compression_enabled && (compression_type == MultiThreaded))
{
// 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);
requires_rescale = true;
}
if (height_rem != 0)
{
m_height += (4 - height_rem);
requires_rescale = true;
}
}
if (requires_rescale)
image = image.ResampleBicubic(m_width, m_height);
int n_pixels = m_width * m_height;
if (n_pixels <= 0)
{
reset();
@ -440,28 +425,99 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, boo
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
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()));
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)
{
if (compression_type == SingleThreaded)
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
{
// 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
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));
int lod_w = m_width;
int lod_h = m_height;
GLint level = 0;
// we do not need to generate all levels down to 1x1
while ((lod_w > 16) || (lod_h > 16))
{
++level;
lod_w = std::max(lod_w / 2, 1);
lod_h = std::max(lod_h / 2, 1);
n_pixels = lod_w * lod_h;
image = image.ResampleBicubic(lod_w, lod_h);
data.resize(n_pixels * 4);
img_rgb = image.GetData();
img_alpha = image.GetAlpha();
for (int i = 0; i < n_pixels; ++i)
{
int data_id = i * 4;
int img_id = i * 3;
data[data_id + 0] = img_rgb[img_id + 0];
data[data_id + 1] = img_rgb[img_id + 1];
data[data_id + 2] = img_rgb[img_id + 2];
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
}
if (compression_enabled)
{
if (compression_type == SingleThreaded)
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
{
// 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()));
}
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
{
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));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
m_source = filename;
if (compression_enabled && (compression_type == MultiThreaded))
// start asynchronous compression
m_compressor.start_compressing();
return true;
}
@ -472,7 +528,6 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
if (image == nullptr)
{
// printf("Could not open SVG image.\n");
reset();
return false;
}
@ -507,7 +562,6 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
NSVGrasterizer* rast = nsvgCreateRasterizer();
if (rast == nullptr)
{
// printf("Could not init rasterizer.\n");
nsvgDelete(image);
reset();
return false;
@ -579,6 +633,7 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, boo
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));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));

View File

@ -48,6 +48,13 @@ namespace GUI {
};
public:
enum ECompressionType : unsigned char
{
None,
SingleThreaded,
MultiThreaded
};
struct UV
{
float u;
@ -75,7 +82,7 @@ namespace GUI {
GLTexture();
virtual ~GLTexture();
bool load_from_file(const std::string& filename, bool use_mipmaps, bool compress);
bool load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
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):
@ -101,11 +108,8 @@ namespace GUI {
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, bool compress);
private:
bool load_from_png(const std::string& filename, bool use_mipmaps, bool compress);
bool load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
bool load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
friend class Compressor;

View File

@ -174,7 +174,7 @@ bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture)
bool res = false;
if (!background_texture.filename.empty())
res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, true);
res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, GLTexture::SingleThreaded, false);
if (res)
m_background_texture.metadata = background_texture;

View File

@ -67,9 +67,12 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
/* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA",
/* FT_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF",
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
/* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG",
/* FT_PNGZIP */"Masked SLA files (*.sl1)|*.sl1;*.SL1",
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
/* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG",
/* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG",
/* FT_PNGZIP */ "Masked SLA files (*.sl1)|*.sl1;*.SL1",
};
std::string out = defaults[file_type];

View File

@ -44,6 +44,9 @@ enum FileType
FT_INI,
FT_SVG,
FT_TEX,
FT_PNGZIP,
FT_SIZE,

View File

@ -43,7 +43,7 @@ bool GLGizmosManager::init()
if (!m_background_texture.metadata.filename.empty())
{
if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false, true))
if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false, GLTexture::SingleThreaded, false))
{
reset();
return false;

View File

@ -1853,7 +1853,7 @@ struct Plater::priv
// triangulate the bed and store the triangles into m_bed.m_triangles,
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
// Sets m_bed.m_polygon to limit the object placement.
void set_bed_shape(const Pointfs& shape);
void set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model);
bool can_delete() const;
bool can_delete_all() const;
@ -1914,7 +1914,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
: q(q)
, main_frame(main_frame)
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
"bed_shape", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
"brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host",
"printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material",
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle",
@ -2017,11 +2017,21 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this);
view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this);
view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); });
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values); });
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&)
{
set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values,
config->option<ConfigOptionString>("bed_custom_texture")->value,
config->option<ConfigOptionString>("bed_custom_model")->value);
});
// Preview events:
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&)
{
set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values,
config->option<ConfigOptionString>("bed_custom_texture")->value,
config->option<ConfigOptionString>("bed_custom_model")->value);
});
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); });
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_EDIT_COLOR_CHANGE, [this](wxKeyEvent& evt) { preview->edit_double_slider(evt); });
@ -3625,9 +3635,9 @@ bool Plater::priv::can_mirror() const
return get_selection().is_from_single_instance();
}
void Plater::priv::set_bed_shape(const Pointfs& shape)
void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
{
bool new_shape = bed.set_shape(shape);
bool new_shape = bed.set_shape(shape, custom_texture, custom_model);
if (new_shape)
{
if (view3D) view3D->bed_shape_changed();
@ -4559,7 +4569,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
p->config->set_key_value(opt_key, config.option(opt_key)->clone());
if (opt_key == "printer_technology")
this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key));
else if (opt_key == "bed_shape") {
else if ((opt_key == "bed_shape") || (opt_key == "bed_custom_texture") || (opt_key == "bed_custom_model")) {
bed_shape_changed = true;
update_scheduled = true;
}
@ -4593,7 +4603,9 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
}
if (bed_shape_changed)
p->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values);
p->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values,
p->config->option<ConfigOptionString>("bed_custom_texture")->value,
p->config->option<ConfigOptionString>("bed_custom_model")->value);
if (update_scheduled)
update();

View File

@ -411,7 +411,7 @@ const std::vector<std::string>& Preset::printer_options()
if (s_opts.empty()) {
s_opts = {
"printer_technology",
"bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
"bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
"use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
"host_type", "print_host", "printhost_apikey", "printhost_cafile",
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
@ -512,6 +512,7 @@ const std::vector<std::string>& Preset::sla_printer_options()
if (s_opts.empty()) {
s_opts = {
"printer_technology",
"bed_shape", "bed_custom_texture", "bed_custom_model", "max_print_height",
"bed_shape", "max_print_height",
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
"display_mirror_x", "display_mirror_y",

View File

@ -14,20 +14,20 @@
namespace Slic3r {
ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id):
self(new wxStatusBar(parent ? parent : GUI::wxGetApp().mainframe,
id == -1? wxID_ANY : id)),
m_timer(new wxTimer(self)),
m_prog (new wxGauge(self,
wxGA_HORIZONTAL,
100,
wxDefaultPosition,
wxDefaultSize)),
m_cancelbutton(new wxButton(self,
-1,
_(L("Cancel")),
wxDefaultPosition,
wxDefaultSize))
ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id)
: self{new wxStatusBar(parent ? parent : GUI::wxGetApp().mainframe,
id == -1 ? wxID_ANY : id)}
, m_prog{new wxGauge(self,
wxGA_HORIZONTAL,
100,
wxDefaultPosition,
wxDefaultSize)}
, m_cancelbutton{new wxButton(self,
-1,
_(L("Cancel")),
wxDefaultPosition,
wxDefaultSize)}
, m_timer{new wxTimer(self)}
{
m_prog->Hide();
m_cancelbutton->Hide();

View File

@ -25,9 +25,9 @@ namespace Slic3r {
class ProgressStatusBar
{
wxStatusBar *self; // we cheat! It should be the base class but: perl!
wxTimer *m_timer;
wxGauge *m_prog;
wxButton *m_cancelbutton;
std::unique_ptr<wxTimer> m_timer;
public:
/// Cancel callback function type

View File

@ -1862,12 +1862,18 @@ void TabPrinter::build_fff()
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
{
BedShapeDialog dlg(this);
dlg.build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
dlg.build_dialog(*m_config->option<ConfigOptionPoints>("bed_shape"),
*m_config->option<ConfigOptionString>("bed_custom_texture"),
*m_config->option<ConfigOptionString>("bed_custom_model"));
if (dlg.ShowModal() == wxID_OK) {
std::vector<Vec2d> shape = dlg.GetValue();
const std::vector<Vec2d>& shape = dlg.get_shape();
const std::string& custom_texture = dlg.get_custom_texture();
const std::string& custom_model = dlg.get_custom_model();
if (!shape.empty())
{
load_key_value("bed_shape", shape);
load_key_value("bed_custom_texture", custom_texture);
load_key_value("bed_custom_model", custom_model);
update_changed_ui();
}
}
@ -2095,12 +2101,18 @@ void TabPrinter::build_sla()
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
{
BedShapeDialog dlg(this);
dlg.build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
dlg.build_dialog(*m_config->option<ConfigOptionPoints>("bed_shape"),
*m_config->option<ConfigOptionString>("bed_custom_texture"),
*m_config->option<ConfigOptionString>("bed_custom_model"));
if (dlg.ShowModal() == wxID_OK) {
std::vector<Vec2d> shape = dlg.GetValue();
const std::vector<Vec2d>& shape = dlg.get_shape();
const std::string& custom_texture = dlg.get_custom_texture();
const std::string& custom_model = dlg.get_custom_model();
if (!shape.empty())
{
load_key_value("bed_shape", shape);
load_key_value("bed_custom_texture", custom_texture);
load_key_value("bed_custom_model", custom_model);
update_changed_ui();
}
}