#include "pch.h" #include "log.h" #include "canvas.h" ui::Canvas* ui::Canvas::I; glm::vec3 ui::Canvas::m_plane_origin[6] = { { 0, 0,-1}, // front { 1, 0, 0}, // right { 0, 0, 1}, // back {-1, 0, 0}, // left { 0, 1, 0}, // top { 0,-1, 0}, // bottom }; glm::vec3 ui::Canvas::m_plane_normal[6] = { { 0, 0, 1}, // front {-1, 0, 0}, // right { 0, 0,-1}, // back { 1, 0, 0}, // left { 0,-1, 0}, // top { 0, 1, 0}, // bottom }; glm::vec3 ui::Canvas::m_plane_tangent[6] = { {0, 1, 0}, // front {0, 1, 0}, // right {0, 1, 0}, // back {0, 1, 0}, // left {0, 0,-1}, // top {0, 0, 1}, // bottom }; glm::mat4 ui::Canvas::m_plane_transform[6] = { glm::lookAt(glm::vec3(), { 0, 0,-1}, {0, 1, 0}), // front glm::lookAt(glm::vec3(), {-1, 0, 0}, {0, 1, 0}), // right glm::lookAt(glm::vec3(), { 0, 0, 1}, {0, 1, 0}), // back glm::lookAt(glm::vec3(), { 1, 0, 0}, {0, 1, 0}), // left glm::lookAt(glm::vec3(), { 0, 1, 0}, {0, 0,-1}), // top glm::lookAt(glm::vec3(), { 0,-1, 0}, {0, 0, 1}), // bottom }; void ui::Canvas::clear(const glm::vec4& c/*={0,0,0,1}*/) { m_layers[m_current_layer_idx].clear(c); } void ui::Canvas::stroke_end() { stroke_commit(); m_current_stroke = nullptr; m_show_tmp = false; } void ui::Canvas::stroke_draw() { if (!(m_current_stroke && m_current_stroke->has_sample())) return; m_dirty = true; GLint vp[4]; GLfloat cc[4]; glGetIntegerv(GL_VIEWPORT, vp); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); glViewport(0, 0, m_width, m_height); auto ortho_proj = glm::ortho(0.f, (float)m_width, 0.f, (float)m_height, -1.f, 1.f); auto m_brush = m_current_stroke->m_brush; auto samples = m_current_stroke->compute_samples(); auto& tex = TextureManager::get(m_brush.m_tex_id); tex.bind(); m_sampler.bind(0); m_sampler_bg.bind(1); m_sampler_mask.bind(2); for (int i = 0; i < 6; i++) { m_tmp[i].bindFramebuffer(); glActiveTexture(GL_TEXTURE1); m_tex[i].bind(); // bg, copy of framebuffer (copied before drawing) if (m_use_instanced) { glEnable(GL_BLEND); m_mesh.shader.use(); m_mesh.shader.u_vec4(kShaderUniform::Col, m_brush.m_tip_color); m_mesh.shader.u_int(kShaderUniform::Tex, 0); m_mesh.draw(samples, ortho_proj); } else { glDisable(GL_BLEND); if (m_erase) { ShaderManager::use(ui::kShader::StrokeErase); //ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); } else if(m_layers[m_current_layer_idx].m_alpha_locked) { ShaderManager::use(kShader::StrokeLock); ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); ShaderManager::u_int(kShaderUniform::TexMask, 2); // alpha mask glActiveTexture(GL_TEXTURE2); m_layers[m_current_layer_idx].m_rtt[i].bindTexture(); glActiveTexture(GL_TEXTURE1); } else { ShaderManager::use(ui::kShader::Stroke); ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); } ShaderManager::u_int(kShaderUniform::Tex, 0); // brush ShaderManager::u_int(kShaderUniform::TexBG, 1); // bg ShaderManager::u_vec2(kShaderUniform::Resolution, { m_width, m_height }); for (const auto& s : samples) { auto unproject = [](glm::vec2 loc, glm::vec4 vp, glm::mat4 camera, glm::mat4 proj, glm::vec3& out_origin, glm::vec3& out_dir) { auto clip_space = glm::vec2(loc.x, vp.w - loc.y - 1.f) / vp.zw() * 2.f - 1.f; auto inv = glm::inverse(proj * camera); auto wp0 = inv * glm::vec4(clip_space, 0, 1); auto wp1 = inv * glm::vec4(clip_space, .5, 1); out_origin = (wp0 / wp0.w).xyz(); out_dir = glm::normalize((wp1 / wp1.w).xyz() - out_origin); }; auto intersect = [](glm::vec3 ray_origin, glm::vec3 ray_dir, glm::vec3 plane_origin, glm::vec3 plane_normal, glm::vec3 plane_tangent, glm::vec3& out_hit) { float den = glm::dot(ray_dir, plane_normal); if (den == 0) return false; // no intersection float num = glm::dot(plane_origin - ray_origin, plane_normal); float t = num / den; if (t > 0) out_hit = ray_origin + ray_dir * t; else // negative intersection return false; return true; }; glm::vec3 ray_origin, ray_dir; unproject(s.pos, { 0, 0, m_box.zw }, m_mv, m_proj, ray_origin, ray_dir); glm::vec3 hit; glm::vec2 fb_pos; if (intersect(ray_origin, ray_dir, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit)) { glm::mat4 plane_camera = glm::lookAt(m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i]); glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1); if (glm::abs(plane_local.x) < 1.f && glm::abs(plane_local.y) < 1.f) { fb_pos.x = -(plane_local.x * 0.5f - 0.5f) * m_width; fb_pos.y = (plane_local.y * 0.5f + 0.5f) * m_height; //LOG("draw %f %f", fb_pos.x, fb_pos.y); } else { continue; } } else { continue; } m_dirty_face[i] = true; auto mvp = ortho_proj * glm::translate(glm::vec3(fb_pos, 0)) * glm::scale(glm::vec3(s.size, s.size, 1)) * glm::eulerAngleZ(s.angle); glm::vec4 P[4] { mvp * glm::vec4(glm::vec2(-.5f, -.5f), 0, 1.f), // A - bottom-left mvp * glm::vec4(glm::vec2(-.5f, +.5f), 0, 1.f), // B - top-left mvp * glm::vec4(glm::vec2(+.5f, +.5f), 0, 1.f), // C - top-right mvp * glm::vec4(glm::vec2(+.5f, -.5f), 0, 1.f), // D - bottom-right }; auto mvp_inv = glm::inverse(ortho_proj); glm::vec4 P2[4]{ mvp_inv * P[0], mvp_inv * P[1], mvp_inv * P[2], mvp_inv * P[3], }; glm::vec2 bb_min(m_width, m_height); glm::vec2 bb_max(0, 0); for (int i = 0; i < 4; i++) { bb_min = glm::max({ 0, 0 }, glm::min(bb_min, P2[i].xy())); bb_max = glm::min({ m_width, m_height }, glm::max(bb_max, P2[i].xy())); } auto bb_sz = bb_max - bb_min; glm::vec2 pad(1); glm::ivec2 tex_pos = glm::clamp(glm::floor(bb_min) - pad , { 0, 0 }, { m_width, m_height }); glm::ivec2 tex_sz = glm::clamp(glm::ceil(bb_sz ) + pad*2.f, { 0, 0 }, (glm::vec2)(glm::ivec2(m_width, m_height) - tex_pos)); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, tex_pos.x, tex_pos.y, tex_pos.x, tex_pos.y, tex_sz.x, tex_sz.y); m_dirty_box[i].xy = glm::min(m_dirty_box[i].xy(), (glm::vec2)tex_pos); m_dirty_box[i].zw = glm::max(m_dirty_box[i].zw(), (glm::vec2)(tex_pos + tex_sz)); ShaderManager::u_mat4(kShaderUniform::MVP, mvp); ShaderManager::u_float(kShaderUniform::Alpha, s.flow); //m_plane_brush.update_vertices(P); m_plane_brush.draw_fill(); } } if (m_alpha_lock) { glActiveTexture(GL_TEXTURE2); m_layers[m_current_layer_idx].m_rtt[i].unbindTexture(); } glActiveTexture(GL_TEXTURE1); m_tex[i].unbind(); m_tmp[i].unbindFramebuffer(); } glDisable(GL_BLEND); glActiveTexture(GL_TEXTURE0); m_sampler.unbind(); m_sampler_bg.unbind(); m_sampler_mask.unbind(); tex.unbind(); glViewport(vp[0], vp[1], vp[2], vp[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]); } void ui::Canvas::stroke_commit() { if (!m_dirty || m_layers.empty()) return; m_dirty = false; // save viewport and clear color states GLint vp[4]; GLfloat cc[4]; glGetIntegerv(GL_VIEWPORT, vp); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); GLboolean blend = glIsEnabled(GL_BLEND); // allocate action to add to history auto action = new ActionStroke; // prepare common states glViewport(0, 0, m_width, m_height); glDisable(GL_BLEND); for (int i = 0; i < 6; i++) { if (!m_dirty_face[i]) continue; // no stroke on this face, skip it m_layers[m_current_layer_idx].m_rtt[i].bindFramebuffer(); // save image before commit glm::vec2 box_sz = m_dirty_box[i].zw() - m_dirty_box[i].xy(); action->m_image[i] = std::make_unique(box_sz.x * box_sz.y * 4); glReadPixels(m_dirty_box[i].x, m_dirty_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, action->m_image[i].get()); action->m_box[i] = m_dirty_box[i]; // copy to tmp2 for layer blending glActiveTexture(GL_TEXTURE0); m_tex2[i].bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height); m_tex2[i].unbind(); m_tmp[i].bindTexture(); glActiveTexture(GL_TEXTURE1); m_tex2[i].bind(); m_sampler.bind(0); m_sampler_bg.bind(1); if (m_erase) { ShaderManager::use(ui::kShader::Texture); } else { ShaderManager::use(ui::kShader::StrokeLayer); ShaderManager::u_int(kShaderUniform::TexBG, 1); ShaderManager::u_float(kShaderUniform::Alpha, m_current_stroke->m_brush.m_tip_opacity); } ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); m_plane.draw_fill(); m_sampler.unbind(); m_sampler_bg.unbind(); m_tex2[i].unbind(); m_tmp[i].unbindTexture(); m_layers[m_current_layer_idx].m_rtt[i].unbindFramebuffer(); } // restore viewport and clear color states blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); glViewport(vp[0], vp[1], vp[2], vp[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]); glActiveTexture(GL_TEXTURE0); // save history action->m_layer_idx = m_current_layer_idx; action->m_canvas = this; action->m_stroke = std::move(m_current_stroke); ActionManager::add(action); } void ui::Canvas::stroke_update(glm::vec2 point, float pressure) { m_current_stroke->add_point(point, pressure); } void ui::Canvas::stroke_start(glm::vec2 point, float pressure, const ui::Brush& brush) { m_current_stroke = std::make_unique(); m_current_stroke->m_camera = { m_cam_rot, m_cam_fov }; m_current_stroke->start(brush); m_current_stroke->add_point(point, pressure); for (int i = 0; i < 6; i++) { m_dirty_box[i] = glm::vec4(m_width, m_height, 0, 0); // reset bounding box m_dirty_face[i] = false; if (m_erase) { m_layers[m_current_layer_idx].m_rtt[i].bindFramebuffer(); m_tmp[i].bindTexture(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height); m_tmp[i].unbindTexture(); m_layers[m_current_layer_idx].m_rtt[i].unbindFramebuffer(); } else { m_tmp[i].bindFramebuffer(); m_tmp[i].clear({ 0, 0, 0, 0 }); m_tmp[i].unbindFramebuffer(); } } m_show_tmp = true; } void ui::Canvas::layer_add(std::string name) { int idx = (int)m_layers.size(); m_layers.emplace_back(); m_layers.back().create(m_width, m_height, name); m_order.push_back(idx); } void ui::Canvas::layer_order(int idx, int pos) { std::swap(m_order[idx], m_order[pos]); } void ui::Canvas::resize(int width, int height) { m_width = width; m_height = height; for (int i = 0; i < 6; i++) { m_tmp[i].create(width, height); m_tex[i].create(width, height); m_tex2[i].create(width, height); } for (auto& l : m_layers) { l.create(width, height, ""); } m_latlong.create(width * 4, height * 2); // NOTE: w and h must be equal to make sense } bool ui::Canvas::create(int width, int height) { m_width = width; m_height = height; for (int i = 0; i < 6; i++) { m_tmp[i].create(width, height); m_tex[i].create(width, height); m_tex2[i].create(width, height); // TODO: destroy before recreating } m_sampler.create(); m_sampler_bg.create(); m_sampler_mask.create(); m_plane.create<1>(1, 1); m_plane_brush.create<1>(1, 1); m_mesh.create(); for (auto& l : m_layers) { l.create(width, height, ""); } m_latlong.create(width * 4, height * 2); // NOTE: w and h must be equal to make sense return true; } void ui::Canvas::snapshot_save(std::string data_path) { LOG("SAVE SNAPSHOT"); m_layers_snapshot.clear(); m_layers_snapshot.resize(m_layers.size()); for (int i = 0; i < m_layers.size(); i++) m_layers_snapshot[i] = m_layers[i].snapshot(data_path); } void ui::Canvas::snapshot_restore() { LOG("RESTORE SNAPSHOT"); for (int i = 0; i < m_layers.size(); i++) m_layers[i].restore(m_layers_snapshot[i]); m_layers_snapshot.clear(); LOG("RESTORE SNAPSHOT complete"); } void ui::Canvas::clear_context() { LOG("Canvas CLEAR CONTEXT"); for (auto& layer : m_layers) layer.destroy(); for (int i = 0; i < 6; i++) { m_tmp[i].destroy(); m_tex[i].destroy(); m_tex2[i].destroy(); } m_latlong.destroy(); }; void ui::Canvas::save(std::string data_path) { // save viewport and clear color states GLint vp[4]; GLfloat cc[4]; glGetIntegerv(GL_VIEWPORT, vp); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); GLboolean blend = glIsEnabled(GL_BLEND); // prepare common states glViewport(0, 0, m_width, m_height); glDisable(GL_BLEND); 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 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 }; for (int i = 0; i < 6; i++) { m_tmp[i].bindFramebuffer(); m_tmp[i].clear({ 1, 1, 1, 1 }); for (auto layer_index : m_order) { // copy to tmp2 for layer blending glActiveTexture(GL_TEXTURE0); // TODO: maybe remove this line m_tex2[i].bind(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height); m_tex2[i].unbind(); m_layers[layer_index].m_rtt[i].bindTexture(); glActiveTexture(GL_TEXTURE1); m_tex2[i].bind(); m_sampler.bind(0); m_sampler_bg.bind(1); ShaderManager::use(ui::kShader::StrokeLayer); ShaderManager::u_int(kShaderUniform::TexBG, 1); ShaderManager::u_float(kShaderUniform::Alpha, m_layers[layer_index].m_opacity); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); m_plane.draw_fill(); m_sampler.unbind(); m_sampler_bg.unbind(); m_tex2[i].unbind(); m_layers[layer_index].m_rtt[i].unbindTexture(); } // 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(); } // auto data = std::make_unique(m_tmp[0].bytes()); // for (int i = 0; i < 6; 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()); // } glViewport(0, 0, m_latlong.getWidth(), m_latlong.getHeight()); m_latlong.bindFramebuffer(); ui::ShaderManager::use(kShader::Equirect); ui::ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); ui::ShaderManager::u_int(kShaderUniform::Tex, 0); glBindTexture(GL_TEXTURE_CUBE_MAP, cube_id); m_sampler.bind(0); m_plane.draw_fill(); m_sampler.unbind(); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); m_latlong.unbindFramebuffer(); { auto latlong_data = std::make_unique(m_latlong.bytes()); m_latlong.readTextureData(latlong_data.get()); static char name[128]; sprintf(name, "%s/latlong.png", data_path.c_str()); LOG("writing %s", name); int ret = stbi_write_png(name, m_latlong.getWidth(), m_latlong.getHeight(), 4, latlong_data.get(), m_latlong.stride()); } // restore viewport and clear color states blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); glViewport(vp[0], vp[1], vp[2], vp[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]); glActiveTexture(GL_TEXTURE0); } void ui::Canvas::project_save(std::string data_path) { static char name[128]; sprintf(name, "%s/latlong.pano", data_path.c_str()); FILE* fp = fopen(name, "wb"); if (!fp) { LOG("cannot write project to %s", name); return; } // load thumbnail Image thumb = thumbnail_generate(64, 64); fwrite(&thumb.width, sizeof(int), 1, fp); fwrite(&thumb.height, sizeof(int), 1, fp); fwrite(&thumb.comp, sizeof(int), 1, fp); fwrite(thumb.data(), thumb.size(), 1, fp); fwrite(&m_width, sizeof(int), 1, fp); fwrite(&m_height, sizeof(int), 1, fp); int n_layers = (int)m_layers.size(); fwrite(&n_layers, sizeof(int), 1, fp); for (int i = 0; i < (int)m_layers.size(); i++) { int n_order = m_order[i]; fwrite(&n_order, sizeof(int), 1, fp); int name_len = m_layers[i].m_name.size(); fwrite(&name_len, sizeof(int), 1, fp); fwrite(m_layers[i].m_name.data(), name_len, 1, fp); auto snap = m_layers[i].snapshot(data_path); for (int plane_index = 0; plane_index < 6; plane_index++) { fwrite(snap.image[plane_index].get(), 1, m_width * m_height * 4, fp); } } fclose(fp); LOG("project saved to %s", name); } void ui::Canvas::project_open(std::string data_path) { static char name[128]; sprintf(name, "%s/latlong.pano", data_path.c_str()); FILE* fp = fopen(name, "rb"); if (!fp) { LOG("cannot write project to %s", name); return; } // skip thumbnail Image thumb; fread(&thumb.width, sizeof(int), 1, fp); fread(&thumb.height, sizeof(int), 1, fp); fread(&thumb.comp, sizeof(int), 1, fp); fseek(fp, thumb.size(), SEEK_CUR); fread(&m_width, sizeof(int), 1, fp); fread(&m_height, sizeof(int), 1, fp); int n_layers = (int)m_layers.size(); fread(&n_layers, sizeof(int), 1, fp); const int bytes = m_width * m_height * 4; Layer::Snapshot snap; snap.create(m_width, m_height); m_layers.clear(); m_order.clear(); for (int i = 0; i < n_layers; i++) { int n_order; fread(&n_order, sizeof(int), 1, fp); m_order.push_back(n_order); int name_len; fread(&name_len, sizeof(int), 1, fp); std::string name(name_len, '\0'); fread((char*)name.data(), name_len, 1, fp); for (int plane_index = 0; plane_index < 6; plane_index++) { fread(snap.image[plane_index].get(), 1, bytes, fp); } m_layers.emplace_back(); m_layers.back().create(m_width, m_height, name.c_str()); m_layers.back().restore(snap); } fclose(fp); LOG("project restore from %s", name); } ui::Image ui::Canvas::thumbnail_generate(int w, int h) { // save viewport and clear color states GLint vp[4]; GLfloat cc[4]; glGetIntegerv(GL_VIEWPORT, vp); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); GLboolean blend = glIsEnabled(GL_BLEND); // prepare common states glViewport(0, 0, w, h); RTT fb; fb.create(w, h); fb.bindFramebuffer(); fb.clear({ 1, 1, 1, 1 }); ui::Plane m_face_plane; m_face_plane.create<1>(2, 2); // recalculate because of different aspect ratio than the m_proj matrix glm::mat4 proj = glm::perspective(glm::radians(m_cam_fov), (float)w / (float)h, 0.1f, 1000.f); glEnable(GL_BLEND); for (int plane_index = 0; plane_index < 6; plane_index++) { auto plane_mvp = proj * m_mv * m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); ui::ShaderManager::use(kShader::Checkerboard); ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); m_face_plane.draw_fill(); ui::ShaderManager::use(kShader::TextureAlpha); ui::ShaderManager::u_int(kShaderUniform::Tex, 0); ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); for (auto layer_index : m_order) { ui::ShaderManager::u_float(kShaderUniform::Alpha, m_layers[layer_index].m_opacity); m_layers[layer_index].m_rtt[plane_index].bindTexture(); m_face_plane.draw_fill(); m_layers[layer_index].m_rtt[plane_index].unbindTexture(); } } fb.unbindFramebuffer(); // read the rendered image ui::Image image; image.create(w, h); fb.readTextureData((uint8_t*)image.data()); fb.destroy(); // restore viewport and clear color states blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); glViewport(vp[0], vp[1], vp[2], vp[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]); glActiveTexture(GL_TEXTURE0); return std::move(image); } ui::Image ui::Canvas::thumbnail_read(std::string data_path) { static char name[128]; sprintf(name, "%s/latlong.pano", data_path.c_str()); FILE* fp = fopen(name, "rb"); if (!fp) { LOG("cannot read project %s", name); return {}; // return empty image } Image thumb; fread(&thumb.width, sizeof(int), 1, fp); fread(&thumb.height, sizeof(int), 1, fp); fread(&thumb.comp, sizeof(int), 1, fp); thumb.create(); fread((uint8_t*)thumb.data(), thumb.size(), 1, fp); fclose(fp); LOG("project thumbnail read from %s", name); return std::move(thumb); } /////////////////////////////////////////////////////////////////////////////////////////// void ui::Layer::destroy() { for (int i = 0; i < 6; i++) m_rtt[i].destroy(); } void ui::Layer::restore(const Snapshot& snap) { for (int i = 0; i < 6; i++) { if (!snap.image[i]) continue; m_rtt[i].recreate(); // TODO: this should not be recreated here! Sorry I messed up with this, just quick fix DON'T SHIP!! m_rtt[i].bindTexture(); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_rtt[i].getWidth(), m_rtt[i].getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, snap.image[i].get()); m_rtt[i].unbindTexture(); LOG("restore face %d - %d bytes (%dx%d)", i, m_rtt[i].bytes(), m_rtt[i].getWidth(), m_rtt[i].getHeight()); } } ui::Layer::Snapshot ui::Layer::snapshot(std::string data_path) { Snapshot snap; static int counter = 0; LOG("errno = %d", errno); glBindTexture(GL_TEXTURE_2D, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); for (int i = 0; i < 6; i++) { snap.image[i] = std::make_unique(m_rtt[i].bytes()); m_rtt[i].readTextureData(snap.image[i].get()); LOG("snapshot face %d - %d bytes (%dx%d)", i, m_rtt[i].bytes(), m_rtt[i].getWidth(), m_rtt[i].getHeight()); static char name[128]; sprintf(name, "%s/Layer%d-%d.png", data_path.c_str(), counter, i); //int ret = stbi_write_png(name, m_rtt[i].getWidth(), m_rtt[i].getHeight(), 4, snap.image[i].get(), m_rtt[i].stride()); } counter++; return std::move(snap); } void ui::Layer::clear(const glm::vec4& c) { // push clear color state GLfloat cc[4]; glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); glClearColor(c.r, c.g, c.b, c.a); for (int i = 0; i < 6; i++) { m_rtt[i].bindFramebuffer(); glClear(GL_COLOR_BUFFER_BIT); m_rtt[i].unbindFramebuffer(); } // restore clear color state glClearColor(cc[0], cc[1], cc[2], cc[3]); } bool ui::Layer::create(int width, int height, std::string name) { m_name = name; for (int i = 0; i < 6; i++) { m_rtt[i].create(width, height); m_rtt[i].bindFramebuffer(); m_rtt[i].clear(); m_rtt[i].unbindFramebuffer(); } return true; }