Files
panopainter/src/canvas_layer.cpp

429 lines
11 KiB
C++

#include "pch.h"
#include "canvas_layer.h"
#include "app.h"
#include "rtt.h"
uint32_t Layer::s_count = 0;
RTT& Layer::rtt(int i, int frame /*= -1*/)
{
if (frame == -1)
frame = m_frame_index;
return m_frames[frame].m_rtt[i];
}
glm::vec4& Layer::box(int i, int frame /*= -1*/)
{
if (frame == -1)
frame = m_frame_index;
return m_frames[frame].m_dirty_box[i];
}
bool& Layer::face(int i, int frame /*= -1*/)
{
if (frame == -1)
frame = m_frame_index;
return m_frames[frame].m_dirty_face[i];
}
LayerFrame& Layer::frame()
{
return m_frames[m_frame_index];
}
TextureCube Layer::gen_cube()
{
TextureCube ret;
App::I->render_task([&]
{
ret.create(w);
ret.bind();
for (int i = 0; i < 6; i++)
{
rtt(i).bindFramebuffer();
glCopyTexSubImage2D(TextureCube::m_faces_map[i], 0, 0, 0, 0, 0, w, w);
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::destroy()
{
for (int i = 0; i < 6; i++)
rtt(i).destroy();
}
void Layer::optimize(int frame /*= -1*/)
{
if (frame == -1)
frame = m_frame_index;
int saved_bytes = 0;
for (int i = 0; i < 6; i++)
{
if (!face(i, frame))
continue;
auto data = std::unique_ptr<glm::u8vec4[]>(reinterpret_cast<glm::u8vec4*>(rtt(i, frame).readTextureData()));
glm::ivec2 bbmin(w, h);
glm::ivec2 bbmax(0);
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
if (data[x + y * w].a > 0)
{
bbmin = glm::min(bbmin, { x, y });
bbmax = glm::max(bbmax, { x + 1, y + 1 });
}
}
}
glm::vec2 bbsz = bbmax - bbmin;
glm::vec2 old_size = zw(box(i, frame)) - xy(box(i, frame));
glm::vec2 diff;
if (bbsz.x <= 0 || bbmax.y <= 0)
{
face(i, frame) = false;
box(i, frame) = glm::vec4(0);
diff = old_size;
}
else
{
box(i, frame) = { bbmin, bbmax };
diff = old_size - bbsz;
}
saved_bytes += (int)(diff.x * diff.y * 4);
}
LOG("optimized %d bytes", saved_bytes);
}
void Layer::restore(const Snapshot& snap, int frame /*= -1*/)
{
if (frame == -1)
frame = m_frame_index;
clear({ 0, 0, 0, 0 }, frame);
for (int i = 0; i < 6; i++)
{
if (snap.image[i] == nullptr || snap.m_dirty_face[i] == false || box_area(snap.m_dirty_box[i]) <= 0)
{
box(i, frame) = glm::vec4(snap.width, snap.height, 0, 0);
face(i, frame) = false;
continue;
}
box(i, frame) = snap.m_dirty_box[i];
face(i, frame) = snap.m_dirty_face[i];
// TODO: this should not be recreated here!
// Sorry I messed up with this,
// it's just a quick fix DON'T SHIP!!
//m_rtt[i].recreate();
App::I->render_task_async([this, i, &snap, frame]
{
rtt(i, frame).bindTexture();
glm::vec2 box_sz = zw(box(i, frame)) - xy(box(i, frame));
glTexSubImage2D(GL_TEXTURE_2D, 0,
box(i, frame).x, box(i, frame).y,
box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE,
snap.image[i].get());
rtt(i, frame).unbindTexture();
LOG("restore frame %d face %d - %d bytes (%dx%d)", frame, i,
(int)box_sz.x * (int)box_sz.y * 4, (int)box_sz.x, (int)box_sz.y);
});
}
App::I->render_sync();
}
Layer::Snapshot Layer::snapshot(int frame /*= -1*/, std::array<glm::vec4, 6>* dirty_box /*= nullptr*/, std::array<bool, 6>* dirty_face /*= nullptr*/)
{
if (frame == -1)
frame = m_frame_index;
Snapshot snap;
snap.width = w;
snap.height = h;
for (int i = 0; i < 6; i++)
{
snap.m_dirty_box[i] = dirty_box ? dirty_box->at(i) : box(i, frame);
snap.m_dirty_face[i] = dirty_face ? dirty_face->at(i) : face(i, frame);
if (!snap.m_dirty_face[i])
continue;
snap.image[i] = std::make_unique<uint8_t[]>(rtt(i, frame).bytes());
App::I->render_task_async([this, i, &snap, frame]
{
rtt(i, frame).bindFramebuffer();
glm::vec2 box_sz = zw(snap.m_dirty_box[i]) - xy(snap.m_dirty_box[i]);
glReadPixels(snap.m_dirty_box[i].x, snap.m_dirty_box[i].y,
box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, snap.image[i].get());
rtt(i, frame).unbindFramebuffer();
});
}
App::I->render_sync();
return snap;
}
void Layer::clear(const glm::vec4& c, int frame /*= -1*/)
{
if (frame == -1)
frame = m_frame_index;
m_frames[frame].clear(c);
}
bool Layer::create(int width, int height, std::string name)
{
m_name = name;
w = width;
h = height;
m_frame_index = 0;
m_frames.clear();
m_frames.emplace_back();
m_frames.back().create(width, height);
return true;
}
bool Layer::add_frame()
{
m_frames.emplace_back();
return m_frames.back().create(w, h);
}
int Layer::total_duration() const noexcept
{
int duration = 0;
for (auto& f : m_frames)
duration += f.m_duration;
return duration;
}
void Layer::goto_frame(int frame) noexcept
{
int i = 0;
for (i = 0; i < m_frames.size() && frame >= 0; i++)
frame -= m_frames[i].m_duration;
m_frame_index = i - 1;
}
void Layer::resize(int width, int height)
{
w = width;
h = height;
for (auto& frame : m_frames)
frame.resize(width, height);
}
///////////////////////////////////////////////////////////////////////////////////////////
void Layer::Snapshot::create(int w, int h)
{
width = w;
height = h;
for (int i = 0; i < 6; i++)
{
m_dirty_face[i] = false;
m_dirty_box[i] = glm::vec4(width, height, 0, 0);
image[i] = std::make_unique<uint8_t[]>(w * h * 4);
std::fill_n(image[i].get(), w * h * 4, 0);
}
}
void Layer::Snapshot::clear()
{
for (int i = 0; i < 6; i++)
{
m_dirty_face[i] = false;
m_dirty_box[i] = glm::vec4(width, height, 0, 0);
std::fill_n(image[i].get(), width * height * 4, 0);
}
}
void Layer::Snapshot::optimize()
{
for (int i = 0; i < 6; i++)
{
if (!m_dirty_face[i] || !image[i])
continue;
auto data = reinterpret_cast<glm::u8vec4*>(image[i].get());
glm::ivec2 bbmin(width, height);
glm::ivec2 bbmax(0);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (data[x + y * width].a > 0)
{
bbmin = glm::min(bbmin, { x, y });
bbmax = glm::max(bbmax, { x + 1, y + 1 });
}
}
}
//glm::vec2 bbsz = bbmax - bbmin;
}
}
int Layer::Snapshot::memsize() const
{
int ret = 0;
for (int i = 0; i < 6; i++)
{
if (m_dirty_face[i])
{
glm::vec2 sz = zw(m_dirty_box[i]) - xy(m_dirty_box[i]);
ret += sz.x * sz.y * 4;
}
}
return ret;
}
///////////////////////////////////////////////////////////////////////////////////////////
/*
LayerFrame::LayerFrame(LayerFrame&& other)
{
LOG("LayerFrame move-ctor");
for (int i = 0; i < 6; i++)
{
m_rtt[i] = std::move(other.m_rtt[i]);
m_dirty_box[i] = other.m_dirty_box[i];
m_dirty_face[i] = other.m_dirty_face[i];
}
m_duration = other.m_duration;
w = other.w;
h = other.h;
}
LayerFrame& LayerFrame::operator=(LayerFrame&& other)
{
LOG("LayerFrame move-assignment");
for (int i = 0; i < 6; i++)
{
m_rtt[i] = std::move(other.m_rtt[i]);
m_dirty_box[i] = other.m_dirty_box[i];
m_dirty_face[i] = other.m_dirty_face[i];
}
m_duration = other.m_duration;
w = other.w;
h = other.h;
return *this;
}
*/
bool LayerFrame::create(int width, int height, int duration /*= 1*/)
{
App::I->render_task([&]
{
for (int i = 0; i < 6; i++)
{
if (!m_rtt[i].create(width, height))
return false;
m_rtt[i].bindFramebuffer();
m_rtt[i].clear();
m_rtt[i].unbindFramebuffer();
m_dirty_box[i] = glm::vec4(width, height, 0, 0); // reset bounding box
m_dirty_face[i] = false;
}
});
m_duration = duration;
w = width;
h = height;
return true;
}
bool LayerFrame::resize(int width, int height)
{
glm::vec2 ratio = glm::vec2(width, height) / glm::vec2(w, h);
for (int i = 0; i < 6; i++)
{
if (!m_rtt[i].resize(width, height))
return false;
m_dirty_box[i] = m_dirty_box[i] * glm::vec4(ratio, ratio);
}
w = width;
h = height;
return true;
}
void LayerFrame::clear(const glm::vec4& c)
{
App::I->render_task([&]
{
// push clear color state
GLfloat cc[4];
glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
glClearColor(c.r, c.g, c.b, c.a);
bool erase = (c.a == 0.f);
for (int i = 0; i < 6; i++)
{
m_rtt[i].bindFramebuffer();
glClear(GL_COLOR_BUFFER_BIT);
m_rtt[i].unbindFramebuffer();
if (erase)
{
m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box
m_dirty_face[i] = false;
}
else
{
m_dirty_box[i] = glm::vec4(0, 0, w, h); // reset bounding box
m_dirty_face[i] = true;
}
}
// restore clear color state
glClearColor(cc[0], cc[1], cc[2], cc[3]);
});
}