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

github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnrico Turri <enricoturri@seznam.cz>2019-12-03 14:37:27 +0300
committerEnrico Turri <enricoturri@seznam.cz>2019-12-03 14:37:27 +0300
commitc1a316124fd736ac0a004a6ec4a947eb4238632f (patch)
tree1bafb42a8ffc8e8192bec5577d6c69f19ff0edd9
parent02e107fa6ac86207b4bdd1b404afc27a6d275358 (diff)
Tech ENABLE_THUMBNAIL_GENERATOR partially (only export to .gcode) ported from masterversion_2.1.1-beta0
-rw-r--r--resources/profiles/PrusaResearch.ini1
-rw-r--r--src/libslic3r/CMakeLists.txt2
-rw-r--r--src/libslic3r/GCode.cpp60
-rw-r--r--src/libslic3r/GCode.hpp11
-rw-r--r--src/libslic3r/GCode/ThumbnailData.cpp36
-rw-r--r--src/libslic3r/GCode/ThumbnailData.hpp31
-rw-r--r--src/libslic3r/Point.hpp12
-rw-r--r--src/libslic3r/Print.cpp8
-rw-r--r--src/libslic3r/Print.hpp7
-rw-r--r--src/libslic3r/PrintConfig.cpp5
-rw-r--r--src/libslic3r/Technologies.hpp9
-rw-r--r--src/slic3r/GUI/3DBed.cpp10
-rw-r--r--src/slic3r/GUI/3DBed.hpp2
-rw-r--r--src/slic3r/GUI/BackgroundSlicingProcess.cpp12
-rw-r--r--src/slic3r/GUI/BackgroundSlicingProcess.hpp11
-rw-r--r--src/slic3r/GUI/Camera.cpp135
-rw-r--r--src/slic3r/GUI/Camera.hpp23
-rw-r--r--src/slic3r/GUI/GLCanvas3D.cpp355
-rw-r--r--src/slic3r/GUI/GLCanvas3D.hpp27
-rw-r--r--src/slic3r/GUI/GLCanvas3DManager.cpp8
-rw-r--r--src/slic3r/GUI/GLCanvas3DManager.hpp10
-rw-r--r--src/slic3r/GUI/Plater.cpp39
-rw-r--r--src/slic3r/GUI/Preset.cpp3
-rw-r--r--src/slic3r/GUI/PresetBundle.cpp1
24 files changed, 788 insertions, 30 deletions
diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini
index 55e127921..36f097552 100644
--- a/resources/profiles/PrusaResearch.ini
+++ b/resources/profiles/PrusaResearch.ini
@@ -4031,7 +4031,6 @@ printer_model = SL1
printer_variant = default
default_sla_material_profile = Prusa Orange Tough 0.05
default_sla_print_profile = 0.05 Normal
-thumbnails = 400x400,800x480
bed_shape = 1.48x1.02,119.48x1.02,119.48x67.02,1.48x67.02
display_height = 68.04
display_orientation = portrait
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 97e0fc09b..154bb479a 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -71,6 +71,8 @@ add_library(libslic3r STATIC
Format/STL.hpp
GCode/Analyzer.cpp
GCode/Analyzer.hpp
+ GCode/ThumbnailData.cpp
+ GCode/ThumbnailData.hpp
GCode/CoolingBuffer.cpp
GCode/CoolingBuffer.hpp
GCode/PostProcessor.cpp
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index ff62a5a03..19d4dc9e0 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -17,6 +17,9 @@
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
+#if ENABLE_THUMBNAIL_GENERATOR
+#include <boost/beast/core/detail/base64.hpp>
+#endif // ENABLE_THUMBNAIL_GENERATOR
#include <boost/nowide/iostream.hpp>
#include <boost/nowide/cstdio.hpp>
@@ -28,6 +31,8 @@
#include <Shiny/Shiny.h>
+#include "miniz_extension.hpp"
+
#if 0
// Enable debugging and asserts, even in the release build.
#define DEBUG
@@ -651,7 +656,11 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
return layers_to_print;
}
+#if ENABLE_THUMBNAIL_GENERATOR
+void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb)
+#else
void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_data)
+#endif // ENABLE_THUMBNAIL_GENERATOR
{
PROFILE_CLEAR();
@@ -677,7 +686,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
try {
m_placeholder_parser_failed_templates.clear();
+#if ENABLE_THUMBNAIL_GENERATOR
+ this->_do_export(*print, file, thumbnail_cb);
+#else
this->_do_export(*print, file);
+#endif // ENABLE_THUMBNAIL_GENERATOR
fflush(file);
if (ferror(file)) {
fclose(file);
@@ -737,7 +750,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str());
}
+#if ENABLE_THUMBNAIL_GENERATOR
+void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb)
+#else
void GCode::_do_export(Print &print, FILE *file)
+#endif // ENABLE_THUMBNAIL_GENERATOR
{
PROFILE_FUNC();
@@ -925,6 +942,49 @@ void GCode::_do_export(Print &print, FILE *file)
// Write information on the generator.
_write_format(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str());
+
+#if ENABLE_THUMBNAIL_GENERATOR
+ // Write thumbnails using base64 encoding
+ if (thumbnail_cb != nullptr)
+ {
+ const size_t max_row_length = 78;
+ ThumbnailsList thumbnails;
+ thumbnail_cb(thumbnails, print.full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, true, true);
+ for (const ThumbnailData& data : thumbnails)
+ {
+ if (data.is_valid())
+ {
+ size_t png_size = 0;
+ void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)data.pixels.data(), data.width, data.height, 4, &png_size, MZ_DEFAULT_LEVEL, 1);
+ if (png_data != nullptr)
+ {
+ std::string encoded;
+ encoded.resize(boost::beast::detail::base64::encoded_size(png_size));
+ encoded.resize(boost::beast::detail::base64::encode((void*)&encoded[0], (const void*)png_data, png_size));
+
+ _write_format(file, "\n;\n; thumbnail begin %dx%d %d\n", data.width, data.height, encoded.size());
+
+ unsigned int row_count = 0;
+ while (encoded.size() > max_row_length)
+ {
+ _write_format(file, "; %s\n", encoded.substr(0, max_row_length).c_str());
+ encoded = encoded.substr(max_row_length);
+ ++row_count;
+ }
+
+ if (encoded.size() > 0)
+ _write_format(file, "; %s\n", encoded.c_str());
+
+ _write(file, "; thumbnail end\n;\n");
+
+ mz_free(png_data);
+ }
+ }
+ print.throw_if_canceled();
+ }
+ }
+#endif // ENABLE_THUMBNAIL_GENERATOR
+
// Write notes (content of the Print Settings tab -> Notes)
{
std::list<std::string> lines;
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 83d61c483..c6257b81e 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -17,6 +17,9 @@
#include "GCodeTimeEstimator.hpp"
#include "EdgeGrid.hpp"
#include "GCode/Analyzer.hpp"
+#if ENABLE_THUMBNAIL_GENERATOR
+#include "GCode/ThumbnailData.hpp"
+#endif // ENABLE_THUMBNAIL_GENERATOR
#include <memory>
#include <string>
@@ -162,7 +165,11 @@ public:
// throws std::runtime_exception on error,
// throws CanceledException through print->throw_if_canceled().
+#if ENABLE_THUMBNAIL_GENERATOR
+ void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
+#else
void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr);
+#endif // ENABLE_THUMBNAIL_GENERATOR
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
const Vec2d& origin() const { return m_origin; }
@@ -190,7 +197,11 @@ public:
static void append_full_config(const Print& print, std::string& str);
protected:
+#if ENABLE_THUMBNAIL_GENERATOR
+ void _do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb);
+#else
void _do_export(Print &print, FILE *file);
+#endif //ENABLE_THUMBNAIL_GENERATOR
// Object and support extrusions of the same PrintObject at the same print_z.
struct LayerToPrint
diff --git a/src/libslic3r/GCode/ThumbnailData.cpp b/src/libslic3r/GCode/ThumbnailData.cpp
new file mode 100644
index 000000000..80165916b
--- /dev/null
+++ b/src/libslic3r/GCode/ThumbnailData.cpp
@@ -0,0 +1,36 @@
+#include "libslic3r/libslic3r.h"
+#include "ThumbnailData.hpp"
+
+#if ENABLE_THUMBNAIL_GENERATOR
+
+namespace Slic3r {
+
+void ThumbnailData::set(unsigned int w, unsigned int h)
+{
+ if ((w == 0) || (h == 0))
+ return;
+
+ if ((width != w) || (height != h))
+ {
+ width = w;
+ height = h;
+ // defaults to white texture
+ pixels = std::vector<unsigned char>(width * height * 4, 255);
+ }
+}
+
+void ThumbnailData::reset()
+{
+ width = 0;
+ height = 0;
+ pixels.clear();
+}
+
+bool ThumbnailData::is_valid() const
+{
+ return (width != 0) && (height != 0) && ((unsigned int)pixels.size() == 4 * width * height);
+}
+
+} // namespace Slic3r
+
+#endif // ENABLE_THUMBNAIL_GENERATOR \ No newline at end of file
diff --git a/src/libslic3r/GCode/ThumbnailData.hpp b/src/libslic3r/GCode/ThumbnailData.hpp
new file mode 100644
index 000000000..4acfe4374
--- /dev/null
+++ b/src/libslic3r/GCode/ThumbnailData.hpp
@@ -0,0 +1,31 @@
+#ifndef slic3r_ThumbnailData_hpp_
+#define slic3r_ThumbnailData_hpp_
+
+#if ENABLE_THUMBNAIL_GENERATOR
+
+#include <vector>
+#include "libslic3r/Point.hpp"
+
+namespace Slic3r {
+
+struct ThumbnailData
+{
+ unsigned int width;
+ unsigned int height;
+ std::vector<unsigned char> pixels;
+
+ ThumbnailData() { reset(); }
+ void set(unsigned int w, unsigned int h);
+ void reset();
+
+ bool is_valid() const;
+};
+
+typedef std::vector<ThumbnailData> ThumbnailsList;
+typedef std::function<void(ThumbnailsList & thumbnails, const Vec2ds & sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)> ThumbnailsGeneratorCallback;
+
+} // namespace Slic3r
+
+#endif // ENABLE_THUMBNAIL_GENERATOR
+
+#endif // slic3r_ThumbnailData_hpp_ \ No newline at end of file
diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp
index 994f45e59..9631ba975 100644
--- a/src/libslic3r/Point.hpp
+++ b/src/libslic3r/Point.hpp
@@ -38,6 +38,7 @@ typedef std::vector<Point*> PointPtrs;
typedef std::vector<const Point*> PointConstPtrs;
typedef std::vector<Vec3crd> Points3;
typedef std::vector<Vec2d> Pointfs;
+typedef std::vector<Vec2d> Vec2ds;
typedef std::vector<Vec3d> Pointf3s;
typedef Eigen::Matrix<float, 2, 2, Eigen::DontAlign> Matrix2f;
@@ -87,11 +88,12 @@ class Point : public Vec2crd
public:
typedef coord_t coord_type;
- Point() : Vec2crd() { (*this)(0) = 0; (*this)(1) = 0; }
- Point(coord_t x, coord_t y) { (*this)(0) = x; (*this)(1) = y; }
- Point(int64_t x, int64_t y) { (*this)(0) = coord_t(x); (*this)(1) = coord_t(y); } // for Clipper
- Point(double x, double y) { (*this)(0) = coord_t(lrint(x)); (*this)(1) = coord_t(lrint(y)); }
- Point(const Point &rhs) { *this = rhs; }
+ Point() : Vec2crd(0, 0) {}
+ Point(coord_t x, coord_t y) : Vec2crd(x, y) {}
+ Point(int64_t x, int64_t y) : Vec2crd(coord_t(x), coord_t(y)) {} // for Clipper
+ Point(double x, double y) : Vec2crd(coord_t(lrint(x)), coord_t(lrint(y))) {}
+ Point(const Point& rhs) { *this = rhs; }
+ explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(lrint(rhs.x())), coord_t(lrint(rhs.y()))) {}
// This constructor allows you to construct Point from Eigen expressions
template<typename OtherDerived>
Point(const Eigen::MatrixBase<OtherDerived> &other) : Vec2crd(other) {}
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 796215781..630020c1b 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -1525,7 +1525,11 @@ void Print::process()
// The export_gcode may die for various reasons (fails to process output_filename_format,
// write error into the G-code, cannot execute post-processing scripts).
// It is up to the caller to show an error message.
+#if ENABLE_THUMBNAIL_GENERATOR
+std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb)
+#else
std::string Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
+#endif // ENABLE_THUMBNAIL_GENERATOR
{
// output everything to a G-code file
// The following call may die if the output_filename_format template substitution fails.
@@ -1542,7 +1546,11 @@ std::string Print::export_gcode(const std::string &path_template, GCodePreviewDa
// The following line may die for multiple reasons.
GCode gcode;
+#if ENABLE_THUMBNAIL_GENERATOR
+ gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb);
+#else
gcode.do_export(this, path.c_str(), preview_data);
+#endif // ENABLE_THUMBNAIL_GENERATOR
return path.c_str();
}
diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp
index b6d7b678d..139048c5c 100644
--- a/src/libslic3r/Print.hpp
+++ b/src/libslic3r/Print.hpp
@@ -11,6 +11,9 @@
#include "Slicing.hpp"
#include "GCode/ToolOrdering.hpp"
#include "GCode/WipeTower.hpp"
+#if ENABLE_THUMBNAIL_GENERATOR
+#include "GCode/ThumbnailData.hpp"
+#endif // ENABLE_THUMBNAIL_GENERATOR
namespace Slic3r {
@@ -301,7 +304,11 @@ public:
void process() override;
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
+#if ENABLE_THUMBNAIL_GENERATOR
+ std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
+#else
std::string export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
+#endif // ENABLE_THUMBNAIL_GENERATOR
// methods for handling state
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 8b73a2e82..f524a6d49 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -62,6 +62,11 @@ void PrintConfigDef::init_common_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
+ def = this->add("thumbnails", coPoints);
+ def->label = L("Picture sizes to be stored into a .gcode and .sl1 files");
+ def->mode = comExpert;
+ def->set_default_value(new ConfigOptionPoints());
+
def = this->add("layer_height", coFloat);
def->label = L("Layer height");
def->category = L("Layers and Perimeters");
diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp
index 51d092094..0c555c616 100644
--- a/src/libslic3r/Technologies.hpp
+++ b/src/libslic3r/Technologies.hpp
@@ -32,4 +32,13 @@
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1)
+//============
+// 2.1.1 techs
+//============
+#define ENABLE_2_1_1 1
+
+// Enable thumbnail generator
+#define ENABLE_THUMBNAIL_GENERATOR (1 && ENABLE_2_1_1)
+
+
#endif // _technologies_h_
diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp
index b2b6c167e..8533ba7b4 100644
--- a/src/slic3r/GUI/3DBed.cpp
+++ b/src/slic3r/GUI/3DBed.cpp
@@ -171,6 +171,7 @@ void Bed3D::Axes::render() const
glsafe(::glPopMatrix());
glsafe(::glDisable(GL_LIGHTING));
+ glsafe(::glDisable(GL_DEPTH_TEST));
}
void Bed3D::Axes::render_axis(double length) const
@@ -262,11 +263,14 @@ Point Bed3D::point_projection(const Point& point) const
return m_polygon.point_projection(point);
}
-void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor) const
+void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor, bool show_axes) const
{
m_scale_factor = scale_factor;
- render_axes();
+ if (show_axes)
+ render_axes();
+
+ glsafe(::glEnable(GL_DEPTH_TEST));
switch (m_type)
{
@@ -277,6 +281,8 @@ void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor) const
default:
case Custom: { render_custom(canvas, theta > 90.0f); break; }
}
+
+ glsafe(::glDisable(GL_DEPTH_TEST));
}
void Bed3D::calc_bounding_boxes() const
diff --git a/src/slic3r/GUI/3DBed.hpp b/src/slic3r/GUI/3DBed.hpp
index c485b93e3..044654a97 100644
--- a/src/slic3r/GUI/3DBed.hpp
+++ b/src/slic3r/GUI/3DBed.hpp
@@ -111,7 +111,7 @@ public:
bool contains(const Point& point) const;
Point point_projection(const Point& point) const;
- void render(GLCanvas3D& canvas, float theta, float scale_factor) const;
+ void render(GLCanvas3D& canvas, float theta, float scale_factor, bool show_axes) const;
private:
void calc_bounding_boxes() const;
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index a1db6884e..49f0a8c01 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -10,6 +10,10 @@
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
+#if ENABLE_THUMBNAIL_GENERATOR
+#include <miniz.h>
+#endif // ENABLE_THUMBNAIL_GENERATOR
+
// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx.
#include "libslic3r/Print.hpp"
#include "libslic3r/SLAPrint.hpp"
@@ -82,8 +86,12 @@ void BackgroundSlicingProcess::process_fff()
assert(m_print == m_fff_print);
m_print->process();
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id));
- m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
- if (this->set_step_started(bspsGCodeFinalize)) {
+#if ENABLE_THUMBNAIL_GENERATOR
+ m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb);
+#else
+ m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
+#endif // ENABLE_THUMBNAIL_GENERATOR
+ if (this->set_step_started(bspsGCodeFinalize)) {
if (! m_export_path.empty()) {
//FIXME localize the messages
// Perform the final post-processing of the export path by applying the print statistics over the file name.
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
index cf5edd55f..bd6d0ac44 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
@@ -49,7 +49,10 @@ public:
void set_fff_print(Print *print) { m_fff_print = print; }
void set_sla_print(SLAPrint *print) { m_sla_print = print; }
void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; }
- // The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished
+#if ENABLE_THUMBNAIL_GENERATOR
+ void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
+#endif // ENABLE_THUMBNAIL_GENERATOR
+ // The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished
// and the background processing will transition into G-code export.
// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
void set_slicing_completed_event(int event_id) { m_event_slicing_completed_id = event_id; }
@@ -151,7 +154,11 @@ private:
SLAPrint *m_sla_print = nullptr;
// Data structure, to which the G-code export writes its annotations.
GCodePreviewData *m_gcode_preview_data = nullptr;
- // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
+#if ENABLE_THUMBNAIL_GENERATOR
+ // Callback function, used to write thumbnails into gcode.
+ ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
+#endif // ENABLE_THUMBNAIL_GENERATOR
+ // Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
std::string m_temp_output_path;
// Output path provided by the user. The output path may be set even if the slicing is running,
// but once set, it cannot be re-set.
diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp
index 8e3a6d1f1..053a1254d 100644
--- a/src/slic3r/GUI/Camera.cpp
+++ b/src/slic3r/GUI/Camera.cpp
@@ -1,7 +1,9 @@
#include "libslic3r/libslic3r.h"
#include "Camera.hpp"
+#if !ENABLE_THUMBNAIL_GENERATOR
#include "3DScene.hpp"
+#endif // !ENABLE_THUMBNAIL_GENERATOR
#include "GUI_App.hpp"
#include "AppConfig.hpp"
@@ -22,6 +24,10 @@ namespace Slic3r {
namespace GUI {
const double Camera::DefaultDistance = 1000.0;
+#if ENABLE_THUMBNAIL_GENERATOR
+const double Camera::DefaultZoomToBoxMarginFactor = 1.025;
+const double Camera::DefaultZoomToVolumesMarginFactor = 1.025;
+#endif // ENABLE_THUMBNAIL_GENERATOR
double Camera::FrustrumMinZRange = 50.0;
double Camera::FrustrumMinNearZ = 100.0;
double Camera::FrustrumZMargin = 10.0;
@@ -184,7 +190,7 @@ void Camera::apply_view_matrix() const
glsafe(::glGetDoublev(GL_MODELVIEW_MATRIX, m_view_matrix.data()));
}
-void Camera::apply_projection(const BoundingBoxf3& box) const
+void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double far_z) const
{
set_distance(DefaultDistance);
@@ -195,6 +201,12 @@ void Camera::apply_projection(const BoundingBoxf3& box) const
{
m_frustrum_zs = calc_tight_frustrum_zs_around(box);
+ if (near_z > 0.0)
+ m_frustrum_zs.first = std::min(m_frustrum_zs.first, near_z);
+
+ if (far_z > 0.0)
+ m_frustrum_zs.second = std::max(m_frustrum_zs.second, far_z);
+
w = 0.5 * (double)m_viewport[2];
h = 0.5 * (double)m_viewport[3];
@@ -266,10 +278,18 @@ void Camera::apply_projection(const BoundingBoxf3& box) const
glsafe(::glMatrixMode(GL_MODELVIEW));
}
+#if ENABLE_THUMBNAIL_GENERATOR
+void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h, double margin_factor)
+#else
void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h)
+#endif // ENABLE_THUMBNAIL_GENERATOR
{
// Calculate the zoom factor needed to adjust the view around the given box.
+#if ENABLE_THUMBNAIL_GENERATOR
+ double zoom = calc_zoom_to_bounding_box_factor(box, canvas_w, canvas_h, margin_factor);
+#else
double zoom = calc_zoom_to_bounding_box_factor(box, canvas_w, canvas_h);
+#endif // ENABLE_THUMBNAIL_GENERATOR
if (zoom > 0.0)
{
m_zoom = zoom;
@@ -278,6 +298,20 @@ void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h)
}
}
+#if ENABLE_THUMBNAIL_GENERATOR
+void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, int canvas_w, int canvas_h, double margin_factor)
+{
+ Vec3d center;
+ double zoom = calc_zoom_to_volumes_factor(volumes, canvas_w, canvas_h, center, margin_factor);
+ if (zoom > 0.0)
+ {
+ m_zoom = zoom;
+ // center view around the calculated center
+ m_target = center;
+ }
+}
+#endif // ENABLE_THUMBNAIL_GENERATOR
+
#if ENABLE_CAMERA_STATISTICS
void Camera::debug_render() const
{
@@ -372,7 +406,11 @@ std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBo
return ret;
}
+#if ENABLE_THUMBNAIL_GENERATOR
+double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h, double margin_factor) const
+#else
double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const
+#endif // ENABLE_THUMBNAIL_GENERATOR
{
double max_bb_size = box.max_size();
if (max_bb_size == 0.0)
@@ -402,34 +440,111 @@ double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int ca
vertices.push_back(box.max);
vertices.emplace_back(box.min(0), box.max(1), box.max(2));
- double max_x = 0.0;
- double max_y = 0.0;
+ double min_x = DBL_MAX;
+ double min_y = DBL_MAX;
+ double max_x = -DBL_MAX;
+ double max_y = -DBL_MAX;
+#if !ENABLE_THUMBNAIL_GENERATOR
// margin factor to give some empty space around the box
double margin_factor = 1.25;
+#endif // !ENABLE_THUMBNAIL_GENERATOR
for (const Vec3d& v : vertices)
{
// project vertex on the plane perpendicular to camera forward axis
- Vec3d pos(v(0) - bb_center(0), v(1) - bb_center(1), v(2) - bb_center(2));
+ Vec3d pos = v - bb_center;
Vec3d proj_on_plane = pos - pos.dot(forward) * forward;
// calculates vertex coordinate along camera xy axes
double x_on_plane = proj_on_plane.dot(right);
double y_on_plane = proj_on_plane.dot(up);
- max_x = std::max(max_x, std::abs(x_on_plane));
- max_y = std::max(max_y, std::abs(y_on_plane));
+ min_x = std::min(min_x, x_on_plane);
+ min_y = std::min(min_y, y_on_plane);
+ max_x = std::max(max_x, x_on_plane);
+ max_y = std::max(max_y, y_on_plane);
}
- if ((max_x == 0.0) || (max_y == 0.0))
+ double dx = max_x - min_x;
+ double dy = max_y - min_y;
+ if ((dx <= 0.0) || (dy <= 0.0))
return -1.0f;
- max_x *= margin_factor;
- max_y *= margin_factor;
+ double med_x = 0.5 * (max_x + min_x);
+ double med_y = 0.5 * (max_y + min_y);
+
+ dx *= margin_factor;
+ dy *= margin_factor;
+
+ return std::min((double)canvas_w / dx, (double)canvas_h / dy);
+}
+
+#if ENABLE_THUMBNAIL_GENERATOR
+double Camera::calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, int canvas_w, int canvas_h, Vec3d& center, double margin_factor) const
+{
+ if (volumes.empty())
+ return -1.0;
+
+ // project the volumes vertices on a plane perpendicular to the camera forward axis
+ // then calculates the vertices coordinate on this plane along the camera xy axes
+
+ // ensure that the view matrix is updated
+ apply_view_matrix();
+
+ Vec3d right = get_dir_right();
+ Vec3d up = get_dir_up();
+ Vec3d forward = get_dir_forward();
+
+ BoundingBoxf3 box;
+ for (const GLVolume* volume : volumes)
+ {
+ box.merge(volume->transformed_bounding_box());
+ }
+ center = box.center();
+
+ double min_x = DBL_MAX;
+ double min_y = DBL_MAX;
+ double max_x = -DBL_MAX;
+ double max_y = -DBL_MAX;
+
+ for (const GLVolume* volume : volumes)
+ {
+ const Transform3d& transform = volume->world_matrix();
+ const TriangleMesh* hull = volume->convex_hull();
+ if (hull == nullptr)
+ continue;
+
+ for (const Vec3f& vertex : hull->its.vertices)
+ {
+ Vec3d v = transform * vertex.cast<double>();
+
+ // project vertex on the plane perpendicular to camera forward axis
+ Vec3d pos = v - center;
+ Vec3d proj_on_plane = pos - pos.dot(forward) * forward;
+
+ // calculates vertex coordinate along camera xy axes
+ double x_on_plane = proj_on_plane.dot(right);
+ double y_on_plane = proj_on_plane.dot(up);
+
+ min_x = std::min(min_x, x_on_plane);
+ min_y = std::min(min_y, y_on_plane);
+ max_x = std::max(max_x, x_on_plane);
+ max_y = std::max(max_y, y_on_plane);
+ }
+ }
+
+ center += 0.5 * (max_x + min_x) * right + 0.5 * (max_y + min_y) * up;
+
+ double dx = margin_factor * (max_x - min_x);
+ double dy = margin_factor * (max_y - min_y);
+
+ if ((dx <= 0.0) || (dy <= 0.0))
+ return -1.0f;
- return std::min((double)canvas_w / (2.0 * max_x), (double)canvas_h / (2.0 * max_y));
+ return std::min((double)canvas_w / dx, (double)canvas_h / dy);
}
+#endif // ENABLE_THUMBNAIL_GENERATOR
void Camera::set_distance(double distance) const
{
diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp
index 839d0d6cf..022ac76eb 100644
--- a/src/slic3r/GUI/Camera.hpp
+++ b/src/slic3r/GUI/Camera.hpp
@@ -2,6 +2,9 @@
#define slic3r_Camera_hpp_
#include "libslic3r/BoundingBox.hpp"
+#if ENABLE_THUMBNAIL_GENERATOR
+#include "3DScene.hpp"
+#endif // ENABLE_THUMBNAIL_GENERATOR
#include <array>
namespace Slic3r {
@@ -10,6 +13,10 @@ namespace GUI {
struct Camera
{
static const double DefaultDistance;
+#if ENABLE_THUMBNAIL_GENERATOR
+ static const double DefaultZoomToBoxMarginFactor;
+ static const double DefaultZoomToVolumesMarginFactor;
+#endif // ENABLE_THUMBNAIL_GENERATOR
static double FrustrumMinZRange;
static double FrustrumMinNearZ;
static double FrustrumZMargin;
@@ -88,9 +95,16 @@ public:
void apply_viewport(int x, int y, unsigned int w, unsigned int h) const;
void apply_view_matrix() const;
- void apply_projection(const BoundingBoxf3& box) const;
-
+ // Calculates and applies the projection matrix tighting the frustrum z range around the given box.
+ // If larger z span is needed, pass the desired values of near and far z (negative values are ignored)
+ void apply_projection(const BoundingBoxf3& box, double near_z = -1.0, double far_z = -1.0) const;
+
+#if ENABLE_THUMBNAIL_GENERATOR
+ void zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h, double margin_factor = DefaultZoomToBoxMarginFactor);
+ void zoom_to_volumes(const GLVolumePtrs& volumes, int canvas_w, int canvas_h, double margin_factor = DefaultZoomToVolumesMarginFactor);
+#else
void zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h);
+#endif // ENABLE_THUMBNAIL_GENERATOR
#if ENABLE_CAMERA_STATISTICS
void debug_render() const;
@@ -100,7 +114,12 @@ private:
// returns tight values for nearZ and farZ plane around the given bounding box
// the camera MUST be outside of the bounding box in eye coordinate of the given box
std::pair<double, double> calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const;
+#if ENABLE_THUMBNAIL_GENERATOR
+ double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h, double margin_factor = DefaultZoomToBoxMarginFactor) const;
+ double calc_zoom_to_volumes_factor(const GLVolumePtrs& volumes, int canvas_w, int canvas_h, Vec3d& center, double margin_factor = DefaultZoomToVolumesMarginFactor) const;
+#else
double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const;
+#endif // ENABLE_THUMBNAIL_GENERATOR
void set_distance(double distance) const;
};
diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp
index 71bacd815..0bd5bb24b 100644
--- a/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/src/slic3r/GUI/GLCanvas3D.cpp
@@ -7,6 +7,9 @@
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/GCode/PreviewData.hpp"
+#if ENABLE_THUMBNAIL_GENERATOR
+#include "libslic3r/GCode/ThumbnailData.hpp"
+#endif // ENABLE_THUMBNAIL_GENERATOR
#include "libslic3r/Geometry.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/Utils.hpp"
@@ -1119,6 +1122,10 @@ wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
+#if ENABLE_THUMBNAIL_GENERATOR
+const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25;
+#endif // ENABLE_THUMBNAIL_GENERATOR
+
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar)
: m_canvas(canvas)
, m_context(nullptr)
@@ -1585,7 +1592,7 @@ void GLCanvas3D::render()
_render_objects();
_render_sla_slices();
_render_selection();
- _render_bed(theta);
+ _render_bed(theta, true);
#if ENABLE_RENDER_SELECTION_CENTER
_render_selection_center();
@@ -1645,6 +1652,18 @@ void GLCanvas3D::render()
#endif // ENABLE_RENDER_STATISTICS
}
+#if ENABLE_THUMBNAIL_GENERATOR
+void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
+{
+ switch (GLCanvas3DManager::get_framebuffers_type())
+ {
+ case GLCanvas3DManager::FB_Arb: { _render_thumbnail_framebuffer(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; }
+ case GLCanvas3DManager::FB_Ext: { _render_thumbnail_framebuffer_ext(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; }
+ default: { _render_thumbnail_legacy(thumbnail_data, w, h, printable_only, parts_only, show_bed, transparent_background); break; }
+ }
+}
+#endif // ENABLE_THUMBNAIL_GENERATOR
+
void GLCanvas3D::select_all()
{
m_selection.add_all();
@@ -3526,6 +3545,323 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x)
imgui->end();
}
+#if ENABLE_THUMBNAIL_GENERATOR
+void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
+{
+ auto is_visible = [](const GLVolume& v) -> bool
+ {
+ bool ret = v.printable;
+ ret &= (!v.shader_outside_printer_detection_enabled || !v.is_outside);
+ return ret;
+ };
+
+ static const GLfloat orange[] = { 0.923f, 0.504f, 0.264f, 1.0f };
+ static const GLfloat gray[] = { 0.64f, 0.64f, 0.64f, 1.0f };
+
+ GLVolumePtrs visible_volumes;
+
+ for (GLVolume* vol : m_volumes.volumes)
+ {
+ if (!vol->is_modifier && !vol->is_wipe_tower && (!parts_only || (vol->composite_id.volume_id >= 0)))
+ {
+ if (!printable_only || is_visible(*vol))
+ visible_volumes.push_back(vol);
+ }
+ }
+
+ if (visible_volumes.empty())
+ return;
+
+ BoundingBoxf3 box;
+ for (const GLVolume* vol : visible_volumes)
+ {
+ box.merge(vol->transformed_bounding_box());
+ }
+
+ Camera camera;
+ camera.set_type(Camera::Ortho);
+ camera.zoom_to_volumes(visible_volumes, thumbnail_data.width, thumbnail_data.height);
+ camera.apply_viewport(0, 0, thumbnail_data.width, thumbnail_data.height);
+ camera.apply_view_matrix();
+
+ double near_z = -1.0;
+ double far_z = -1.0;
+
+ if (show_bed)
+ {
+ // extends the near and far z of the frustrum to avoid the bed being clipped
+
+ // box in eye space
+ BoundingBoxf3 t_bed_box = m_bed.get_bounding_box(true).transformed(camera.get_view_matrix());
+ near_z = -t_bed_box.max(2);
+ far_z = -t_bed_box.min(2);
+ }
+
+ camera.apply_projection(box, near_z, far_z);
+
+ if (transparent_background)
+ glsafe(::glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
+
+ glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
+ glsafe(::glEnable(GL_DEPTH_TEST));
+
+ m_shader.start_using();
+
+ GLint shader_id = m_shader.get_shader_program_id();
+ GLint color_id = ::glGetUniformLocation(shader_id, "uniform_color");
+ GLint print_box_detection_id = ::glGetUniformLocation(shader_id, "print_box.volume_detection");
+ glcheck();
+
+ if (print_box_detection_id != -1)
+ glsafe(::glUniform1i(print_box_detection_id, 0));
+
+ for (const GLVolume* vol : visible_volumes)
+ {
+ if (color_id >= 0)
+ glsafe(::glUniform4fv(color_id, 1, (vol->printable && !vol->is_outside) ? orange : gray));
+ else
+ glsafe(::glColor4fv((vol->printable && !vol->is_outside) ? orange : gray));
+
+ vol->render();
+ }
+
+ m_shader.stop_using();
+
+ glsafe(::glDisable(GL_DEPTH_TEST));
+
+ if (show_bed)
+ _render_bed(camera.get_theta(), false);
+
+ if (transparent_background)
+ glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 1.0f));
+}
+
+void GLCanvas3D::_render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
+{
+ thumbnail_data.set(w, h);
+ if (!thumbnail_data.is_valid())
+ return;
+
+ bool multisample = m_multisample_allowed;
+ if (multisample)
+ glsafe(::glEnable(GL_MULTISAMPLE));
+
+ GLint max_samples;
+ glsafe(::glGetIntegerv(GL_MAX_SAMPLES, &max_samples));
+ GLsizei num_samples = max_samples / 2;
+
+ GLuint render_fbo;
+ glsafe(::glGenFramebuffers(1, &render_fbo));
+ glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, render_fbo));
+
+ GLuint render_tex = 0;
+ GLuint render_tex_buffer = 0;
+ if (multisample)
+ {
+ // use renderbuffer instead of texture to avoid the need to use glTexImage2DMultisample which is available only since OpenGL 3.2
+ glsafe(::glGenRenderbuffers(1, &render_tex_buffer));
+ glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_tex_buffer));
+ glsafe(::glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples, GL_RGBA8, w, h));
+ glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_tex_buffer));
+ }
+ else
+ {
+ glsafe(::glGenTextures(1, &render_tex));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, render_tex));
+ glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render_tex, 0));
+ }
+
+ GLuint render_depth;
+ glsafe(::glGenRenderbuffers(1, &render_depth));
+ glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_depth));
+ if (multisample)
+ glsafe(::glRenderbufferStorageMultisample(GL_RENDERBUFFER, num_samples, GL_DEPTH_COMPONENT24, w, h));
+ else
+ glsafe(::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h));
+
+ glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render_depth));
+
+ GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0 };
+ glsafe(::glDrawBuffers(1, drawBufs));
+
+ if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
+ {
+ _render_thumbnail_internal(thumbnail_data, printable_only, parts_only, show_bed, transparent_background);
+
+ if (multisample)
+ {
+ GLuint resolve_fbo;
+ glsafe(::glGenFramebuffers(1, &resolve_fbo));
+ glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, resolve_fbo));
+
+ GLuint resolve_tex;
+ glsafe(::glGenTextures(1, &resolve_tex));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, resolve_tex));
+ glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolve_tex, 0));
+
+ glsafe(::glDrawBuffers(1, drawBufs));
+
+ if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
+ {
+ glsafe(::glBindFramebuffer(GL_READ_FRAMEBUFFER, render_fbo));
+ glsafe(::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolve_fbo));
+ glsafe(::glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR));
+
+ glsafe(::glBindFramebuffer(GL_READ_FRAMEBUFFER, resolve_fbo));
+ glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data()));
+ }
+
+ glsafe(::glDeleteTextures(1, &resolve_tex));
+ glsafe(::glDeleteFramebuffers(1, &resolve_fbo));
+ }
+ else
+ glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data()));
+ }
+
+ glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
+ glsafe(::glDeleteRenderbuffers(1, &render_depth));
+ if (render_tex_buffer != 0)
+ glsafe(::glDeleteRenderbuffers(1, &render_tex_buffer));
+ if (render_tex != 0)
+ glsafe(::glDeleteTextures(1, &render_tex));
+ glsafe(::glDeleteFramebuffers(1, &render_fbo));
+
+ if (multisample)
+ glsafe(::glDisable(GL_MULTISAMPLE));
+}
+
+void GLCanvas3D::_render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
+{
+ thumbnail_data.set(w, h);
+ if (!thumbnail_data.is_valid())
+ return;
+
+ bool multisample = m_multisample_allowed;
+ if (multisample)
+ glsafe(::glEnable(GL_MULTISAMPLE));
+
+ GLint max_samples;
+ glsafe(::glGetIntegerv(GL_MAX_SAMPLES_EXT, &max_samples));
+ GLsizei num_samples = max_samples / 2;
+
+ GLuint render_fbo;
+ glsafe(::glGenFramebuffersEXT(1, &render_fbo));
+ glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, render_fbo));
+
+ GLuint render_tex = 0;
+ GLuint render_tex_buffer = 0;
+ if (multisample)
+ {
+ // use renderbuffer instead of texture to avoid the need to use glTexImage2DMultisample which is available only since OpenGL 3.2
+ glsafe(::glGenRenderbuffersEXT(1, &render_tex_buffer));
+ glsafe(::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_tex_buffer));
+ glsafe(::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, num_samples, GL_RGBA8, w, h));
+ glsafe(::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, render_tex_buffer));
+ }
+ else
+ {
+ glsafe(::glGenTextures(1, &render_tex));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, render_tex));
+ glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, render_tex, 0));
+ }
+
+ GLuint render_depth;
+ glsafe(::glGenRenderbuffersEXT(1, &render_depth));
+ glsafe(::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_depth));
+ if (multisample)
+ glsafe(::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, num_samples, GL_DEPTH_COMPONENT24, w, h));
+ else
+ glsafe(::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, w, h));
+
+ glsafe(::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, render_depth));
+
+ GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0 };
+ glsafe(::glDrawBuffers(1, drawBufs));
+
+ if (::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ _render_thumbnail_internal(thumbnail_data, printable_only, parts_only, show_bed, transparent_background);
+
+ if (multisample)
+ {
+ GLuint resolve_fbo;
+ glsafe(::glGenFramebuffersEXT(1, &resolve_fbo));
+ glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, resolve_fbo));
+
+ GLuint resolve_tex;
+ glsafe(::glGenTextures(1, &resolve_tex));
+ glsafe(::glBindTexture(GL_TEXTURE_2D, resolve_tex));
+ glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ glsafe(::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, resolve_tex, 0));
+
+ glsafe(::glDrawBuffers(1, drawBufs));
+
+ if (::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT)
+ {
+ glsafe(::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, render_fbo));
+ glsafe(::glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, resolve_fbo));
+ glsafe(::glBlitFramebufferEXT(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR));
+
+ glsafe(::glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, resolve_fbo));
+ glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data()));
+ }
+
+ glsafe(::glDeleteTextures(1, &resolve_tex));
+ glsafe(::glDeleteFramebuffersEXT(1, &resolve_fbo));
+ }
+ else
+ glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data()));
+ }
+
+ glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
+ glsafe(::glDeleteRenderbuffersEXT(1, &render_depth));
+ if (render_tex_buffer != 0)
+ glsafe(::glDeleteRenderbuffersEXT(1, &render_tex_buffer));
+ if (render_tex != 0)
+ glsafe(::glDeleteTextures(1, &render_tex));
+ glsafe(::glDeleteFramebuffersEXT(1, &render_fbo));
+
+ if (multisample)
+ glsafe(::glDisable(GL_MULTISAMPLE));
+}
+
+void GLCanvas3D::_render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
+{
+ // check that thumbnail size does not exceed the default framebuffer size
+ const Size& cnv_size = get_canvas_size();
+ unsigned int cnv_w = (unsigned int)cnv_size.get_width();
+ unsigned int cnv_h = (unsigned int)cnv_size.get_height();
+ if ((w > cnv_w) || (h > cnv_h))
+ {
+ float ratio = std::min((float)cnv_w / (float)w, (float)cnv_h / (float)h);
+ w = (unsigned int)(ratio * (float)w);
+ h = (unsigned int)(ratio * (float)h);
+ }
+
+ thumbnail_data.set(w, h);
+ if (!thumbnail_data.is_valid())
+ return;
+
+ _render_thumbnail_internal(thumbnail_data, printable_only, parts_only, show_bed, transparent_background);
+
+ glsafe(::glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data()));
+
+ // restore the default framebuffer size to avoid flickering on the 3D scene
+ m_camera.apply_viewport(0, 0, cnv_size.get_width(), cnv_size.get_height());
+}
+#endif // ENABLE_THUMBNAIL_GENERATOR
+
bool GLCanvas3D::_init_toolbars()
{
if (!_init_main_toolbar())
@@ -3837,12 +4173,21 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be
return bb;
}
+#if ENABLE_THUMBNAIL_GENERATOR
+void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box, double margin_factor)
+{
+ const Size& cnv_size = get_canvas_size();
+ m_camera.zoom_to_box(box, cnv_size.get_width(), cnv_size.get_height(), margin_factor);
+ m_dirty = true;
+}
+#else
void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box)
{
const Size& cnv_size = get_canvas_size();
m_camera.zoom_to_box(box, cnv_size.get_width(), cnv_size.get_height());
m_dirty = true;
}
+#endif // ENABLE_THUMBNAIL_GENERATOR
void GLCanvas3D::_refresh_if_shown_on_screen()
{
@@ -4025,13 +4370,13 @@ void GLCanvas3D::_render_background() const
glsafe(::glPopMatrix());
}
-void GLCanvas3D::_render_bed(float theta) const
+void GLCanvas3D::_render_bed(float theta, bool show_axes) const
{
float scale_factor = 1.0;
#if ENABLE_RETINA_GL
scale_factor = m_retina_helper->get_scale_factor();
#endif // ENABLE_RETINA_GL
- m_bed.render(const_cast<GLCanvas3D&>(*this), theta, scale_factor);
+ m_bed.render(const_cast<GLCanvas3D&>(*this), theta, scale_factor, show_axes);
}
void GLCanvas3D::_render_objects() const
@@ -4039,7 +4384,9 @@ void GLCanvas3D::_render_objects() const
if (m_volumes.empty())
return;
+#if !ENABLE_THUMBNAIL_GENERATOR
glsafe(::glEnable(GL_LIGHTING));
+#endif // !ENABLE_THUMBNAIL_GENERATOR
glsafe(::glEnable(GL_DEPTH_TEST));
m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane();
@@ -4083,7 +4430,9 @@ void GLCanvas3D::_render_objects() const
m_shader.stop_using();
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
+#if !ENABLE_THUMBNAIL_GENERATOR
glsafe(::glDisable(GL_LIGHTING));
+#endif // !ENABLE_THUMBNAIL_GENERATOR
}
void GLCanvas3D::_render_selection() const
diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp
index 0beef094b..66ecf315a 100644
--- a/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/src/slic3r/GUI/GLCanvas3D.hpp
@@ -35,6 +35,9 @@ class GLShader;
class ExPolygon;
class BackgroundSlicingProcess;
class GCodePreviewData;
+#if ENABLE_THUMBNAIL_GENERATOR
+struct ThumbnailData;
+#endif // ENABLE_THUMBNAIL_GENERATOR
struct SlicingParameters;
enum LayerHeightEditActionType : unsigned int;
@@ -131,6 +134,10 @@ wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
class GLCanvas3D
{
+#if ENABLE_THUMBNAIL_GENERATOR
+ static const double DefaultCameraZoomToBoxMarginFactor;
+#endif // ENABLE_THUMBNAIL_GENERATOR
+
public:
struct GCodePreviewVolumeIndex
{
@@ -543,6 +550,11 @@ public:
bool is_dragging() const { return m_gizmos.is_dragging() || m_moving; }
void render();
+#if ENABLE_THUMBNAIL_GENERATOR
+ // printable_only == false -> render also non printable volumes as grayed
+ // parts_only == false -> render also sla support and pad
+ void render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
+#endif // ENABLE_THUMBNAIL_GENERATOR
void select_all();
void deselect_all();
@@ -662,14 +674,18 @@ private:
BoundingBoxf3 _max_bounding_box(bool include_gizmos, bool include_bed_model) const;
+#if ENABLE_THUMBNAIL_GENERATOR
+ void _zoom_to_box(const BoundingBoxf3& box, double margin_factor = DefaultCameraZoomToBoxMarginFactor);
+#else
void _zoom_to_box(const BoundingBoxf3& box);
+#endif // ENABLE_THUMBNAIL_GENERATOR
void _refresh_if_shown_on_screen();
void _picking_pass() const;
void _rectangular_selection_picking_pass() const;
void _render_background() const;
- void _render_bed(float theta) const;
+ void _render_bed(float theta, bool show_axes) const;
void _render_objects() const;
void _render_selection() const;
#if ENABLE_RENDER_SELECTION_CENTER
@@ -690,6 +706,15 @@ private:
void _render_sla_slices() const;
void _render_selection_sidebar_hints() const;
void _render_undo_redo_stack(const bool is_undo, float pos_x);
+#if ENABLE_THUMBNAIL_GENERATOR
+ void _render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
+ // render thumbnail using an off-screen framebuffer
+ void _render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
+ // render thumbnail using an off-screen framebuffer when GLEW_EXT_framebuffer_object is supported
+ void _render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
+ // render thumbnail using the default framebuffer
+ void _render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
+#endif // ENABLE_THUMBNAIL_GENERATOR
void _update_volumes_hover_state() const;
diff --git a/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp
index 59a34d8cd..1a887d543 100644
--- a/src/slic3r/GUI/GLCanvas3DManager.cpp
+++ b/src/slic3r/GUI/GLCanvas3DManager.cpp
@@ -187,6 +187,7 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown;
bool GLCanvas3DManager::s_compressed_textures_supported = false;
+GLCanvas3DManager::EFramebufferType GLCanvas3DManager::s_framebuffers_type = GLCanvas3DManager::FB_None;
GLCanvas3DManager::GLInfo GLCanvas3DManager::s_gl_info;
GLCanvas3DManager::GLCanvas3DManager()
@@ -267,6 +268,13 @@ void GLCanvas3DManager::init_gl()
else
s_compressed_textures_supported = false;
+ if (GLEW_ARB_framebuffer_object)
+ s_framebuffers_type = FB_Arb;
+ else if (GLEW_EXT_framebuffer_object)
+ s_framebuffers_type = FB_Ext;
+ else
+ s_framebuffers_type = FB_None;
+
if (! s_gl_info.is_version_greater_or_equal_to(2, 0)) {
// Complain about the OpenGL version.
wxString message = wxString::Format(
diff --git a/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp
index 760266a27..940e0230a 100644
--- a/src/slic3r/GUI/GLCanvas3DManager.hpp
+++ b/src/slic3r/GUI/GLCanvas3DManager.hpp
@@ -30,6 +30,13 @@ struct Camera;
class GLCanvas3DManager
{
public:
+ enum EFramebufferType : unsigned char
+ {
+ FB_None,
+ FB_Arb,
+ FB_Ext
+ };
+
class GLInfo
{
mutable bool m_detected;
@@ -77,6 +84,7 @@ private:
bool m_gl_initialized;
static EMultisampleState s_multisample;
static bool s_compressed_textures_supported;
+ static EFramebufferType s_framebuffers_type;
public:
GLCanvas3DManager();
@@ -97,6 +105,8 @@ public:
static bool can_multisample() { return s_multisample == MS_Enabled; }
static bool are_compressed_textures_supported() { return s_compressed_textures_supported; }
+ static bool are_framebuffers_supported() { return (s_framebuffers_type != FB_None); }
+ static EFramebufferType get_framebuffers_type() { return s_framebuffers_type; }
static wxGLCanvas* create_wxglcanvas(wxWindow *parent);
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index ef6a7d3c5..c3683b8b0 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -31,6 +31,9 @@
#include "libslic3r/Format/AMF.hpp"
#include "libslic3r/Format/3mf.hpp"
#include "libslic3r/GCode/PreviewData.hpp"
+#if ENABLE_THUMBNAIL_GENERATOR
+#include "libslic3r/GCode/ThumbnailData.hpp"
+#endif // ENABLE_THUMBNAIL_GENERATOR
#include "libslic3r/Model.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Print.hpp"
@@ -1901,6 +1904,11 @@ struct Plater::priv
bool can_set_instance_to_object() const;
bool can_mirror() const;
+#if ENABLE_THUMBNAIL_GENERATOR
+ void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
+ void generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
+#endif // ENABLE_THUMBNAIL_GENERATOR
+
void msw_rescale_object_menu();
// returns the path to project file with the given extension (none if extension == wxEmptyString)
@@ -1968,6 +1976,17 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
background_process.set_fff_print(&fff_print);
background_process.set_sla_print(&sla_print);
background_process.set_gcode_preview_data(&gcode_preview_data);
+#if ENABLE_THUMBNAIL_GENERATOR
+ background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
+ {
+ std::packaged_task<void(ThumbnailsList&, const Vec2ds&, bool, bool, bool, bool)> task([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) {
+ generate_thumbnails(thumbnails, sizes, printable_only, parts_only, show_bed, transparent_background);
+ });
+ std::future<void> result = task.get_future();
+ wxTheApp->CallAfter([&]() { task(thumbnails, sizes, printable_only, parts_only, show_bed, transparent_background); });
+ result.wait();
+ });
+#endif // ENABLE_THUMBNAIL_GENERATOR
background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED);
background_process.set_finished_event(EVT_PROCESS_COMPLETED);
// Default printer technology for default config.
@@ -3481,6 +3500,26 @@ bool Plater::priv::init_object_menu()
return true;
}
+#if ENABLE_THUMBNAIL_GENERATOR
+void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
+{
+ view3D->get_canvas3d()->render_thumbnail(data, w, h, printable_only, parts_only, show_bed, transparent_background);
+}
+
+void Plater::priv::generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
+{
+ thumbnails.clear();
+ for (const Vec2d& size : sizes)
+ {
+ thumbnails.push_back(ThumbnailData());
+ Point isize(size); // round to ints
+ generate_thumbnail(thumbnails.back(), isize.x(), isize.y(), printable_only, parts_only, show_bed, transparent_background);
+ if (!thumbnails.back().is_valid())
+ thumbnails.pop_back();
+ }
+}
+#endif // ENABLE_THUMBNAIL_GENERATOR
+
void Plater::priv::msw_rescale_object_menu()
{
for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu })
diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp
index d2503d349..69e6c86d5 100644
--- a/src/slic3r/GUI/Preset.cpp
+++ b/src/slic3r/GUI/Preset.cpp
@@ -426,7 +426,8 @@ const std::vector<std::string>& Preset::printer_options()
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
"machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
"machine_min_extruding_rate", "machine_min_travel_rate",
- "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e"
+ "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e",
+ "thumbnails"
};
s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end());
}
diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp
index 79d49f13a..f6167ae74 100644
--- a/src/slic3r/GUI/PresetBundle.cpp
+++ b/src/slic3r/GUI/PresetBundle.cpp
@@ -93,6 +93,7 @@ PresetBundle::PresetBundle() :
preset.config.optptr("printer_vendor", true);
preset.config.optptr("printer_model", true);
preset.config.optptr("printer_variant", true);
+ preset.config.optptr("thumbnails", true);
if (i == 0) {
preset.config.optptr("default_print_profile", true);
preset.config.option<ConfigOptionStrings>("default_filament_profile", true)->values = { "" };