prepare to draw on multiple planes

This commit is contained in:
2017-04-27 21:13:09 +01:00
parent e6332322b3
commit a6a020a389
4 changed files with 270 additions and 281 deletions

View File

@@ -76,22 +76,4 @@ public:
StrokeSample randomize_sample(const glm::vec2& pos, float pressure); StrokeSample randomize_sample(const glm::vec2& pos, float pressure);
}; };
class Layer
{
public:
RTT m_rtt;
bool m_visible = true;
bool m_locked = false;
float m_opacity = 1.f;
std::string m_name;
bool create(int width, int height, std::string name)
{
m_rtt.create(width, height);
m_rtt.bindFramebuffer();
m_rtt.clear();
m_rtt.unbindFramebuffer();
return true;
}
};
NS_END NS_END

View File

@@ -4,14 +4,7 @@
void ui::Canvas::clear(const glm::vec4& c/*={0,0,0,1}*/) void ui::Canvas::clear(const glm::vec4& c/*={0,0,0,1}*/)
{ {
GLfloat cc[4]; m_layers[m_current_layer_idx].clear(c);
m_layers[m_current_layer_idx].m_rtt.bindFramebuffer();
glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
glClearColor(c.r, c.g, c.b, c.a);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, m_width, m_height);
glClearColor(cc[0], cc[1], cc[2], cc[3]);
m_layers[m_current_layer_idx].m_rtt.unbindFramebuffer();
} }
void ui::Canvas::stroke_end() void ui::Canvas::stroke_end()
{ {
@@ -26,8 +19,6 @@ void ui::Canvas::stroke_draw()
m_dirty = true; m_dirty = true;
m_tmp.bindFramebuffer();
GLint vp[4]; GLint vp[4];
GLfloat cc[4]; GLfloat cc[4];
glGetIntegerv(GL_VIEWPORT, vp); glGetIntegerv(GL_VIEWPORT, vp);
@@ -42,10 +33,15 @@ void ui::Canvas::stroke_draw()
auto& tex = TextureManager::get(m_brush.m_tex_id); auto& tex = TextureManager::get(m_brush.m_tex_id);
tex.bind(); tex.bind();
m_sampler.bind(0); m_sampler.bind(0);
glActiveTexture(GL_TEXTURE1);
m_tex.bind(); // bg, copy of framebuffer (copied before drawing)
m_sampler_bg.bind(1); m_sampler_bg.bind(1);
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) if (m_use_instanced)
{ {
glEnable(GL_BLEND); glEnable(GL_BLEND);
@@ -147,8 +143,8 @@ void ui::Canvas::stroke_draw()
tex_pos.x, tex_pos.y, tex_pos.x, tex_pos.y,
tex_sz.x, tex_sz.y); tex_sz.x, tex_sz.y);
m_box.xy = glm::min(m_box.xy(), (glm::vec2)tex_pos); m_dirty_box[i].xy = glm::min(m_dirty_box[i].xy(), (glm::vec2)tex_pos);
m_box.zw = glm::max(m_box.zw(), (glm::vec2)(tex_pos + tex_sz)); m_dirty_box[i].zw = glm::max(m_dirty_box[i].zw(), (glm::vec2)(tex_pos + tex_sz));
ShaderManager::u_mat4(kShaderUniform::MVP, glm::mat4()); ShaderManager::u_mat4(kShaderUniform::MVP, glm::mat4());
ShaderManager::u_float(kShaderUniform::Alpha, s.flow); ShaderManager::u_float(kShaderUniform::Alpha, s.flow);
@@ -157,18 +153,20 @@ void ui::Canvas::stroke_draw()
} }
} }
m_tex.unbind(); m_tex[i].unbind();
m_tmp[i].unbindFramebuffer();
}
glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
m_sampler.unbind(); m_sampler.unbind();
m_sampler_bg.unbind(); m_sampler_bg.unbind();
tex.unbind(); tex.unbind();
glDisable(GL_BLEND);
glViewport(vp[0], vp[1], vp[2], vp[3]); glViewport(vp[0], vp[1], vp[2], vp[3]);
glClearColor(cc[0], cc[1], cc[2], cc[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]);
m_tmp.unbindFramebuffer();
} }
void ui::Canvas::stroke_commit() void ui::Canvas::stroke_commit()
{ {
@@ -176,30 +174,40 @@ void ui::Canvas::stroke_commit()
return; return;
m_dirty = false; m_dirty = false;
m_layers[m_current_layer_idx].m_rtt.bindFramebuffer(); // save viewport and clear color states
// save image before commit
glm::vec2 box_sz = m_box.zw() - m_box.xy();
auto image = std::make_unique<uint8_t[]>(box_sz.x * box_sz.y * 4);
glReadPixels(m_box.x, m_box.y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, image.get());
// copy to tmp2 for layer blending
m_tex2.bind();
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height);
m_tex2.unbind();
GLint vp[4]; GLint vp[4];
GLfloat cc[4]; GLfloat cc[4];
glGetIntegerv(GL_VIEWPORT, vp); glGetIntegerv(GL_VIEWPORT, vp);
glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
glViewport(0, 0, m_width, m_height);
GLboolean blend = glIsEnabled(GL_BLEND); 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); glDisable(GL_BLEND);
m_tmp.bindTexture(); for (int i = 0; i < 6; i++)
{
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<uint8_t[]>(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); glActiveTexture(GL_TEXTURE1);
m_tex2.bind(); m_tex2[i].bind();
m_sampler.bind(0); m_sampler.bind(0);
m_sampler_bg.bind(1); m_sampler_bg.bind(1);
if (m_erase) if (m_erase)
@@ -217,21 +225,20 @@ void ui::Canvas::stroke_commit()
m_plane.draw_fill(); m_plane.draw_fill();
m_sampler.unbind(); m_sampler.unbind();
m_sampler_bg.unbind(); m_sampler_bg.unbind();
m_tex2.unbind(); m_tex2[i].unbind();
glActiveTexture(GL_TEXTURE0); m_tmp[i].unbindTexture();
m_tmp.unbindTexture();
m_layers[m_current_layer_idx].m_rtt[i].unbindFramebuffer();
}
// restore viewport and clear color states
blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
glViewport(vp[0], vp[1], vp[2], vp[3]); glViewport(vp[0], vp[1], vp[2], vp[3]);
glClearColor(cc[0], cc[1], cc[2], cc[3]); glClearColor(cc[0], cc[1], cc[2], cc[3]);
glActiveTexture(GL_TEXTURE0);
m_layers[m_current_layer_idx].m_rtt.unbindFramebuffer();
// save history // save history
auto action = new ActionStroke;
action->m_image = std::move(image);
action->m_box = m_box;
action->m_layer_idx = m_current_layer_idx; action->m_layer_idx = m_current_layer_idx;
action->m_canvas = this; action->m_canvas = this;
action->m_stroke = std::move(m_current_stroke); action->m_stroke = std::move(m_current_stroke);
@@ -248,21 +255,24 @@ void ui::Canvas::stroke_start(glm::vec2 point, float pressure, const ui::Brush&
m_current_stroke->start(brush); m_current_stroke->start(brush);
m_current_stroke->add_point(point, pressure); m_current_stroke->add_point(point, pressure);
m_box = glm::vec4(m_width, m_height, 0, 0); // reset bounding box for (int i = 0; i < 6; i++)
{
m_dirty_box[i] = glm::vec4(m_width, m_height, 0, 0); // reset bounding box
if (m_erase) if (m_erase)
{ {
m_layers[m_current_layer_idx].m_rtt.bindFramebuffer(); m_layers[m_current_layer_idx].m_rtt[i].bindFramebuffer();
m_tmp.bindTexture(); m_tmp[i].bindTexture();
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height);
m_tmp.unbindTexture(); m_tmp[i].unbindTexture();
m_layers[m_current_layer_idx].m_rtt.unbindFramebuffer(); m_layers[m_current_layer_idx].m_rtt[i].unbindFramebuffer();
} }
else else
{ {
m_tmp.bindFramebuffer(); m_tmp[i].bindFramebuffer();
m_tmp.clear({ 0, 0, 0, 0 }); m_tmp[i].clear({ 0, 0, 0, 0 });
m_tmp.unbindFramebuffer(); m_tmp[i].unbindFramebuffer();
}
} }
m_show_tmp = true; m_show_tmp = true;
} }
@@ -281,9 +291,12 @@ void ui::Canvas::resize(int width, int height)
{ {
m_width = width; m_width = width;
m_height = height; m_height = height;
m_tmp.create(width, height); for (int i = 0; i < 6; i++)
m_tex.create(width, height); {
m_tex2.create(width, height); m_tmp[i].create(width, height);
m_tex[i].create(width, height);
m_tex2[i].create(width, height);
}
for (auto& l : m_layers) for (auto& l : m_layers)
{ {
l.create(width, height, ""); l.create(width, height, "");
@@ -293,9 +306,12 @@ bool ui::Canvas::create(int width, int height)
{ {
m_width = width; m_width = width;
m_height = height; m_height = height;
m_tmp.create(width, height); for (int i = 0; i < 6; i++)
m_tex.create(width, height); {
m_tex2.create(width, height); // TODO: destroy before recreating 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.create();
m_sampler_bg.create(); m_sampler_bg.create();
m_plane.create<1>(1, 1); m_plane.create<1>(1, 1);

View File

@@ -8,6 +8,44 @@
NS_START NS_START
class Layer
{
public:
RTT m_rtt[6];
bool m_visible = true;
bool m_locked = false;
float m_opacity = 1.f;
std::string m_name;
bool create(int width, int height, std::string 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;
}
void 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]);
}
};
class Canvas class Canvas
{ {
Plane m_plane; Plane m_plane;
@@ -27,9 +65,12 @@ public:
bool m_show_tmp = false; bool m_show_tmp = false;
std::vector<Layer> m_layers; std::vector<Layer> m_layers;
std::vector<int> m_order; std::vector<int> m_order;
RTT m_tmp; glm::vec4 m_dirty_box[6];
Texture2D m_tex; RTT m_tmp[6];
Texture2D m_tex2; Texture2D m_tex[6];
Texture2D m_tex2[6];
glm::vec3 m_plane_origin[6];
glm::vec3 m_plane_normal[6];
Sampler m_sampler; Sampler m_sampler;
Sampler m_sampler_bg; Sampler m_sampler_bg;
glm::vec2 m_cam_rot; glm::vec2 m_cam_rot;
@@ -52,8 +93,9 @@ class ActionStroke : public Action
{ {
public: public:
std::unique_ptr<Stroke> m_stroke; std::unique_ptr<Stroke> m_stroke;
std::unique_ptr<uint8_t[]> m_image; std::unique_ptr<uint8_t[]> m_image[6];
glm::ivec4 m_box; glm::ivec4 m_box[6];
bool m_dirty[6];
int m_layer_idx; int m_layer_idx;
Canvas* m_canvas; Canvas* m_canvas;
virtual void run() override virtual void run() override
@@ -62,12 +104,17 @@ public:
} }
virtual void undo() override virtual void undo() override
{ {
m_canvas->m_layers[m_layer_idx].m_rtt.bindTexture(); for (int i = 0; i < 6; i++)
{
// empty data
if (!m_image[i])
continue;
glm::vec2 box_sz = m_box.zw() - m_box.xy(); m_canvas->m_layers[m_layer_idx].m_rtt[i].bindTexture();
glTexSubImage2D(GL_TEXTURE_2D, 0, m_box.x, m_box.y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, m_image.get()); glm::vec2 box_sz = m_box[i].zw() - m_box[i].xy();
glTexSubImage2D(GL_TEXTURE_2D, 0, m_box[i].x, m_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, m_image[i].get());
m_canvas->m_layers[m_layer_idx].m_rtt.unbindTexture(); m_canvas->m_layers[m_layer_idx].m_rtt[i].unbindTexture();
}
} }
}; };

View File

@@ -1910,90 +1910,37 @@ public:
// auto plane_mvp = proj * camera * transform * // auto plane_mvp = proj * camera * transform *
// glm::scale(glm::vec3(sz, 1)); // glm::scale(glm::vec3(sz, 1));
auto plane_mvp = proj * camera * glm::translate(glm::vec3(0, 0, -1));
m_sampler.bind(0); m_sampler.bind(0);
ui::ShaderManager::use(kShader::TextureAlpha); ui::ShaderManager::use(kShader::TextureAlpha);
ui::ShaderManager::u_int(kShaderUniform::Tex, 0); ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp);
auto blend = glIsEnabled(GL_BLEND); auto blend = glIsEnabled(GL_BLEND);
glEnable(GL_BLEND); glEnable(GL_BLEND);
for (auto i : m_canvas->m_order) for (auto layer_index : m_canvas->m_order)
{ {
if (!(m_canvas->m_erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == i)) for (int plane_index = 0; plane_index < 4; plane_index++)
{ {
ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[i].m_opacity); auto plane_mvp = proj * camera * glm::eulerAngleY(glm::radians(90.f * plane_index)) * glm::translate(glm::vec3(0, 0, -1));
m_canvas->m_layers[i].m_rtt.bindTexture(); ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp);
if (!(m_canvas->m_erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index))
{
ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index].m_opacity);
m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture();
NodeBorder::m_plane.draw_fill(); NodeBorder::m_plane.draw_fill();
m_canvas->m_layers[i].m_rtt.unbindTexture(); m_canvas->m_layers[layer_index].m_rtt[plane_index].unbindTexture();
} }
if (m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == i) if (m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index)
{ {
glEnable(GL_BLEND); glEnable(GL_BLEND);
ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity); ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity);
m_canvas->m_tmp.bindTexture(); m_canvas->m_tmp[plane_index].bindTexture();
NodeBorder::m_plane.draw_fill(); NodeBorder::m_plane.draw_fill();
m_canvas->m_tmp.unbindTexture(); m_canvas->m_tmp[plane_index].unbindTexture();
} }
} }
ui::ShaderManager::use(kShader::Color);
ui::ShaderManager::u_vec4(kShaderUniform::Col, { 1, 0, 0, 1 });
// auto loc = m_cur;
// auto clip_space = glm::vec2(loc.x, box.w - loc.y - 1.f) / box.zw() * 2.f - 1.f;
// auto inv = glm::inverse(proj * camera);
// auto wp = inv * glm::vec4(clip_space, 0.9, 1);
// wp = wp / wp.w;
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& 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(m_cur, { 0, 0, box.zw }, camera, proj, ray_origin, ray_dir);
glm::vec3 plane_origin{ 0, 0, -1 }, plane_dir{ 0, 0, 1 };
glm::vec3 hit;
if (intersect(ray_origin, ray_dir, plane_origin, plane_dir, hit))
{
glm::mat4 plane_camera = glm::lookAt(plane_origin, plane_dir, { 0, 1, 0 });
glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1);
if (glm::abs(plane_local.x) < 0.5f && glm::abs(plane_local.y) < 0.5f)
{
LOG("hit %f %f %f", plane_local.x, plane_local.y, plane_local.z);
} }
}
float fovy = glm::radians(m_canvas->m_cam_fov);
float fovx = fovy * box.z / box.w;
glm::vec2 fov = { fovx, fovy };
auto fov_t = glm::vec2(m_cur.x, box.w - m_cur.y - 1.f) / box.zw();
glm::vec2 fov_rot = glm::lerp(-fov*.5f, fov*.5f, fov_t);
glm::mat4 fov_rot_mat = glm::eulerAngleXY(-fov_rot.y, fov_rot.x) * camera;
if (method)
ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera * glm::translate(hit) * glm::scale(glm::vec3(.1)));
else
ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera * glm::translate(hit) * glm::scale(glm::vec3(.1)) * glm::transpose(fov_rot_mat));
//NodeBorder::m_plane.draw_fill();
blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
m_sampler.unbind(); m_sampler.unbind();
@@ -2015,9 +1962,6 @@ public:
KeyEvent* ke = static_cast<KeyEvent*>(e); KeyEvent* ke = static_cast<KeyEvent*>(e);
GestureEvent* ge = static_cast<GestureEvent*>(e); GestureEvent* ge = static_cast<GestureEvent*>(e);
auto loc = (me->m_pos - m_pos) * root()->m_zoom; auto loc = (me->m_pos - m_pos) * root()->m_zoom;
auto clip_space = glm::vec2(loc.x, m_size.y - loc.y - 1.f) / m_size * 2.f - 1.f;
//auto fb_space = glm::inverse(m_canvas->m_mvp) * glm::vec4(clip_space, 0, 1);
auto cur = glm::vec2(loc.x, m_size.y - loc.y - 1.f);// fb_space.xy();
switch (e->m_type) switch (e->m_type)
{ {