From 542e5a9c1990cc22eee14b9846eda16f61a994ac Mon Sep 17 00:00:00 2001 From: omigamedev Date: Thu, 15 Aug 2019 19:12:50 +0200 Subject: [PATCH] refactor export equirectangular --- src/canvas.cpp | 217 +++++--------------------------------- src/canvas.h | 2 +- src/canvas_layer.cpp | 66 ++++++++++++ src/canvas_layer.h | 3 + src/image.cpp | 12 ++- src/image.h | 7 +- src/node_panel_brush.cpp | 4 +- src/node_panel_stroke.cpp | 8 +- src/texture.cpp | 81 ++++++++++++++ src/texture.h | 21 ++++ src/util.cpp | 6 +- src/util.h | 2 +- 12 files changed, 223 insertions(+), 206 deletions(-) diff --git a/src/canvas.cpp b/src/canvas.cpp index 32b776e..9beb7d2 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -76,7 +76,7 @@ void Canvas::pick_update(int plane) { std::array faces{ false }; faces[plane] = true; - draw_merge(faces); + draw_merge(true, faces); int i = plane; m_layers_merge.m_rtt[i].bindFramebuffer(); @@ -915,8 +915,10 @@ void Canvas::stroke_commit() ActionManager::add(action); } -void Canvas::draw_merge(std::array faces /*= SIXPLETTE(false)*/) +void Canvas::draw_merge(bool draw_checkerboard, std::array faces /*= SIXPLETTE(false)*/) { + assert(App::I->is_render_thread()); + glViewport(0, 0, m_width, m_height); auto ortho = glm::ortho(-0.5f, 0.5f, -0.5f, 0.5f, -1.f, 1.f); const auto& b = m_current_stroke->m_brush; @@ -939,6 +941,7 @@ void Canvas::draw_merge(std::array faces /*= SIXPLETTE(false)*/) continue; m_layers_merge.m_rtt[plane_index].bindFramebuffer(); + m_layers_merge.m_rtt[plane_index].clear({ 1, 1, 1, 0 }); if (use_blend) { @@ -947,10 +950,13 @@ void Canvas::draw_merge(std::array faces /*= SIXPLETTE(false)*/) } else { - ShaderManager::use(kShader::Checkerboard); - ShaderManager::u_int(kShaderUniform::Colorize, false); - ShaderManager::u_mat4(kShaderUniform::MVP, ortho); - m_plane.draw_fill(); + if (draw_checkerboard) + { + ShaderManager::use(kShader::Checkerboard); + ShaderManager::u_int(kShaderUniform::Colorize, false); + ShaderManager::u_mat4(kShaderUniform::MVP, ortho); + m_plane.draw_fill(); + } glEnable(GL_BLEND); } @@ -1125,10 +1131,13 @@ void Canvas::draw_merge(std::array faces /*= SIXPLETTE(false)*/) glEnable(GL_BLEND); //draw the grid - ShaderManager::use(kShader::Checkerboard); - ShaderManager::u_int(kShaderUniform::Colorize, false); - ShaderManager::u_mat4(kShaderUniform::MVP, ortho); - m_plane.draw_fill(); + if (draw_checkerboard) + { + ShaderManager::use(kShader::Checkerboard); + ShaderManager::u_int(kShaderUniform::Colorize, false); + ShaderManager::u_mat4(kShaderUniform::MVP, ortho); + m_plane.draw_fill(); + } // draw the layers m_sampler.bind(0); @@ -1720,203 +1729,29 @@ void Canvas::export_equirectangular(std::string file_path, std::function void Canvas::export_equirectangular_thread(std::string file_path) { - std::shared_ptr pb; - if (App::I->layout.m_loaded) - { - pb = std::make_shared(); - pb->m_manager = &App::I->layout; - pb->init(); - pb->create(); - pb->loaded(); - pb->m_progress->SetWidthP(0); - pb->m_title->set_text("Export Pano Image"); - App::I->layout[App::I->main_id]->add_child(pb); - } - - RTT m_latlong; - m_latlong.create(m_width * 4, m_height * 2); // NOTE: w and h must be equal to make sense - - GLuint cube_id; - static int faces[]{ - GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, // front - GL_TEXTURE_CUBE_MAP_NEGATIVE_X, // right - GL_TEXTURE_CUBE_MAP_POSITIVE_Z, // back - GL_TEXTURE_CUBE_MAP_POSITIVE_X, // left - GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, // top - GL_TEXTURE_CUBE_MAP_POSITIVE_Y, // bottom - }; - - App::I->render_task([&] - { - glGenTextures(1, &cube_id); - glBindTexture(GL_TEXTURE_CUBE_MAP, cube_id); - for (GLuint i = 0; i < 6; i++) - { - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA8, - m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - } - }); - - int progress = 0; - int total = 6 + 2; - - Texture2D face; - face.create(m_width, m_height); - - for (int i = 0; i < 6; i++) - { - App::I->render_task([&] - { - // prepare common states - glViewport(0, 0, m_width, m_height); - glDisable(GL_BLEND); - - ShaderManager::use(kShader::TextureBlend); - ShaderManager::u_int(kShaderUniform::Tex, 0); - ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); - - m_tmp[i].bindFramebuffer(); - // clear transparent not to mess with blending modes - m_tmp[i].clear({ 1, 1, 1, 0 }); - - if (!ShaderManager::ext_framebuffer_fetch) - { - ShaderManager::u_int(kShaderUniform::TexBG, 2); - glActiveTexture(GL_TEXTURE2); - face.bind(); - m_sampler_nearest.bind(2); - } - m_sampler_nearest.bind(0); // nearest - for (int layer_index = 0; layer_index < m_layers.size(); layer_index++) - { - if (!m_layers[layer_index]->m_visible || - m_layers[layer_index]->m_opacity == 0.f || - !m_layers[layer_index]->m_dirty_face[i]) - continue; - if (!ShaderManager::ext_framebuffer_fetch) - { - glActiveTexture(GL_TEXTURE2); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height); - } - ShaderManager::u_int(kShaderUniform::BlendMode, m_layers[layer_index]->m_blend_mode); - ShaderManager::u_float(kShaderUniform::Alpha, m_layers[layer_index]->m_opacity); - glActiveTexture(GL_TEXTURE0); - m_layers[layer_index]->m_rtt[i].bindTexture(); - m_plane.draw_fill(); - m_layers[layer_index]->m_rtt[i].unbindTexture(); - } - - if (!ShaderManager::ext_framebuffer_fetch) - { - glActiveTexture(GL_TEXTURE2); - face.unbind(); - } - - // now blend with the background - glEnable(GL_BLEND); - ShaderManager::use(kShader::Texture); - ShaderManager::u_int(kShaderUniform::Tex, 0); - ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); - m_sampler.bind(0); // linear - glActiveTexture(GL_TEXTURE0); - face.bind(); - // copy the framebuffer before clearing to white - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height); - m_tmp[i].clear({ 1, 1, 1, 0 }); - m_plane.draw_fill(); - face.unbind(); - - // copy result to cubemap - glBindTexture(GL_TEXTURE_CUBE_MAP, cube_id); - glCopyTexImage2D(faces[i], 0, GL_RGBA8, 0, 0, m_width, m_height, 0); - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - - m_tmp[i].unbindFramebuffer(); - }); - - progress++; - float p = (float)progress / total * 100.f; - LOG("progress: %f", p); - - if (App::I->layout.m_loaded) - { - pb->m_progress->SetWidthP(p); - } - } - - face.destroy(); - - //auto data = std::make_unique(m_tmp[0].bytes()); - //for (int i = 0; i < 1; i++) - //{ - // m_tmp[i].readTextureData(data.get()); - // static char name[128]; - // sprintf(name, "%s/Face%d.png", data_path.c_str(), i); - // LOG("writing %s", name); - // int ret = stbi_write_png(name, m_tmp[i].getWidth(), m_tmp[i].getHeight(), 4, data.get(), m_tmp[i].stride()); - //} + Image data; App::I->render_task([&] { - glDisable(GL_BLEND); - glViewport(0, 0, m_latlong.getWidth(), m_latlong.getHeight()); - glActiveTexture(GL_TEXTURE0); - m_latlong.bindFramebuffer(); - ShaderManager::use(kShader::Equirect); - ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); - ShaderManager::u_int(kShaderUniform::Tex, 0); - glBindTexture(GL_TEXTURE_CUBE_MAP, cube_id); - m_sampler.bind(0); - m_plane.draw_fill(); - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - m_latlong.unbindFramebuffer(); + draw_merge(false); + Texture2D equirect = m_layers_merge.gen_equirect(); + data = equirect.get_image(); }); - auto latlong_data = std::make_unique(m_latlong.bytes()); - m_latlong.readTextureData(latlong_data.get()); - - progress++; - LOG("progress: %f", (float)progress / total * 100.f); - - if (App::I->layout.m_loaded) - { - pb->m_progress->SetWidthP((float)progress / total * 100.f); - } - LOG("writing %s", file_path.c_str()); if (file_path.substr(file_path.size() - 4) == ".jpg") { - stbi_write_jpg(file_path.c_str(), m_latlong.getWidth(), m_latlong.getHeight(), 4, latlong_data.get(), 100); + data.save_jpg(file_path, 100); inject_xmp(file_path); } else if (file_path.substr(file_path.size() - 4) == ".png") { - stbi_write_png(file_path.c_str(), m_latlong.getWidth(), m_latlong.getHeight(), 4, latlong_data.get(), 0); + data.save_png(file_path); } - progress++; - LOG("progress: %f", (float)progress / total * 100.f); - - if (App::I->layout.m_loaded) - { - pb->m_progress->SetWidthP((float)progress / total * 100.f); - } - - //int ret = stbi_write_png(name, m_latlong.getWidth(), m_latlong.getHeight(), 4, latlong_data.get(), m_latlong.stride()); #ifdef __IOS__ save_image_library(file_path); #endif - - App::I->render_task_async([id=cube_id] - { - glDeleteTextures(1, &id); - }); - m_latlong.destroy(); - - if (App::I->layout.m_loaded) - { - pb->destroy(); - } } void Canvas::inject_xmp(std::string jpg_path) @@ -1990,7 +1825,7 @@ void Canvas::export_depth_thread(std::string file_name) App::I->render_task([&] { - draw_merge(); + draw_merge(false); rtt.bindFramebuffer(); rtt.clear({ 0, 0, 0, 1 }); diff --git a/src/canvas.h b/src/canvas.h index e7953ff..2fd7ba3 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -188,7 +188,7 @@ public: void stroke_end(); void stroke_cancel(); void stroke_commit(); - void draw_merge(std::array faces = SIXPLETTE(true)); + void draw_merge(bool draw_checkerboard, std::array faces = SIXPLETTE(true)); void clear(const glm::vec4& color = { 1, 1, 1, 0 }); void clear_all(); void pick_start(); diff --git a/src/canvas_layer.cpp b/src/canvas_layer.cpp index d88a5ce..844eeb3 100644 --- a/src/canvas_layer.cpp +++ b/src/canvas_layer.cpp @@ -1,8 +1,74 @@ #include "pch.h" #include "canvas_layer.h" +#include "app.h" +#include "rtt.h" uint32_t Layer::s_count = 0; +TextureCube Layer::gen_cube() +{ + TextureCube ret; + App::I->render_task([&] + { + ret.create(w); + ret.bind(); + for (int i = 0; i < 6; i++) + { + m_rtt[i].bindFramebuffer(); + glCopyTexSubImage2D(TextureCube::m_faces_map[i], 0, 0, 0, 0, 0, w, w); + m_rtt[i].unbindFramebuffer(); + } + }); + return ret; +} + +Texture2D Layer::gen_equirect() +{ + Texture2D ret; + + App::I->render_task([&] + { + gl_state gl; + gl.save(); + + TextureCube cube; + RTT latlong; + + cube = gen_cube(); + latlong.create(w * 4, h * 2); + ret.create(w * 4, h * 2); + + glDisable(GL_BLEND); + + latlong.bindFramebuffer(); + + latlong.clear({ 0, 1, 1, 1 }); + + glViewport(0, 0, latlong.getWidth(), latlong.getHeight()); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, cube.m_cubetex_id); + + ShaderManager::use(kShader::Equirect); + ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); + ShaderManager::u_int(kShaderUniform::Tex, 0); + Canvas::I->m_sampler.bind(0); + Canvas::I->m_plane.draw_fill(); + + ret.bind(); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, latlong.getWidth(), latlong.getHeight()); + + latlong.unbindFramebuffer(); + + latlong.destroy(); + cube.destroy(); + + gl.restore(); + }); + + return ret; +} + void Layer::Snapshot::create(int w, int h) { width = w; diff --git a/src/canvas_layer.h b/src/canvas_layer.h index 213d69d..2451c87 100644 --- a/src/canvas_layer.h +++ b/src/canvas_layer.h @@ -1,6 +1,7 @@ #pragma once #include "rtt.h" #include "log.h" +#include "texture.h" class LayerFrame { @@ -42,6 +43,8 @@ public: bool create(int width, int height, std::string name); void clear(const glm::vec4& c); Snapshot snapshot(std::array* dirty_box = nullptr, std::array* dirty_face = nullptr); + TextureCube gen_cube(); + Texture2D gen_equirect(); void restore(const Snapshot& snap); void destroy(); void optimize(); diff --git a/src/image.cpp b/src/image.cpp index f098573..affd75a 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -39,11 +39,19 @@ bool Image::load_file(std::string filename) return true; } -bool Image::save(const std::string& path) +bool Image::save_png(const std::string& path) { bool ret = stbi_write_png(path.c_str(), width, height, comp, data(), 0); if (!ret) - LOG("failed Image::save %s", path.c_str()); + LOG("failed Image::save_png %s", path.c_str()); + return ret; +} + +bool Image::save_jpg(const std::string& path, int quality) +{ + bool ret = stbi_write_jpg(path.c_str(), width, height, comp, data(), quality); + if (!ret) + LOG("failed Image::save_jpg %s", path.c_str()); return ret; } diff --git a/src/image.h b/src/image.h index 479f85f..fe536d2 100644 --- a/src/image.h +++ b/src/image.h @@ -11,13 +11,14 @@ public: bool load_file(std::string filename); const uint8_t* data() const { return m_data.get(); } int size() const { return width * height * comp; } - bool save(const std::string& path); - void create(int w, int h) + bool save_png(const std::string& path); + bool save_jpg(const std::string& path, int quality); + void create(int w, int h, uint8_t* data = nullptr) { width = w; height = h; comp = 4; - m_data = std::make_unique(size()); + m_data = data ? std::unique_ptr(data) : std::make_unique(size()); } void copy_from(const uint8_t* data) { diff --git a/src/node_panel_brush.cpp b/src/node_panel_brush.cpp index f1d633a..ea5f4eb 100644 --- a/src/node_panel_brush.cpp +++ b/src/node_panel_brush.cpp @@ -98,9 +98,9 @@ void NodePanelBrush::init() img.gayscale_alpha(); auto thumb = img.resize(64, 64).resize_squared(glm::u8vec4(255)); - thumb.save(path_thumb); + thumb.save_png(path_thumb); //auto po2 = img.resize_power2(); - img.save(path_high); + img.save_png(path_high); NodeButtonBrush* brush = new NodeButtonBrush; m_container->add_child(brush); diff --git a/src/node_panel_stroke.cpp b/src/node_panel_stroke.cpp index 7758ca9..4f14009 100644 --- a/src/node_panel_stroke.cpp +++ b/src/node_panel_stroke.cpp @@ -60,9 +60,9 @@ bool NodePanelStroke::import_abr(const std::string& path) auto padded = samp.second->resize_squared(glm::u8vec4(255)); //auto high = padded.resize_power2(); //high.save(path_high); - samp.second->save(path_high); + samp.second->save_png(path_high); auto thumb = padded.resize(64, 64); - thumb.save(path_thumb); + thumb.save_png(path_thumb); NodeButtonBrush* brush = new NodeButtonBrush; m_brush_popup->m_container->add_child(brush); @@ -89,9 +89,9 @@ bool NodePanelStroke::import_abr(const std::string& path) const auto& patt = *ii; std::string path_high = App::I->data_path + "/patterns/" + patt.first + ".png"; std::string path_thumb = App::I->data_path + "/patterns/thumbs/" + patt.first + ".png"; - patt.second->save(path_high); + patt.second->save_png(path_high); auto thumb = patt.second->resize(64, 64); - thumb.save(path_thumb); + thumb.save_png(path_thumb); NodeButtonBrush* brush = new NodeButtonBrush; m_pattern_popup->m_container->add_child(brush); diff --git a/src/texture.cpp b/src/texture.cpp index f9719a8..928ad15 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -5,6 +5,75 @@ #include "app.h" std::map TextureManager::m_textures; +std::array TextureCube::m_faces_map { + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, // front + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, // right + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, // back + GL_TEXTURE_CUBE_MAP_POSITIVE_X, // left + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, // top + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, // bottom +}; + +TextureCube::TextureCube(TextureCube&& other) noexcept +{ + other.m_faces = std::move(other.m_faces); + m_cubetex_id = other.m_cubetex_id; other.m_cubetex_id = 0; + m_resolution = other.m_resolution; other.m_resolution = 0; +} + +TextureCube::~TextureCube() +{ + destroy(); +} + +void TextureCube::operator=(TextureCube&& other) noexcept +{ + other.m_faces = std::move(other.m_faces); + m_cubetex_id = other.m_cubetex_id; other.m_cubetex_id = 0; + m_resolution = other.m_resolution; other.m_resolution = 0; +} + +bool TextureCube::create(int resolution) noexcept +{ + App::I->render_task([this, resolution] + { + destroy(); + m_resolution = resolution; + glGenTextures(1, &m_cubetex_id); + + if (!m_cubetex_id) + return; + + glBindTexture(GL_TEXTURE_CUBE_MAP, m_cubetex_id); + for (GLuint i = 0; i < 6; i++) + { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA8, + m_resolution, m_resolution, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + } + }); + return m_cubetex_id != 0; +} + +void TextureCube::destroy() noexcept +{ + if (m_cubetex_id) + { + App::I->render_task([f=m_faces, id=m_cubetex_id] + { + glDeleteTextures(f.size(), f.data()); + glDeleteTextures(1, &id); + }); + m_cubetex_id = 0; + m_faces.fill(0); + m_resolution = 0; + } +} + +void TextureCube::bind() const noexcept +{ + assert(App::I->is_render_thread()); + glBindTexture(GL_TEXTURE_CUBE_MAP, m_cubetex_id); +} bool TextureManager::load(const char* path, bool generate_mipmaps) { @@ -39,6 +108,18 @@ void TextureManager::invalidate() m_textures.clear(); } +Image Texture2D::get_image() const noexcept +{ + Image ret; + ret.create(m_width, m_height); + App::I->render_task([&] + { + bind(); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, ret.m_data.get()); + }); + return ret; +} + bool Texture2D::create(int width, int height, GLint internal_format, GLint format, const uint8_t* data) { App::I->render_task([=] diff --git a/src/texture.h b/src/texture.h index 5414f19..1cd0362 100644 --- a/src/texture.h +++ b/src/texture.h @@ -23,9 +23,30 @@ public: bool ready() const { return m_tex != 0; } void create_mipmaps(); glm::vec2 size() const; + Image get_image() const noexcept; ~Texture2D(); }; +struct TextureCube +{ + TextureCube() noexcept = default; + TextureCube(const TextureCube&) = delete; + void operator=(const TextureCube&) = delete; + TextureCube(TextureCube&& other) noexcept; + void operator=(TextureCube&& other) noexcept; + ~TextureCube() noexcept; + + static std::array m_faces_map; + + GLuint m_cubetex_id; + std::array m_faces{ 0 }; + int m_resolution = 0; + + bool create(int resolution) noexcept; + void destroy() noexcept; + void bind() const noexcept; +}; + class Sampler { GLuint id = 0; diff --git a/src/util.cpp b/src/util.cpp index f8b5cc3..ace842c 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -747,7 +747,8 @@ void gl_state::save() glGetIntegerv(GL_VIEWPORT, vp); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); glGetIntegerv(GL_CURRENT_PROGRAM, &program); - glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fb); + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fbd); + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &fbr); glGetIntegerv(GL_ACTIVE_TEXTURE, &active_tex); glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &cube); for (int i = 0; i < 10; ++i) @@ -766,7 +767,8 @@ void gl_state::restore() scissor_test ? glEnable(GL_SCISSOR_TEST) : glDisable(GL_SCISSOR_TEST); glViewport(vp[0], vp[1], vp[2], vp[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]); - glBindFramebuffer(GL_FRAMEBUFFER, fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbd); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbr); glUseProgram(program); for (int i = 0; i < 10; ++i) { diff --git a/src/util.h b/src/util.h index 0bd0ace..1e64509 100644 --- a/src/util.h +++ b/src/util.h @@ -261,7 +261,7 @@ struct gl_state GLint cube; GLint sampler[10]; GLint program; - GLint fb; + GLint fbr, fbd; GLint active_tex; GLfloat line_width; void save();