mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-05-21 20:18:17 +08:00
ENABLE_THUMBNAIL_GENERATOR -> Use off-screen framebuffer to render the thumbnail on graphic cards supporting it
This commit is contained in:
parent
f36dd833d2
commit
3ff3eed2b1
@ -1649,88 +1649,10 @@ void GLCanvas3D::render()
|
|||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only)
|
void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only)
|
||||||
{
|
{
|
||||||
auto is_visible = [](const GLVolume& v) -> bool {
|
if (GLCanvas3DManager::are_framebuffers_supported())
|
||||||
bool ret = v.printable;
|
_render_thumbnail_framebuffer(thumbnail_data, w, h, printable_only, parts_only);
|
||||||
ret &= (!v.shader_outside_printer_detection_enabled || !v.is_outside);
|
else
|
||||||
return ret;
|
_render_thumbnail_legacy(thumbnail_data, w, h, printable_only, parts_only);
|
||||||
};
|
|
||||||
|
|
||||||
static const float orange[] = { 0.99f, 0.49f, 0.26f };
|
|
||||||
static const float gray[] = { 0.64f, 0.64f, 0.64f };
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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.zoom_to_box(box, thumbnail_data.width, thumbnail_data.height);
|
|
||||||
camera.apply_viewport(0, 0, thumbnail_data.width, thumbnail_data.height);
|
|
||||||
camera.apply_view_matrix();
|
|
||||||
camera.apply_projection(box);
|
|
||||||
|
|
||||||
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
|
||||||
glsafe(::glEnable(GL_LIGHTING));
|
|
||||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
|
||||||
|
|
||||||
for (const GLVolume* vol : visible_volumes)
|
|
||||||
{
|
|
||||||
glsafe(::glColor3fv((vol->printable && !vol->is_outside) ? orange : gray));
|
|
||||||
vol->render();
|
|
||||||
}
|
|
||||||
|
|
||||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
|
||||||
glsafe(::glDisable(GL_LIGHTING));
|
|
||||||
glsafe(::glReadPixels(0, 0, thumbnail_data.width, thumbnail_data.height, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data()));
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// debug export of generated image
|
|
||||||
wxImage image(thumbnail_data.width, thumbnail_data.height);
|
|
||||||
image.InitAlpha();
|
|
||||||
|
|
||||||
for (unsigned int r = 0; r < thumbnail_data.height; ++r)
|
|
||||||
{
|
|
||||||
unsigned int rr = (thumbnail_data.height - 1 - r) * thumbnail_data.width;
|
|
||||||
for (unsigned int c = 0; c < thumbnail_data.width; ++c)
|
|
||||||
{
|
|
||||||
unsigned char* px = thumbnail_data.pixels.data() + 4 * (rr + c);
|
|
||||||
image.SetRGB((int)c, (int)r, px[0], px[1], px[2]);
|
|
||||||
image.SetAlpha((int)c, (int)r, px[3]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
image.SaveFile("C:/test.png", wxBITMAP_TYPE_PNG);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// restore the 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
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
||||||
@ -3644,6 +3566,139 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x)
|
|||||||
imgui->end();
|
imgui->end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
static void render_volumes_in_thumbnail(const GLVolumePtrs& volumes, ThumbnailData& thumbnail_data, bool printable_only, bool parts_only)
|
||||||
|
{
|
||||||
|
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 float orange[] = { 0.99f, 0.49f, 0.26f };
|
||||||
|
static const float gray[] = { 0.64f, 0.64f, 0.64f };
|
||||||
|
|
||||||
|
GLVolumePtrs visible_volumes;
|
||||||
|
|
||||||
|
for (GLVolume* vol : 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.zoom_to_box(box, thumbnail_data.width, thumbnail_data.height);
|
||||||
|
camera.apply_viewport(0, 0, thumbnail_data.width, thumbnail_data.height);
|
||||||
|
camera.apply_view_matrix();
|
||||||
|
camera.apply_projection(box);
|
||||||
|
|
||||||
|
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
||||||
|
glsafe(::glEnable(GL_LIGHTING));
|
||||||
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||||
|
|
||||||
|
for (const GLVolume* vol : visible_volumes)
|
||||||
|
{
|
||||||
|
glsafe(::glColor3fv((vol->printable && !vol->is_outside) ? orange : gray));
|
||||||
|
vol->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||||
|
glsafe(::glDisable(GL_LIGHTING));
|
||||||
|
glsafe(::glReadPixels(0, 0, thumbnail_data.width, thumbnail_data.height, GL_RGBA, GL_UNSIGNED_BYTE, (void*)thumbnail_data.pixels.data()));
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// debug export of generated image
|
||||||
|
wxImage image(thumbnail_data.width, thumbnail_data.height);
|
||||||
|
image.InitAlpha();
|
||||||
|
|
||||||
|
for (unsigned int r = 0; r < thumbnail_data.height; ++r)
|
||||||
|
{
|
||||||
|
unsigned int rr = (thumbnail_data.height - 1 - r) * thumbnail_data.width;
|
||||||
|
for (unsigned int c = 0; c < thumbnail_data.width; ++c)
|
||||||
|
{
|
||||||
|
unsigned char* px = thumbnail_data.pixels.data() + 4 * (rr + c);
|
||||||
|
image.SetRGB((int)c, (int)r, px[0], px[1], px[2]);
|
||||||
|
image.SetAlpha((int)c, (int)r, px[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
image.SaveFile("C:/prusa/test/test.png", wxBITMAP_TYPE_PNG);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::_render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only)
|
||||||
|
{
|
||||||
|
thumbnail_data.set(w, h);
|
||||||
|
if (!thumbnail_data.is_valid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
GLuint fbo;
|
||||||
|
glsafe(::glGenFramebuffers(1, &fbo));
|
||||||
|
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, fbo));
|
||||||
|
|
||||||
|
GLuint tex;
|
||||||
|
glsafe(::glGenTextures(1, &tex));
|
||||||
|
glsafe(::glBindTexture(GL_TEXTURE_2D, 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, tex, 0));
|
||||||
|
|
||||||
|
GLuint depth;
|
||||||
|
glsafe(::glGenRenderbuffers(1, &depth));
|
||||||
|
glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, depth));
|
||||||
|
glsafe(::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h));
|
||||||
|
glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth));
|
||||||
|
|
||||||
|
GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0 };
|
||||||
|
glsafe(::glDrawBuffers(1, drawBufs));
|
||||||
|
|
||||||
|
if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
|
||||||
|
render_volumes_in_thumbnail(m_volumes.volumes, thumbnail_data, printable_only, parts_only);
|
||||||
|
|
||||||
|
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
||||||
|
glsafe(::glDeleteRenderbuffers(1, &depth));
|
||||||
|
glsafe(::glDeleteTextures(1, &tex));
|
||||||
|
glsafe(::glDeleteFramebuffers(1, &fbo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::_render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only)
|
||||||
|
{
|
||||||
|
// 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_volumes_in_thumbnail(m_volumes.volumes, thumbnail_data, printable_only, parts_only);
|
||||||
|
|
||||||
|
// 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()
|
bool GLCanvas3D::_init_toolbars()
|
||||||
{
|
{
|
||||||
if (!_init_main_toolbar())
|
if (!_init_main_toolbar())
|
||||||
|
@ -674,6 +674,12 @@ private:
|
|||||||
void _render_sla_slices() const;
|
void _render_sla_slices() const;
|
||||||
void _render_selection_sidebar_hints() const;
|
void _render_selection_sidebar_hints() const;
|
||||||
void _render_undo_redo_stack(const bool is_undo, float pos_x);
|
void _render_undo_redo_stack(const bool is_undo, float pos_x);
|
||||||
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
// 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);
|
||||||
|
// 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);
|
||||||
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
||||||
void _update_volumes_hover_state() const;
|
void _update_volumes_hover_state() const;
|
||||||
|
|
||||||
|
@ -189,6 +189,7 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
|
|||||||
|
|
||||||
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown;
|
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown;
|
||||||
bool GLCanvas3DManager::s_compressed_textures_supported = false;
|
bool GLCanvas3DManager::s_compressed_textures_supported = false;
|
||||||
|
bool GLCanvas3DManager::s_framebuffers_supported = false;
|
||||||
GLCanvas3DManager::GLInfo GLCanvas3DManager::s_gl_info;
|
GLCanvas3DManager::GLInfo GLCanvas3DManager::s_gl_info;
|
||||||
|
|
||||||
GLCanvas3DManager::GLCanvas3DManager()
|
GLCanvas3DManager::GLCanvas3DManager()
|
||||||
@ -269,6 +270,11 @@ void GLCanvas3DManager::init_gl()
|
|||||||
else
|
else
|
||||||
s_compressed_textures_supported = false;
|
s_compressed_textures_supported = false;
|
||||||
|
|
||||||
|
if (s_gl_info.is_version_greater_or_equal_to(3, 0) && GLEW_ARB_framebuffer_object)
|
||||||
|
s_framebuffers_supported = true;
|
||||||
|
else
|
||||||
|
s_framebuffers_supported = false;
|
||||||
|
|
||||||
if (! s_gl_info.is_version_greater_or_equal_to(2, 0)) {
|
if (! s_gl_info.is_version_greater_or_equal_to(2, 0)) {
|
||||||
// Complain about the OpenGL version.
|
// Complain about the OpenGL version.
|
||||||
wxString message = wxString::Format(
|
wxString message = wxString::Format(
|
||||||
|
@ -77,6 +77,7 @@ private:
|
|||||||
bool m_gl_initialized;
|
bool m_gl_initialized;
|
||||||
static EMultisampleState s_multisample;
|
static EMultisampleState s_multisample;
|
||||||
static bool s_compressed_textures_supported;
|
static bool s_compressed_textures_supported;
|
||||||
|
static bool s_framebuffers_supported;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GLCanvas3DManager();
|
GLCanvas3DManager();
|
||||||
@ -97,6 +98,7 @@ public:
|
|||||||
|
|
||||||
static bool can_multisample() { return s_multisample == MS_Enabled; }
|
static bool can_multisample() { return s_multisample == MS_Enabled; }
|
||||||
static bool are_compressed_textures_supported() { return s_compressed_textures_supported; }
|
static bool are_compressed_textures_supported() { return s_compressed_textures_supported; }
|
||||||
|
static bool are_framebuffers_supported() { return s_framebuffers_supported; }
|
||||||
|
|
||||||
static wxGLCanvas* create_wxglcanvas(wxWindow *parent);
|
static wxGLCanvas* create_wxglcanvas(wxWindow *parent);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user