split transform into cut and copy commands and implement history
This commit is contained in:
@@ -1039,7 +1039,8 @@ Here's a list of what's available in this release.
|
||||
<image path="data/ui/line.png" width="100%" height="100%" align="center" justify="flex-end"/>
|
||||
</button-custom>
|
||||
<button id="btn-grid" width="50" height="100%" margin="0 0 0 5" text="Grid"/>
|
||||
<button id="btn-transform" width="80" height="100%" margin="0 0 0 5" text="Transform"/>
|
||||
<button id="btn-copy" width="50" height="100%" margin="0 0 0 5" text="Copy"/>
|
||||
<button id="btn-cut" width="50" height="100%" margin="0 0 0 5" text="Cut"/>
|
||||
<!-- <button id="btn-fill" width="50" height="100%" margin="0 0 0 0" text="Fill"/> -->
|
||||
<button-custom id="btn-mask-free" width="50" height="100%" margin="0 0 0 5" thickness="1" border-color="0 0 0 1" pad="2">
|
||||
<image path="data/ui/sel-free.png" width="100%" height="100%" align="center" justify="flex-end"/>
|
||||
|
||||
@@ -251,7 +251,8 @@ void select_button(Node* main, T* button) {
|
||||
main->find<NodeButtonCustom>("btn-line")->set_color(color_button_normal);
|
||||
main->find<NodeButton>("btn-cam")->set_color(color_button_normal);
|
||||
main->find<NodeButton>("btn-grid")->set_color(color_button_normal);
|
||||
main->find<NodeButton>("btn-transform")->set_color(color_button_normal);
|
||||
main->find<NodeButton>("btn-copy")->set_color(color_button_normal);
|
||||
main->find<NodeButton>("btn-cut")->set_color(color_button_normal);
|
||||
//main->find<NodeButton>("btn-fill")->set_color(color_button_normal);
|
||||
main->find<NodeButtonCustom>("btn-mask-free")->set_color(color_button_normal);
|
||||
main->find<NodeButtonCustom>("btn-mask-line")->set_color(color_button_normal);
|
||||
@@ -313,10 +314,21 @@ void App::init_toolbar_draw()
|
||||
Canvas::set_mode(Canvas::kCanvasMode::Grid);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-transform"))
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-copy"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
select_button(layout[main_id], button);
|
||||
auto m = static_cast<CanvasModeTransform*>(canvas->m_canvas->modes[(int)ui::Canvas::kCanvasMode::Transform][0]);
|
||||
m->m_action = CanvasModeTransform::ActionType::Copy;
|
||||
Canvas::set_mode(Canvas::kCanvasMode::Transform);
|
||||
};
|
||||
}
|
||||
if (auto* button = layout[main_id]->find<NodeButton>("btn-cut"))
|
||||
{
|
||||
button->on_click = [this, button](Node*) {
|
||||
select_button(layout[main_id], button);
|
||||
auto m = static_cast<CanvasModeTransform*>(canvas->m_canvas->modes[(int)ui::Canvas::kCanvasMode::Transform][0]);
|
||||
m->m_action = CanvasModeTransform::ActionType::Cut;
|
||||
Canvas::set_mode(Canvas::kCanvasMode::Transform);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -451,6 +451,13 @@ void CanvasModeMaskFree::leave()
|
||||
// m_points.clear();
|
||||
}
|
||||
|
||||
void CanvasModeMaskFree::clear()
|
||||
{
|
||||
m_points.clear();
|
||||
m_points2d.clear();
|
||||
m_shape.clear();
|
||||
}
|
||||
|
||||
void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
||||
{
|
||||
static glm::vec2 oldpos;
|
||||
@@ -849,9 +856,21 @@ void CanvasModeTransform::init()
|
||||
|
||||
void CanvasModeTransform::enter()
|
||||
{
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
m_shape[i].clear();
|
||||
m_points_face[i].clear();
|
||||
m_shape[i].clear();
|
||||
}
|
||||
|
||||
if (m_action == ActionType::Import)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto m = static_cast<CanvasModeMaskFree*>(canvas->modes[(int)ui::Canvas::kCanvasMode::MaskFree][0]);
|
||||
canvas->m_smask_active = false;
|
||||
auto points = std::move(m->m_points2d);
|
||||
auto points = m->m_points2d;
|
||||
|
||||
glm::vec2 bb_min(FLT_MAX);
|
||||
glm::vec2 bb_max(-FLT_MAX);
|
||||
@@ -881,11 +900,11 @@ void CanvasModeTransform::enter()
|
||||
auto shape2d = poly_intersect(points, face);
|
||||
if (shape2d.size() < 3)
|
||||
{
|
||||
m_shape[plane].update_vertices(nullptr, 0);
|
||||
m_shape[plane].clear();
|
||||
m_points_face[plane].clear();
|
||||
continue;
|
||||
}
|
||||
std::vector<vertex_t> shape3d;
|
||||
shape3d.reserve(shape2d.size());
|
||||
m_points_face[plane].reserve(shape2d.size());
|
||||
glm::vec2 bb_min(canvas->m_size);
|
||||
glm::vec2 bb_max(0, 0);
|
||||
for (auto p2d : shape2d)
|
||||
@@ -893,7 +912,10 @@ void CanvasModeTransform::enter()
|
||||
p2d.y = canvas->m_box.w - p2d.y - 1;
|
||||
auto p2d_clip = ((p2d / zw(canvas->m_box)) * 2.f - 1.f);
|
||||
auto p3d_plane = canvas->m_plane_unproject[plane] * glm::vec4(p2d_clip, 0, 1);
|
||||
auto p2d_plane = (-xy(p3d_plane) / p3d_plane.z);
|
||||
auto p3d_norm = -p3d_plane / p3d_plane.z;
|
||||
if (p3d_norm.w < 0)
|
||||
continue;
|
||||
auto p2d_plane = xy(p3d_norm);
|
||||
auto p2d_plane_raster = (p2d_plane * 0.5f + 0.5f) * canvas->m_size;
|
||||
auto p3d_world = canvas->m_plane_transform[plane] * glm::vec4(p2d_plane, -1, 1);
|
||||
bb_min = glm::min(bb_min, p2d_plane_raster);
|
||||
@@ -905,17 +927,25 @@ void CanvasModeTransform::enter()
|
||||
vertex_t v;
|
||||
v.pos = glm::vec4(xyz(p3d_world), 1);
|
||||
v.uvs = p2d_plane_raster;
|
||||
shape3d.push_back(v);
|
||||
m_points_face[plane].push_back(v);
|
||||
}
|
||||
|
||||
if (m_points_face[plane].size() < 3)
|
||||
{
|
||||
m_shape[plane].clear();
|
||||
m_points_face[plane].clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto bb_sz = bb_max - bb_min;
|
||||
for (auto& v : shape3d)
|
||||
for (auto& v : m_points_face[plane])
|
||||
{
|
||||
v.uvs2 = v.uvs / canvas->m_size;
|
||||
v.uvs = (v.uvs - bb_min) / bb_sz;
|
||||
v.pos = center_mat * v.pos;
|
||||
}
|
||||
|
||||
shape3d = canvas->triangulate(shape3d);
|
||||
auto shape3d = canvas->triangulate(m_points_face[plane]);
|
||||
m_shape[plane].update_vertices(shape3d.data(), shape3d.size());
|
||||
|
||||
canvas->m_layers[canvas->m_current_layer_idx].m_rtt[plane].bindFramebuffer();
|
||||
@@ -926,37 +956,89 @@ void CanvasModeTransform::enter()
|
||||
canvas->m_layers[canvas->m_current_layer_idx].m_rtt[plane].unbindFramebuffer();
|
||||
}
|
||||
|
||||
auto& layer = canvas->m_layers[canvas->m_current_layer_idx];
|
||||
|
||||
GLint vp[4];
|
||||
glGetIntegerv(GL_VIEWPORT, vp);
|
||||
glViewport(0, 0, layer.w, layer.h);
|
||||
|
||||
bool depth = glIsEnabled(GL_DEPTH_TEST);
|
||||
bool blend = glIsEnabled(GL_BLEND);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f);
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, 0 });
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
if (m_action == ActionType::Cut)
|
||||
{
|
||||
glm::mat4 plane_camera = glm::lookAt(glm::vec3(0), canvas->m_plane_origin[i], canvas->m_plane_tangent[i]);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * plane_camera * m_xform * m_xform_local);
|
||||
|
||||
layer.m_rtt[i].bindFramebuffer();
|
||||
for (int j = 0; j < 6; j++)
|
||||
m_shape[j].draw_fill();
|
||||
auto& layer = canvas->m_layers[canvas->m_current_layer_idx];
|
||||
|
||||
layer.m_rtt[i].unbindFramebuffer();
|
||||
GLint vp[4];
|
||||
glGetIntegerv(GL_VIEWPORT, vp);
|
||||
glViewport(0, 0, layer.w, layer.h);
|
||||
|
||||
bool depth = glIsEnabled(GL_DEPTH_TEST);
|
||||
bool blend = glIsEnabled(GL_BLEND);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f);
|
||||
ui::ShaderManager::use(ui::kShader::Color);
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, 0 });
|
||||
|
||||
auto action = new ui::ActionStroke;
|
||||
action->was_saved = !canvas->m_unsaved;
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
auto plane_camera = glm::lookAt(glm::vec3(0), canvas->m_plane_origin[i], canvas->m_plane_tangent[i]);
|
||||
auto mvp = proj * plane_camera * m_xform * m_xform_local;
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, mvp);
|
||||
|
||||
layer.m_rtt[i].bindFramebuffer();
|
||||
|
||||
glm::vec2 bb_min(canvas->m_size);
|
||||
glm::vec2 bb_max(0, 0);
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
for (auto p : m_points_face[j])
|
||||
{
|
||||
auto p_clip = mvp * p.pos;
|
||||
auto p_norm = p_clip / p_clip.z;
|
||||
if (p_clip.w < 0 || glm::any(glm::greaterThan(glm::abs(xy(p_norm)), { 1, 1 })))
|
||||
continue;
|
||||
auto p_raster = (xy(p_norm) * 0.5f + 0.5f) * canvas->m_size;
|
||||
bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p_raster));
|
||||
bb_max = glm::min(canvas->m_size, glm::max(bb_max, p_raster));
|
||||
}
|
||||
}
|
||||
glm::vec2 pad(2);
|
||||
bb_min = glm::max({ 0, 0 }, glm::floor(bb_min) - pad);
|
||||
bb_max = glm::min(canvas->m_size, glm::ceil(bb_max) + pad);
|
||||
auto bb_sz = bb_max - bb_min;
|
||||
|
||||
if (bb_sz.x <= 0.f || bb_sz.y <= 0.f)
|
||||
{
|
||||
layer.m_rtt[i].unbindFramebuffer();
|
||||
continue;
|
||||
}
|
||||
|
||||
action->m_image[i] = std::make_unique<uint8_t[]>(bb_sz.x * bb_sz.y * 4);
|
||||
glReadPixels(bb_min.x, bb_min.y, bb_sz.x, bb_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, action->m_image[i].get());
|
||||
action->m_box[i] = { bb_min, bb_max };
|
||||
action->m_old_box[i] = layer.m_dirty_box[i];
|
||||
action->m_old_dirty[i] = layer.m_dirty_face[i];
|
||||
|
||||
layer.m_dirty_face[i] = true;
|
||||
layer.m_dirty_box[i] = {
|
||||
glm::min(xy(layer.m_dirty_box[i]), bb_min),
|
||||
glm::max(zw(layer.m_dirty_box[i]), bb_max),
|
||||
};
|
||||
|
||||
for (int j = 0; j < 6; j++)
|
||||
m_shape[j].draw_fill();
|
||||
|
||||
layer.m_rtt[i].unbindFramebuffer();
|
||||
}
|
||||
|
||||
depth ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
|
||||
blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
|
||||
glViewport(vp[0], vp[1], vp[2], vp[3]);
|
||||
|
||||
action->m_layer_idx = canvas->m_current_layer_idx;
|
||||
action->m_canvas = canvas;
|
||||
//action->m_stroke = std::move(m_current_stroke);
|
||||
ActionManager::add(action);
|
||||
}
|
||||
depth ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
|
||||
blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
|
||||
glViewport(vp[0], vp[1], vp[2], vp[3]);
|
||||
}
|
||||
|
||||
void CanvasModeTransform::leave()
|
||||
@@ -989,16 +1071,58 @@ void CanvasModeTransform::leave()
|
||||
canvas->m_sampler_bg.bind(1);
|
||||
canvas->m_sampler_bg.bind(0);
|
||||
|
||||
auto action = new ui::ActionStroke;
|
||||
action->was_saved = !canvas->m_unsaved;
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
glm::mat4 plane_camera = glm::lookAt(glm::vec3(0), canvas->m_plane_origin[i], canvas->m_plane_tangent[i]);
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, proj * plane_camera * m_xform * m_xform_local);
|
||||
auto plane_camera = glm::lookAt(glm::vec3(0), canvas->m_plane_origin[i], canvas->m_plane_tangent[i]);
|
||||
auto mvp = proj * plane_camera * m_xform * m_xform_local;
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, mvp);
|
||||
|
||||
layer.m_rtt[i].bindFramebuffer();
|
||||
|
||||
glm::vec2 bb_min(canvas->m_size);
|
||||
glm::vec2 bb_max(0, 0);
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
for (auto p : m_points_face[j])
|
||||
{
|
||||
auto p_clip = mvp * p.pos;
|
||||
auto p_norm = p_clip / p_clip.z;
|
||||
if (p_clip.w < 0 || glm::any(glm::greaterThan(glm::abs(xy(p_norm)), { 1, 1 })))
|
||||
continue;
|
||||
auto p_raster = (xy(p_norm) * 0.5f + 0.5f) * canvas->m_size;
|
||||
bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p_raster));
|
||||
bb_max = glm::min(canvas->m_size, glm::max(bb_max, p_raster));
|
||||
}
|
||||
}
|
||||
glm::vec2 pad(2);
|
||||
bb_min = glm::max({ 0, 0 }, glm::floor(bb_min) - pad);
|
||||
bb_max = glm::min(canvas->m_size, glm::ceil(bb_max) + pad);
|
||||
auto bb_sz = bb_max - bb_min;
|
||||
|
||||
if (bb_sz.x <= 0.f || bb_sz.y <= 0.f)
|
||||
{
|
||||
layer.m_rtt[i].unbindFramebuffer();
|
||||
continue;
|
||||
}
|
||||
|
||||
action->m_image[i] = std::make_unique<uint8_t[]>(bb_sz.x * bb_sz.y * 4);
|
||||
glReadPixels(bb_min.x, bb_min.y, bb_sz.x, bb_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, action->m_image[i].get());
|
||||
action->m_box[i] = { bb_min, bb_max };
|
||||
action->m_old_box[i] = layer.m_dirty_box[i];
|
||||
action->m_old_dirty[i] = layer.m_dirty_face[i];
|
||||
|
||||
layer.m_dirty_face[i] = true;
|
||||
layer.m_dirty_box[i] = {
|
||||
glm::min(xy(layer.m_dirty_box[i]), bb_min),
|
||||
glm::max(zw(layer.m_dirty_box[i]), bb_max),
|
||||
};
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
canvas->m_tex2[i].bind();
|
||||
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, canvas->m_width, canvas->m_height);
|
||||
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, bb_min.x, bb_min.y, bb_min.x, bb_min.y, bb_sz.x, bb_sz.y);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
for (int j = 0; j < 6; j++)
|
||||
@@ -1013,6 +1137,14 @@ void CanvasModeTransform::leave()
|
||||
depth ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
|
||||
blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
|
||||
glViewport(vp[0], vp[1], vp[2], vp[3]);
|
||||
|
||||
action->m_layer_idx = canvas->m_current_layer_idx;
|
||||
action->m_canvas = canvas;
|
||||
//action->m_stroke = std::move(m_current_stroke);
|
||||
ActionManager::add(action);
|
||||
|
||||
//auto m = static_cast<CanvasModeMaskFree*>(canvas->modes[(int)ui::Canvas::kCanvasMode::MaskFree][0]);
|
||||
//m->clear();
|
||||
}
|
||||
|
||||
void CanvasModeTransform::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera)
|
||||
@@ -1043,11 +1175,21 @@ void CanvasModeTransform::on_Draw(const glm::mat4& ortho, const glm::mat4& proj,
|
||||
for (int i = 0; i < corners.size(); i++)
|
||||
{
|
||||
auto c = m2d * glm::vec4(corners[i], 1);
|
||||
auto c2d = ((xy(c) / c.z) * 0.5f + 0.5f) * zw(canvas->m_box);
|
||||
auto c3d = c / c.z;
|
||||
if (c3d.w < 0)
|
||||
continue;
|
||||
auto c2d = (xy(c3d) * 0.5f + 0.5f) * zw(canvas->m_box);
|
||||
|
||||
ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP,
|
||||
ortho * glm::translate(glm::vec3(c2d, 0)) * glm::scale(glm::vec3(10.f)));
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, i == corner_hl ? 1.f : .5f });
|
||||
|
||||
// draw inside
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 1, 1, 1, i == corner_hl ? 1.f : .1f });
|
||||
m_circle.draw_fill();
|
||||
|
||||
// draw black border
|
||||
ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, { 0, 0, 0, 1 });
|
||||
m_circle.draw_stroke();
|
||||
}
|
||||
|
||||
if (depth) glEnable(GL_DEPTH_TEST);
|
||||
|
||||
@@ -122,12 +122,12 @@ class CanvasModeMaskFree : public CanvasMode
|
||||
bool m_dragging = false;
|
||||
std::vector<vertex_t> m_points;
|
||||
std::vector<glm::vec2> m_points2d;
|
||||
std::map<int, int> m_dirty_planes;
|
||||
public:
|
||||
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 init() override;
|
||||
virtual void leave() override;
|
||||
void clear();
|
||||
};
|
||||
|
||||
class CanvasModeMaskLine : public CanvasMode
|
||||
@@ -170,9 +170,12 @@ class CanvasModeTransform : public CanvasMode
|
||||
glm::mat4 m_drag_xform_local{1};
|
||||
std::vector<vertex_t> m_points{};
|
||||
std::vector<glm::vec2> m_points2d{};
|
||||
std::vector<vertex_t> m_points_face[6];
|
||||
std::map<int, int> m_dirty_planes{};
|
||||
bool m_active_tool = false;
|
||||
public:
|
||||
enum class ActionType : uint8_t { Copy, Cut, Import };
|
||||
ActionType m_action;
|
||||
CanvasModeTransform() = default;
|
||||
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;
|
||||
|
||||
@@ -97,6 +97,7 @@ public:
|
||||
return create_buffers(vertices, sizeof(vertex_t) * vcount);
|
||||
}
|
||||
void update_vertices(vertex_t* vertices, int vcount);
|
||||
void clear() { update_vertices(nullptr, 0); }
|
||||
};
|
||||
|
||||
class Plane : public Shape
|
||||
|
||||
Reference in New Issue
Block a user