From ba4bc8ac82b5ae524f2ce62a5b079259483e82f4 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 19 Jul 2019 09:18:09 +0200 Subject: [PATCH] Render custom bed textures in png format on prusa beds --- resources/shaders/printbed.fs | 22 ++++- src/slic3r/GUI/3DBed.cpp | 33 +++++-- src/slic3r/GUI/GLTexture.cpp | 180 ++++++++++++++++++++++++++++++++++ src/slic3r/GUI/GLTexture.hpp | 2 + 4 files changed, 226 insertions(+), 11 deletions(-) diff --git a/resources/shaders/printbed.fs b/resources/shaders/printbed.fs index be14347c2..d1316ca2f 100644 --- a/resources/shaders/printbed.fs +++ b/resources/shaders/printbed.fs @@ -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(); } \ No newline at end of file diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index f07b224ac..27531f030 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -519,13 +519,18 @@ void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom if ((m_texture.get_id() == 0) || (m_texture.get_source() != filename)) { + m_texture.reset(); + if (boost::algorithm::iends_with(filename, ".svg")) { - // 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)) + if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != filename)) { - render_custom(); - return; + // 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; + } } // starts generating the main texture, compression will run asynchronously @@ -537,9 +542,22 @@ void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom } else if (boost::algorithm::iends_with(filename, ".png")) { - std::cout << "texture: " << filename << std::endl; - render_custom(); - return; + // 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, false, false, max_tex_size)) + { + render_custom(); + return; + } + } + + // starts generating the main texture, compression will run asynchronously + if (!m_texture.load_from_file(filename, true, true, true, max_tex_size)) + { + render_custom(); + return; + } } else { @@ -634,6 +652,7 @@ void Bed3D::render_prusa_shader(bool transparent) const { m_shader.start_using(); m_shader.set_uniform("transparent_background", transparent); + m_shader.set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); unsigned int stride = m_triangles.get_vertex_data_size(); diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 171a5c885..495a9864c 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -147,6 +147,19 @@ bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, bo return false; } +bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px) +{ + reset(); + + if (!boost::filesystem::exists(filename)) + return false; + + if (boost::algorithm::iends_with(filename, ".png")) + return load_from_png(filename, use_mipmaps, compress, apply_anisotropy, max_size_px); + else + return false; +} + bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px) { reset(); @@ -465,6 +478,172 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, boo return true; } +bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px) +{ + bool compression_enabled = compress && GLEW_EXT_texture_compression_s3tc; + + // Load a PNG with an alpha channel. + wxImage image; + if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG)) + { + reset(); + return false; + } + + m_width = image.GetWidth(); + m_height = image.GetHeight(); + + unsigned int max_size = (unsigned int)std::max(m_width, m_height); + bool requires_rescale = false; + if (max_size_px < max_size) + { + float scale = (float)max_size_px / (float)max_size; + + m_width = (int)(scale * (float)m_width); + m_height = (int)(scale * (float)m_height); + requires_rescale = true; + } + + if (compression_enabled) + { + // the stb_dxt compression library seems to like only texture sizes which are a multiple of 4 + int width_rem = m_width % 4; + int height_rem = m_height % 4; + + if (width_rem != 0) + { + m_width += (4 - width_rem); + 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(); + return false; + } + + // Get RGB & alpha raw data from wxImage, pack them into an array. + unsigned char* img_rgb = image.GetData(); + if (img_rgb == nullptr) + { + reset(); + return false; + } + + unsigned char* img_alpha = image.GetAlpha(); + + std::vector data(n_pixels * 4, 0); + 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; + } + + // sends data to gpu + glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + glsafe(::glGenTextures(1, &m_id)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_id)); + + if (apply_anisotropy) + { + GLfloat max_anisotropy = GLCanvas3DManager::get_gl_info().get_max_anisotropy(); + if (max_anisotropy > 1.0f) + glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy)); + } + + if (compression_enabled) + { + // initializes the texture on GPU + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0)); + // and send the uncompressed data to the compressor + m_compressor.add_level((unsigned int)m_width, (unsigned int)m_height, data); + } + else + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data())); + + if (use_mipmaps) + { + // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards + int lod_w = m_width; + int lod_h = m_height; + GLint level = 0; + // 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) + { + // 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) + // start asynchronous compression + m_compressor.start_compressing(); + + return true; +} + bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px) { bool compression_enabled = compress && GLEW_EXT_texture_compression_s3tc; @@ -579,6 +758,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)); diff --git a/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp index f4dc05a16..21e01733d 100644 --- a/src/slic3r/GUI/GLTexture.hpp +++ b/src/slic3r/GUI/GLTexture.hpp @@ -76,6 +76,7 @@ namespace GUI { 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, bool compress, bool apply_anisotropy, unsigned int max_size_px); 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) // first field (int): @@ -106,6 +107,7 @@ namespace GUI { 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, bool compress, bool apply_anisotropy, unsigned int max_size_px); bool load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px); friend class Compressor;