diff --git a/engine/app.cpp b/engine/app.cpp index 0537401..9d18058 100644 --- a/engine/app.cpp +++ b/engine/app.cpp @@ -195,6 +195,22 @@ void App::initShaders() " mediump vec3 rgb = mix(bg.rgb, fg.rgb, fg.a / alpha_tot);\n" " frag = vec4(rgb, alpha_tot);\n" "}\n"; + // ERASER + static const char* shader_stroke_erase_f = + SHADER_VERSION + "uniform mediump sampler2D tex;\n" + "uniform mediump sampler2D tex_bg;\n" + "uniform mediump vec4 col;\n" + "uniform mediump vec2 resolution;\n" + "uniform mediump float alpha;\n" + "in mediump vec2 uv;\n" + "out mediump vec4 frag;\n" + "void main(){\n" + " mediump vec2 uv2 = gl_FragCoord.st / resolution;\n" + " mediump float brush_alpha = ( 1.0 - texture(tex, uv).r ) * alpha;\n" + " mediump vec4 bg = texture(tex_bg, uv2);\n" + " frag = vec4(bg.rgb, bg.a - bg.a * brush_alpha);\n" + "}\n"; // STROKE LAYER BLEND static const char* shader_stroke_layer_v = @@ -243,6 +259,8 @@ void App::initShaders() LOG("Failed to create shader Atlas"); if (!ShaderManager::create(kShader::Stroke, shader_stroke_v, shader_stroke_f)) LOG("Failed to create shader Stroke"); + if (!ShaderManager::create(kShader::StrokeErase, shader_stroke_v, shader_stroke_erase_f)) + LOG("Failed to create shader StrokeErase"); if (!ShaderManager::create(kShader::StrokeLayer, shader_stroke_layer_v, shader_stroke_layer_f)) LOG("Failed to create shader StrokeLayer"); LOG("shaders initialized"); @@ -311,7 +329,11 @@ void App::initLayout() stroke->loaded(); if (canvas) + { + stroke->m_canvas->m_brush.m_tip_color = color->m_color; + stroke->m_canvas->draw_stroke(); canvas->m_brush = stroke->m_canvas->m_brush; + } brushes->on_brush_changed = [this](Node* target, int index) { auto tid = brushes->get_texture_id(index); @@ -324,7 +346,7 @@ void App::initLayout() color->on_color_changed = [this](Node* target, glm::vec4 color) { stroke->m_canvas->m_brush.m_tip_color = color; - stroke->m_canvas->draw_stroke(); +// stroke->m_canvas->draw_stroke(); if (canvas) canvas->m_brush = stroke->m_canvas->m_brush; if (on_color_change) @@ -359,61 +381,25 @@ void App::initLayout() if (auto* button = layout[main_id]->find("btn-stroke")) { button->on_click = [this](Node*) { - panels->remove_all_children(); - if (current_panel != stroke.get()) - { - panels->add_child(std::static_pointer_cast(stroke)); - current_panel = stroke.get(); - } - else - { - current_panel = nullptr; - } + panels->get_child_index(stroke.get()) == -1 ? panels->add_child(stroke) : panels->remove_child(stroke.get()); }; } if (auto* button = layout[main_id]->find("btn-brush")) { button->on_click = [this](Node*) { - panels->remove_all_children(); - if (current_panel != brushes.get()) - { - panels->add_child(std::static_pointer_cast(brushes)); - current_panel = brushes.get(); - } - else - { - current_panel = nullptr; - } + panels->get_child_index(brushes.get()) == -1 ? panels->add_child(brushes) : panels->remove_child(brushes.get()); }; } if (auto* button = layout[main_id]->find("btn-color")) { button->on_click = [this](Node*) { - panels->remove_all_children(); - if (current_panel != color.get()) - { - panels->add_child(std::static_pointer_cast(color)); - current_panel = color.get(); - } - else - { - current_panel = nullptr; - } + panels->get_child_index(color.get()) == -1 ? panels->add_child(color) : panels->remove_child(color.get()); }; } if (auto* button = layout[main_id]->find("btn-layer")) { button->on_click = [this](Node*) { - panels->remove_all_children(); - if (current_panel != layers.get()) - { - panels->add_child(std::static_pointer_cast(layers)); - current_panel = layers.get(); - } - else - { - current_panel = nullptr; - } + panels->get_child_index(layers.get()) == -1 ? panels->add_child(layers) : panels->remove_child(layers.get()); }; } @@ -428,13 +414,14 @@ void App::initLayout() button->m_text->set_text(canvas->m_canvas->m_use_instanced ? "INST" : "NORM"); } }; + button->m_text->set_text("NORM"); } if (auto* button = layout[main_id]->find("btn-close")) { button->on_click = [this](Node*) { //exit(0); if (canvas) - canvas->m_canvas->clear(); + canvas->m_canvas->clear({ 0, 0, 0, 0 }); }; } if (auto* button = layout[main_id]->find("btn-popup")) @@ -536,7 +523,7 @@ void App::init() LOG("OPENGL: %.*s", length, message); FlushConsoleInputBuffer(GetStdHandle(STD_OUTPUT_HANDLE)); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), info.wAttributes); - __debugbreak(); + //__debugbreak(); } }, nullptr); glEnable(GL_DEBUG_OUTPUT); diff --git a/engine/canvas.cpp b/engine/canvas.cpp index 3fdb1a3..6e7f9fd 100644 --- a/engine/canvas.cpp +++ b/engine/canvas.cpp @@ -57,8 +57,16 @@ void ui::Canvas::stroke_draw() else { glDisable(GL_BLEND); - ShaderManager::use(ui::kShader::Stroke); - ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); + if (m_erase) + { + ShaderManager::use(ui::kShader::StrokeErase); + //ShaderManager::u_vec4(kShaderUniform::Col, m_brush.m_tip_color); + } + 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 }); @@ -69,10 +77,6 @@ void ui::Canvas::stroke_draw() glm::scale(glm::vec3(s.size, s.size, 1)) * glm::eulerAngleZ(s.angle); - //ShaderManager::u_mat4(kShaderUniform::MVP, mvp); - //ShaderManager::u_float(kShaderUniform::Alpha, s.flow); - //m_plane.draw_fill(); - 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 @@ -104,7 +108,6 @@ void ui::Canvas::stroke_draw() tex_pos.x, tex_pos.y, tex_pos.x, tex_pos.y, tex_sz.x, tex_sz.y); -// glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 10, 10, 10, 10, 200, 200); ShaderManager::u_mat4(kShaderUniform::MVP, mvp); ShaderManager::u_float(kShaderUniform::Alpha, s.flow); @@ -153,10 +156,17 @@ void ui::Canvas::stroke_commit() m_tex2.bind(); m_sampler.bind(0); m_sampler_bg.bind(1); - ShaderManager::use(ui::kShader::StrokeLayer); + 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_int(kShaderUniform::TexBG, 1); - ShaderManager::u_float(kShaderUniform::Alpha, m_current_stroke->m_brush.m_tip_opacity); ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); m_plane.draw_fill(); m_sampler.unbind(); @@ -183,9 +193,20 @@ void ui::Canvas::stroke_start(glm::vec2 point, float pressure, const ui::Brush& m_strokes.back().add_point(point, pressure); m_current_stroke = &m_strokes.back(); - m_tmp.bindFramebuffer(); - m_tmp.clear({ 0, 0, 0, 0 }); - m_tmp.unbindFramebuffer(); + if (m_erase) + { + m_layers[m_current_layer_idx].m_rtt.bindFramebuffer(); + m_tmp.bindTexture(); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height); + m_tmp.unbindTexture(); + m_layers[m_current_layer_idx].m_rtt.unbindFramebuffer(); + } + else + { + m_tmp.bindFramebuffer(); + m_tmp.clear({ 0, 0, 0, 0 }); + m_tmp.unbindFramebuffer(); + } m_show_tmp = true; } void ui::Canvas::layer_add(std::string name) diff --git a/engine/canvas.h b/engine/canvas.h index b6ae10a..225a9a3 100644 --- a/engine/canvas.h +++ b/engine/canvas.h @@ -13,6 +13,8 @@ class Canvas BrushMesh m_mesh; bool m_dirty = false; public: + bool m_erase = false; + glm::mat4 m_mvp; int m_width; int m_height; bool m_use_instanced = false; @@ -36,7 +38,7 @@ public: void stroke_draw(); void stroke_end(); void stroke_commit(); - void clear(const glm::vec4& color = { 0, 0, 0, 1 }); + void clear(const glm::vec4& color = { 1, 1, 1, 1 }); }; NS_END diff --git a/engine/layout.cpp b/engine/layout.cpp index 4cac4f3..8b0a37b 100644 --- a/engine/layout.cpp +++ b/engine/layout.cpp @@ -69,6 +69,8 @@ kEventResult Node::on_event(Event* e) } break; default: + if (handle_event(e) == kEventResult::Consumed) + return kEventResult::Consumed; break; } break; diff --git a/engine/layout.h b/engine/layout.h index c8bdc3d..a102b8a 100644 --- a/engine/layout.h +++ b/engine/layout.h @@ -1101,6 +1101,13 @@ public: } void set_value(float value) { + m_value = glm::vec2(value) * m_mask; + if (on_value_changed) + on_value_changed(this, glm::length(m_value)); + } + float get_value() + { + return glm::length(m_value); } virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override { @@ -1183,6 +1190,10 @@ public: on_hue_changed(this, m_color); }; } + glm::vec4 get_hue() + { + return m_color; + } virtual void draw() override { using namespace ui; @@ -1605,6 +1616,15 @@ public: m_picker->m_color = glm::vec4(0); add_child(m_picker); } + void set_value(float x, float y) + { + auto sz = GetSize(); + auto pos = glm::clamp(glm::vec2(x, y) * sz, { 0, 0 }, sz); + m_picker->SetPosition(pos.x, pos.y); + m_value = pos / glm::max({ 1,1 }, sz); // avoid div0 + if (on_value_changed) + on_value_changed(this, m_value); + } virtual kEventResult handle_event(Event* e) override { NodeBorder::handle_event(e); @@ -1692,6 +1712,7 @@ public: if (on_color_changed) on_color_changed(this, m_color); }; + m_hue->set_value(0); } }; @@ -1754,7 +1775,7 @@ public: if (true) { m_mesh.shader.use(); - m_mesh.shader.u_vec4(kShaderUniform::Col, m_brush.m_tip_color); + m_mesh.shader.u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); m_mesh.shader.u_int(kShaderUniform::Tex, 0); m_mesh.draw(samples, proj); } @@ -1876,6 +1897,7 @@ class NodeCanvas : public Node glm::vec2 m_dragR_start; glm::vec2 m_pan_start; glm::vec2 m_pan; + float m_zoom_canvas = 1.f; public: std::unique_ptr m_canvas; ui::Brush m_brush; @@ -1889,16 +1911,10 @@ public: m_canvas->layer_add("asd"); m_canvas->clear(); m_sampler.create(); -// SetPositioning(YGPositionTypeAbsolute); -// SetPosition(100, 100); -// SetSize({ 512, 512 }); } virtual void draw() override { using namespace ui; - //glm::mat4 cam = glm::lookAt(glm::vec3(sinf(angle) * 10, 0, -10), glm::vec3(0, 0, 0), glm::vec3(0, -1, 0)); - //glm::mat4 proj = glm::perspective(glm::radians(45.f), m_clip.z / m_clip.w, .1f, 100); - GLint vp[4]; GLfloat cc[4]; glGetIntegerv(GL_VIEWPORT, vp); @@ -1912,26 +1928,32 @@ public: glViewport(c.x, c.y, c.z, c.w); glm::vec2 sz = { m_canvas->m_width, m_canvas->m_height }; - auto mvp = glm::ortho(0.f, box.z, 0.f, box.w, -1.f, 1.f) * - //glm::translate(glm::vec3((m_size - sz) * 0.5f, 0)) * // center - glm::translate(glm::vec3(m_pan, 0)) * // corner - glm::scale(glm::vec3(sz * zoom, 1)) * - glm::translate(glm::vec3(.5f, .5f, 0.f)); // pivot + m_canvas->m_mvp = glm::ortho(0.f, box.z, 0.f, box.w, -1.f, 1.f) * + glm::translate(glm::vec3(m_pan + m_size * 0.5f, 0)) * // pan + glm::scale(glm::vec3(zoom * m_zoom_canvas, zoom * m_zoom_canvas, 1)) * + glm::translate(glm::vec3(-sz/2.f, 0)); + + auto plane_mvp = glm::ortho(0.f, box.z, 0.f, box.w, -1.f, 1.f) * + glm::translate(glm::vec3(m_pan + m_size * 0.5f, 0)) * // pan + glm::scale(glm::vec3(sz * zoom * m_zoom_canvas, 1)); m_sampler.bind(0); ui::ShaderManager::use(kShader::TextureAlpha); ui::ShaderManager::u_int(kShaderUniform::Tex, 0); - ui::ShaderManager::u_mat4(kShaderUniform::MVP, mvp); + ui::ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); auto blend = glIsEnabled(GL_BLEND); glEnable(GL_BLEND); for (int i = 0; i < m_canvas->m_layers.size(); i++) { - ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[i].m_opacity); - m_canvas->m_layers[i].m_rtt.bindTexture(); - NodeBorder::m_plane.draw_fill(); - m_canvas->m_layers[i].m_rtt.unbindTexture(); + if (!(m_canvas->m_erase && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == i)) + { + ui::ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[i].m_opacity); + m_canvas->m_layers[i].m_rtt.bindTexture(); + NodeBorder::m_plane.draw_fill(); + m_canvas->m_layers[i].m_rtt.unbindTexture(); + } if (m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == i) { glEnable(GL_BLEND); @@ -1959,8 +1981,12 @@ public: { Node::handle_event(e); MouseEvent* me = static_cast(e); + KeyEvent* ke = static_cast(e); auto loc = me->m_pos - m_pos; - auto cur = glm::vec2(loc.x, m_canvas->m_height - loc.y - 1); + 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 = fb_space.xy(); + switch (e->m_type) { case kEventType::MouseDownL: @@ -1991,6 +2017,16 @@ public: if (m_draggingR) m_pan = m_pan_start + (me->m_pos - m_dragR_start) * glm::vec2(1, -1); break; + case kEventType::MouseScroll: + m_zoom_canvas += me->m_scroll_delta * 0.1f; + break; + case kEventType::KeyDown: + if (ke->m_key == 'E') + m_canvas->m_erase = true; + break; + case kEventType::KeyUp: + if (ke->m_key == 'E') + m_canvas->m_erase = false; default: break; } diff --git a/engine/shader.h b/engine/shader.h index f425ab6..a6cf823 100644 --- a/engine/shader.h +++ b/engine/shader.h @@ -27,6 +27,7 @@ enum class kShader : uint16_t Font = const_hash("font"), Atlas = const_hash("atlas"), Stroke = const_hash("stroke"), + StrokeErase = const_hash("stroke-erase"), StrokeLayer = const_hash("stroke-layer"), };