Trying to add history to the Fill tool

This commit is contained in:
2017-07-15 10:51:58 +01:00
parent 4666a3dffc
commit 4e765f1450
6 changed files with 112 additions and 17 deletions

View File

@@ -73,7 +73,7 @@ void App::initShaders()
"in vec4 pos;" "in vec4 pos;"
"void main(){" "void main(){"
" gl_Position = mvp * pos;" " gl_Position = mvp * pos;"
" gl_PointSize = 15.0;" " gl_PointSize = 5.0;"
"}"; "}";
static const char* shader_color_f = static const char* shader_color_f =
SHADER_VERSION SHADER_VERSION

View File

@@ -46,8 +46,37 @@ glm::mat4 ui::Canvas::m_plane_transform[6] = {
void ui::Canvas::clear(const glm::vec4& c/*={0,0,0,1}*/) void ui::Canvas::clear(const glm::vec4& c/*={0,0,0,1}*/)
{ {
snap_history({ 0, 1, 2, 3, 4, 5 });
m_layers[m_current_layer_idx].clear(c); m_layers[m_current_layer_idx].clear(c);
} }
void ui::Canvas::snap_history(const std::vector<int>& planes)
{
auto action = new ActionStroke;
for (auto i : planes)
{
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<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];
action->m_old_box[i] = m_layers[m_current_layer_idx].m_dirty_box[i];
action->m_old_dirty[i] = m_layers[m_current_layer_idx].m_dirty_face[i];
m_layers[m_current_layer_idx].m_rtt[i].unbindFramebuffer();
}
// save history
action->m_layer_idx = m_current_layer_idx;
action->m_canvas = this;
action->m_stroke = std::move(m_current_stroke);
action->clear_layer = true;
ActionManager::add(action);
}
void ui::Canvas::stroke_end() void ui::Canvas::stroke_end()
{ {
if (!m_current_stroke) if (!m_current_stroke)
@@ -268,7 +297,7 @@ bool ui::Canvas::point_trace(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ra
return false; return false;
} }
bool ui::Canvas::point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, bool ui::Canvas::point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir,
glm::vec3& hit_pos, glm::vec3& hit_normal, int plane_id) glm::vec3& hit_pos, glm::vec3& hit_normal, glm::vec2& hit_fb_pos, int plane_id)
{ {
point_unproject(loc, { 0, 0, m_box.zw }, m_mv, m_proj, ray_origin, ray_dir); point_unproject(loc, { 0, 0, m_box.zw }, m_mv, m_proj, ray_origin, ray_dir);
glm::vec3 hit; glm::vec3 hit;
@@ -277,8 +306,11 @@ bool ui::Canvas::point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::ve
m_plane_normal[plane_id], m_plane_tangent[plane_id], hit)) m_plane_normal[plane_id], m_plane_tangent[plane_id], hit))
{ {
glm::mat4 plane_camera = glm::lookAt(m_plane_origin[plane_id], m_plane_normal[plane_id], m_plane_tangent[plane_id]); glm::mat4 plane_camera = glm::lookAt(m_plane_origin[plane_id], m_plane_normal[plane_id], m_plane_tangent[plane_id]);
glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1);
hit_pos = hit; hit_pos = hit;
hit_normal = m_plane_normal[plane_id]; hit_normal = m_plane_normal[plane_id];
hit_fb_pos.x = -(plane_local.x * 0.5f - 0.5f);
hit_fb_pos.y = (plane_local.y * 0.5f + 0.5f);
return true; return true;
} }
return false; return false;
@@ -330,6 +362,7 @@ void ui::Canvas::stroke_commit()
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
m_dirty_box[i] = glm::vec4(0, 0, m_width, m_height); // reset bounding box
if (!m_dirty_face[i]) if (!m_dirty_face[i])
continue; // no stroke on this face, skip it continue; // no stroke on this face, skip it
@@ -1067,14 +1100,24 @@ void ui::Layer::clear(const glm::vec4& c)
glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
glClearColor(c.r, c.g, c.b, c.a); glClearColor(c.r, c.g, c.b, c.a);
bool erase = (c.a == 0.f);
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
m_rtt[i].bindFramebuffer(); m_rtt[i].bindFramebuffer();
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
m_rtt[i].unbindFramebuffer(); m_rtt[i].unbindFramebuffer();
m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box if (erase)
m_dirty_face[i] = false; {
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 // restore clear color state

View File

@@ -106,6 +106,7 @@ public:
void clear(const glm::vec4& color = { 1, 1, 1, 0 }); void clear(const glm::vec4& color = { 1, 1, 1, 0 });
void snapshot_save(std::string data_path); void snapshot_save(std::string data_path);
void snapshot_restore(); void snapshot_restore();
void snap_history(const std::vector<int>& planes);
void clear_context(); void clear_context();
void export_equirectangular(std::string data_path); void export_equirectangular(std::string data_path);
void export_anim(std::string data_path); void export_anim(std::string data_path);
@@ -122,7 +123,7 @@ public:
bool point_trace(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, bool point_trace(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir,
glm::vec3& hit_pos, glm::vec3& hit_normal, int& out_plane_id); glm::vec3& hit_pos, glm::vec3& hit_normal, int& out_plane_id);
bool point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, bool point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir,
glm::vec3& hit_pos, glm::vec3& hit_normal, int plane_id); glm::vec3& hit_pos, glm::vec3& hit_normal, glm::vec2& hit_fb_pos, int plane_id);
}; };
class ActionStroke : public Action class ActionStroke : public Action
@@ -134,6 +135,7 @@ public:
bool m_old_dirty[6]; bool m_old_dirty[6];
glm::ivec4 m_box[6]; glm::ivec4 m_box[6];
bool m_dirty[6]; bool m_dirty[6];
bool clear_layer = false;
int m_layer_idx; int m_layer_idx;
Canvas* m_canvas; Canvas* m_canvas;
virtual void run() override virtual void run() override
@@ -142,6 +144,8 @@ public:
} }
virtual void undo() override virtual void undo() override
{ {
if (clear_layer)
m_canvas->m_layers[m_layer_idx].clear({ 0, 0, 0, 0 });
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
// empty data // empty data

View File

@@ -254,7 +254,8 @@ void CanvasModeGrid::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
case kEventType::MouseMove: case kEventType::MouseMove:
{ {
glm::vec3 ro, rd, hit_o, hit_d; glm::vec3 ro, rd, hit_o, hit_d;
if (m_dragging && canvas->point_trace_plane(loc, ro, rd, hit_o, hit_d, m_plane_id)) glm::vec2 hit_fb;
if (m_dragging && canvas->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, m_plane_id))
{ {
m_lines.back() = { hit_o, hit_d }; m_lines.back() = { hit_o, hit_d };
origin = hit_o; origin = hit_o;
@@ -326,8 +327,34 @@ void CanvasModeFill::init()
void CanvasModeFill::leave() void CanvasModeFill::leave()
{ {
canvas->draw_objects(std::bind(&CanvasModeFill::on_Draw, this, glm::mat4(), std::placeholders::_1, std::placeholders::_2)); // canvas->draw_objects(std::bind(&CanvasModeFill::on_Draw, this, glm::mat4(), std::placeholders::_1, std::placeholders::_2));
m_points.clear(); // m_points.clear();
}
// Returns 1 if the lines intersect, otherwise 0. In addition, if the lines
// intersect the intersection point may be stored in the floats i_x and i_y.
char get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y,
float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y)
{
float s1_x, s1_y, s2_x, s2_y;
s1_x = p1_x - p0_x; s1_y = p1_y - p0_y;
s2_x = p3_x - p2_x; s2_y = p3_y - p2_y;
float s, t;
s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
{
// Collision detected
if (i_x != NULL)
*i_x = p0_x + (t * s1_x);
if (i_y != NULL)
*i_y = p0_y + (t * s1_y);
return 1;
}
return 0; // No collision
} }
void CanvasModeFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc) void CanvasModeFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
@@ -336,14 +363,26 @@ void CanvasModeFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
{ {
case kEventType::MouseDownR: case kEventType::MouseDownR:
{ {
auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) { if (!m_points.empty())
ui::ShaderManager::use(ui::kShader::Color); {
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera); auto drawer = [this](const glm::mat4& camera, const glm::mat4& proj) {
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { node->m_brush.m_tip_color.rgb(), node->m_brush.m_tip_opacity }); ui::ShaderManager::use(ui::kShader::Color);
m_shape.draw_fill(); ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * camera);
}; ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { node->m_brush.m_tip_color.rgb(), node->m_brush.m_tip_opacity });
canvas->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2)); m_shape.draw_fill();
m_points.clear(); };
std::vector<int> planes;
for (auto p : m_dirty_planes)
{
planes.push_back(p.first);
canvas->m_dirty_face[p.first] = true;
canvas->m_dirty_box[p.first] = { 0, 0, canvas->m_width, canvas->m_height };
}
canvas->snap_history(planes);
canvas->draw_objects(std::bind(drawer, std::placeholders::_1, std::placeholders::_2));
m_points.clear();
m_dirty_planes.clear();
}
break; break;
} }
case kEventType::MouseDownL: case kEventType::MouseDownL:
@@ -351,9 +390,11 @@ void CanvasModeFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
node->mouse_capture(); node->mouse_capture();
m_dragging = true; m_dragging = true;
glm::vec3 ro, rd, hit_o, hit_d; glm::vec3 ro, rd, hit_o, hit_d;
glm::vec2 hit_fb;
int plane_id; int plane_id;
if (canvas->point_trace(loc, ro, rd, hit_o, hit_d, plane_id)) if (canvas->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, 0))
{ {
m_dirty_planes[plane_id]++;
ui::Shape::vertex_t v; ui::Shape::vertex_t v;
v.pos = glm::vec4(hit_o, 1); v.pos = glm::vec4(hit_o, 1);
v.uvs = glm::vec2(0); v.uvs = glm::vec2(0);
@@ -367,6 +408,11 @@ void CanvasModeFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
m_points.push_back(m_points[0]); m_points.push_back(m_points[0]);
m_points.push_back(last); m_points.push_back(last);
m_points.push_back(v); m_points.push_back(v);
float isx, isy;
if (get_line_intersection(m_points[0].pos.x, m_points[0].pos.y, v.pos.x, v.pos.y, 1, -1, 1, 1, &isx, &isy))
{
LOG("intersection in %f %f", isx, isy);
}
} }
m_shape.update_vertices(m_points.data(), m_points.size()); m_shape.update_vertices(m_points.data(), m_points.size());
} }

View File

@@ -87,6 +87,7 @@ class CanvasModeFill : public CanvasMode
ui::DynamicShape m_shape; ui::DynamicShape m_shape;
bool m_dragging = false; bool m_dragging = false;
std::vector<ui::Shape::vertex_t> m_points; std::vector<ui::Shape::vertex_t> m_points;
std::map<int, int> m_dirty_planes;
public: public:
virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override; virtual void on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) override;
virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override; virtual void on_MouseEvent(MouseEvent* me, glm::vec2& loc) override;

View File

@@ -60,6 +60,7 @@ double now_seconds()
time(&timer); /* get current time; same as: timer = time(NULL) */ time(&timer); /* get current time; same as: timer = time(NULL) */
seconds = difftime(timer, mktime(&y2k)); seconds = difftime(timer, mktime(&y2k));
return seconds;
} }
void check_OpenGLError(const char* stmt, const char* fname, int line) void check_OpenGLError(const char* stmt, const char* fname, int line)