From e4fe0cffedf5838539e31177842a2e4deea7eeae Mon Sep 17 00:00:00 2001 From: omigamedev Date: Sat, 5 Jan 2019 12:41:21 +0100 Subject: [PATCH] Improved mask also work on erase. Improved shaders for layer opacity, now when drawing doesn't change opacity. Improved layers panel layout to be similar to PS. Added layer blending option and visibility. Added custom icons to checkboxes and fixed the combobox items. --- data/layout.xml | 33 ++++++---- data/ui/check-layer-visibility.png | Bin 0 -> 2998 bytes data/ui/check-lock-transparency.png | Bin 0 -> 892 bytes data/ui/check-test.png | Bin 0 -> 1888 bytes extra/ui/checkbox-icons.psd | Bin 0 -> 94396 bytes src/app.cpp | 5 +- src/app_layout.cpp | 94 +++++++++++++++------------- src/app_shaders.cpp | 27 ++++++-- src/app_vr.cpp | 12 ++-- src/canvas.cpp | 19 ++++-- src/canvas.h | 1 + src/canvas_modes.cpp | 1 + src/node_canvas.cpp | 12 ++-- src/node_checkbox.cpp | 45 +++++++++++++ src/node_checkbox.h | 7 +++ src/node_combobox.cpp | 14 +++++ src/node_combobox.h | 1 + src/node_image.cpp | 16 +++++ src/node_image.h | 1 + src/node_panel_layer.cpp | 44 +++++++++++-- src/node_panel_layer.h | 11 +++- src/node_slider.cpp | 4 +- src/shader.h | 1 + 23 files changed, 255 insertions(+), 93 deletions(-) create mode 100644 data/ui/check-layer-visibility.png create mode 100644 data/ui/check-lock-transparency.png create mode 100644 data/ui/check-test.png create mode 100644 extra/ui/checkbox-icons.psd diff --git a/data/layout.xml b/data/layout.xml index 2f24734..3bcb434 100644 --- a/data/layout.xml +++ b/data/layout.xml @@ -35,15 +35,14 @@ - - - - - - - - - + + + + + + + + @@ -54,6 +53,17 @@ + + + + + + + + + + + @@ -69,9 +79,6 @@ - - - @@ -1116,7 +1123,7 @@ Here's a list of what's available in this release. - + diff --git a/data/ui/check-layer-visibility.png b/data/ui/check-layer-visibility.png new file mode 100644 index 0000000000000000000000000000000000000000..7328d2c4c69343f5cb0df5c67d3843aa86c7ce67 GIT binary patch literal 2998 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 AbpQYW literal 0 HcmV?d00001 diff --git a/data/ui/check-lock-transparency.png b/data/ui/check-lock-transparency.png new file mode 100644 index 0000000000000000000000000000000000000000..2267b436652e0a86f9a447a7263eede7f8a9fe30 GIT binary patch literal 892 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!3HERU8}DLQY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiYCyFu3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG?d}4kf#9d}Zjfo5 z1s;*b3=G^tAk28_ZrvXS2Brs|E{-7;x8BaN_by75IJVx!qS=v!ud}X^ulMB@ktfey zJujG~Aj4YvD!HX)a&VI1znLXhVzM$h{yQ9M4BD7e^>}TZ#-8~#ulBCLzl43>?_E#- z>^XnX{alpY{CEcktGUy3+!qE2tWDpib6-8mx6W3?!4eJC-RAw1zvXuBl+&p(@$tz< zGbL8O{QOgZgJr|kt*yPiyo>(^F*MJaGiO1FmgM^1fBx{C|NNj*&S{~*p@$nb-rB<= zvFL7I{ppkS=kMN?J@$Cx#ww8m0#A!7QzQS@-H*`aO6Zt9d-l{+TN@jR)kmutoNesv z>MX)^#O!0gPd&RJZ1vY?e=lF2ym6l;;}mDcDVw)N=(zl6->`T7-{4R8`!gAC+`Vf$ zX}*t|z0B|8%87M#ehg0o@S+0i8v=jphrYCkw=mY3uXNn6u(y8jr#WSz`U`L7EQ(s&l~usR@zLgd z)VC5V(d!EsPAx=25BVLZaj-QP#ELUqcw1&D)w|})LCyru>!lAZ_+*Y(iXLPWIUv;c z_|NCBtB|!p*bhH5E;)buyY6>cD+`N?&-;Q_>V&FKKYj9-_*ymZ&)?(z<(O^0#?HFI zW&7=;IcA4TtX!90-gu4uJ5!5A-{vd!#cqoQ_4M^!Z@zy0`q4j2#ugVP!R(tCGeTmT znK*9VzP)+cT(6}^Of?!NynUPd=KcHiHs4>o;OKO5ifRAOz}9j6afgdihKbaUy?g&| z+x^Qyp&>%2EkrBz$rEOg0}|_A>up`H|5w(DOP>+N#}KDiGQJml;8$4})CI~2p00i_ I>zopr07rw9IsgCw literal 0 HcmV?d00001 diff --git a/data/ui/check-test.png b/data/ui/check-test.png new file mode 100644 index 0000000000000000000000000000000000000000..d0a8463115456306ca94329205bbfe17e1e2ec56 GIT binary patch literal 1888 zcmV-m2cP(fP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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 JzA9m_canvas->m_order) - { - auto* l = layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str()); - l->m_opacity->m_value.x = canvas->m_canvas->m_layers[i].m_opacity; - } + layers->add_layer(canvas->m_canvas->m_layers[i].m_name.c_str()); async_end(); } else diff --git a/src/app_layout.cpp b/src/app_layout.cpp index b2cbd9d..d72f080 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -88,6 +88,26 @@ void App::init_toolbar_main() } } +template std::shared_ptr find_or_create_panel(NodeScroll* panels) +{ + std::shared_ptr ret; + auto node_find = std::find_if(panels->m_children.begin(), panels->m_children.end(), + [](const std::shared_ptr&p) { return (bool)std::dynamic_pointer_cast(p); }); + if (node_find != panels->m_children.end()) + { + ret = std::static_pointer_cast(*node_find); + } + else + { + ret = std::make_shared(); + ret->m_manager = panels->m_manager; + ret->init(); + ret->create(); + ret->loaded(); + } + return ret; +} + void App::init_sidebar() { sidebar = layout[main_id]->find("sidebar"); @@ -99,50 +119,12 @@ void App::init_sidebar() //color = layout[main_id]->find("panel-color"); //stroke = layout[main_id]->find("panel-stroke"); - brushes = std::make_shared(); - brushes->m_manager = &layout; - brushes->init(); - brushes->create(); - brushes->loaded(); - - layers = std::make_shared(); - layers->m_manager = &layout; - layers->init(); - layers->create(); - layers->loaded(); - - color = std::make_shared(); - color->m_manager = &layout; - color->init(); - color->create(); - color->loaded(); - - stroke = std::make_shared(); - stroke->m_manager = &layout; - stroke->init(); - stroke->create(); - stroke->loaded(); - - auto grid_find = std::find_if(panels->m_children.begin(), panels->m_children.end(), - [](const std::shared_ptr&p) { return (bool)std::dynamic_pointer_cast(p); }); - if (grid_find != panels->m_children.end()) - { - grid = std::static_pointer_cast(*grid_find); - } - else - { - grid = std::make_shared(); - grid->m_manager = &layout; - grid->init(); - grid->create(); - grid->loaded(); - } - - presets = std::make_shared(); - presets->m_manager = &layout; - presets->init(); - presets->create(); - presets->loaded(); + brushes = find_or_create_panel(panels); + layers = find_or_create_panel(panels); + color = find_or_create_panel(panels); + stroke = find_or_create_panel(panels); + grid = find_or_create_panel(panels); + presets = find_or_create_panel(panels); // if (canvas) // { @@ -175,6 +157,8 @@ void App::init_sidebar() layers->on_layer_add = [this](Node*) { canvas->m_canvas->layer_add(layers->m_layers.back()->m_label_text.c_str()); + canvas->m_canvas->m_unsaved = true; + title_update(); }; layers->on_layer_change = [this](Node*, int old_idx, int new_idx) { @@ -183,18 +167,38 @@ void App::init_sidebar() layers->on_layer_order = [this](Node*, int old_idx, int new_idx) { canvas->m_canvas->layer_order(old_idx, new_idx); + canvas->m_canvas->m_unsaved = true; + title_update(); }; layers->on_layer_delete = [this](Node*, int idx) { canvas->m_canvas->layer_remove(idx); + canvas->m_canvas->m_unsaved = true; + title_update(); }; layers->on_layer_opacity_changed = [this](Node*, int idx, float value) { canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_opacity = value; + canvas->m_canvas->m_unsaved = true; + title_update(); }; layers->on_layer_visibility_changed = [this](Node*, int idx, bool visible) { - canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_alpha_locked = visible; + canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_visible = visible; + canvas->m_canvas->m_unsaved = true; + title_update(); + }; + + layers->on_layer_alpha_lock_changed = [this](Node*, int idx, bool locked) { + canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_alpha_locked = locked; + canvas->m_canvas->m_unsaved = true; + title_update(); + }; + + layers->on_layer_blend_mode_changed = [this](Node*, int idx, int mode) { + canvas->m_canvas->m_layers[canvas->m_canvas->m_order[idx]].m_blend_mode = mode; + canvas->m_canvas->m_unsaved = true; + title_update(); }; layers->on_layer_highlight_changed = [this](Node*, int idx, bool highlight) { diff --git a/src/app_shaders.cpp b/src/app_shaders.cpp index 400431b..a602480 100644 --- a/src/app_shaders.cpp +++ b/src/app_shaders.cpp @@ -77,17 +77,32 @@ void App::initShaders() SHADER_VERSION "uniform sampler2D tex;\n" "uniform sampler2D tex_stroke;\n" + "uniform sampler2D tex_mask;\n" "uniform mediump float alpha;\n" + "uniform mediump float stroke_alpha;\n" "uniform mediump vec2 resolution;\n" "uniform bool fragUV2;\n" + "uniform bool mask;\n" "in mediump vec2 uv;\n" "out mediump vec4 frag;\n" + "mediump vec4 blur(sampler2D t, mediump vec2 uv){\n" + " mediump vec4 sum = texture(t, uv);\n" + " sum += textureOffset(t, uv, ivec2(-1, -1));\n" + " sum += textureOffset(t, uv, ivec2(-1, 0));\n" + " sum += textureOffset(t, uv, ivec2(-1, 1));\n" + " sum += textureOffset(t, uv, ivec2( 0, -1));\n" + " sum += textureOffset(t, uv, ivec2( 0, 1));\n" + " sum += textureOffset(t, uv, ivec2( 1, -1));\n" + " sum += textureOffset(t, uv, ivec2( 1, 0));\n" + " sum += textureOffset(t, uv, ivec2( 1, 1));\n" + " return sum / vec4(9.0);\n" + "}\n" "void main(){\n" " mediump vec2 uv2 = fragUV2 ? (gl_FragCoord.st / resolution) : uv;\n" " mediump vec4 base = texture(tex, uv2);\n" " mediump vec4 stroke = texture(tex_stroke, uv);\n" - " mediump float a = base.a - (stroke.a * alpha);\n" - " frag = vec4(base.rgb, clamp(a, 0.0, 1.0));\n" + " stroke.a = mask ? stroke.a * stroke_alpha * blur(tex_mask, uv2).r : stroke.a * stroke_alpha;\n" + " frag = vec4(base.rgb, clamp((base.a - stroke.a) * alpha, 0.0, 1.0));\n" "}\n"; // TEXTURE COMP DRAW static const char* shader_comp_draw_f = @@ -97,6 +112,7 @@ void App::initShaders() "uniform sampler2D tex_mask;\n" "uniform sampler2D tex_stencil;\n" "uniform mediump float alpha;\n" + "uniform mediump float stroke_alpha;\n" "uniform mediump int blend_mode;\n" "uniform mediump vec2 resolution;\n" "uniform bool lock;\n" @@ -141,13 +157,12 @@ void App::initShaders() " mediump vec2 uv2 = fragUV2 ? (gl_FragCoord.st / resolution) : uv;\n" " mediump vec4 base = texture(tex, uv2);\n" " mediump vec4 stroke = texture(tex_stroke, uv);\n" - " stroke.a = mask ? stroke.a * alpha * blur(tex_mask, uv2).r : stroke.a * alpha;\n" - - " if (!lock && base.a == 0.0) { frag = stroke; return; }\n" + " stroke.a = mask ? stroke.a * stroke_alpha * blur(tex_mask, uv2).r : stroke.a * stroke_alpha;\n" + " if (!lock && base.a == 0.0) { frag = stroke * vec4(1.0, 1.0, 1.0, alpha); return; }\n" " mediump float contribution = (1.0 - base.a) * stroke.a;\n" " mediump float alpha_tot = base.a + contribution;" " mediump vec3 rgb = blend(base, stroke, alpha_tot, blend_mode);\n" - " frag = vec4(rgb, (lock ? base.a : alpha_tot));\n" + " frag = vec4(rgb, (lock ? base.a : alpha_tot) * alpha);\n" "}\n"; // TEXTURE ATLAS diff --git a/src/app_vr.cpp b/src/app_vr.cpp index c4a7330..e7fde59 100644 --- a/src/app_vr.cpp +++ b/src/app_vr.cpp @@ -46,7 +46,7 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat auto layer_index = canvas->m_canvas->m_order[i]; for (int plane_index = 0; plane_index < 6; plane_index++) { - if (canvas->m_canvas->m_layers[layer_index].m_opacity == .0f) + if (!canvas->m_canvas->m_layers[layer_index].m_visible || canvas->m_canvas->m_layers[layer_index].m_opacity == .0f) continue; int z = (int)(canvas->m_canvas->m_order.size() - i); @@ -62,10 +62,11 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat ShaderManager::use(kShader::CompErase); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::TexStroke, 1); - //ShaderManager::u_int(kShaderUniform::TexMask, 2); - ShaderManager::u_float(kShaderUniform::Alpha, canvas->m_canvas->m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_int(kShaderUniform::TexMask, 2); + ShaderManager::u_float(kShaderUniform::StrokeAlpha, canvas->m_canvas->m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::Alpha, canvas->m_canvas->m_layers[layer_index].m_opacity); //ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[layer_index].m_alpha_locked); - //ShaderManager::u_int(kShaderUniform::Mask, m_canvas->m_smask_active); + ShaderManager::u_int(kShaderUniform::Mask, canvas->m_canvas->m_smask_active); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); glActiveTexture(GL_TEXTURE0); canvas->m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture(); @@ -90,7 +91,8 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat ShaderManager::u_int(kShaderUniform::TexMask, 2); ShaderManager::u_vec2(kShaderUniform::Resolution, canvas->m_canvas->m_size); //ShaderManager::u_int(kShaderUniform::TexStencil, 3); - ShaderManager::u_float(kShaderUniform::Alpha, canvas->m_canvas->m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::StrokeAlpha, canvas->m_canvas->m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::Alpha, canvas->m_canvas->m_layers[layer_index].m_opacity); ShaderManager::u_int(kShaderUniform::Lock, canvas->m_canvas->m_layers[layer_index].m_alpha_locked); ShaderManager::u_int(kShaderUniform::Mask, canvas->m_canvas->m_smask_active); ShaderManager::u_int(kShaderUniform::BlendMode, canvas->m_canvas->m_current_stroke->m_brush.m_blend_mode); diff --git a/src/canvas.cpp b/src/canvas.cpp index 097f7cf..5c819bd 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -91,6 +91,8 @@ void Canvas::pick_update(int plane) m_sampler.bind(0); for (auto layer_index : m_order) { + if (!m_layers[layer_index].m_visible || m_layers[layer_index].m_opacity == 0.f) + continue; ShaderManager::u_float(kShaderUniform::Alpha, m_layers[layer_index].m_opacity); m_layers[layer_index].m_rtt[i].bindTexture(); m_plane.draw_fill(); @@ -242,7 +244,7 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) auto layer_index = m_current_layer_idx; for (int plane_index = 0; plane_index < 6; plane_index++) { - if (m_layers[layer_index].m_opacity == .0f) + if (!m_layers[layer_index].m_visible || m_layers[layer_index].m_opacity == .0f) continue; glm::mat4 proj = glm::perspective(glm::radians(m_cam_fov), (float)m_mixer.getWidth() / m_mixer.getHeight(), 0.1f, 1000.f); @@ -293,7 +295,8 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) ShaderManager::u_int(kShaderUniform::TexMask, 2); //ShaderManager::u_vec2(kShaderUniform::Resolution, m_size); //ShaderManager::u_int(kShaderUniform::TexStencil, 3); - ShaderManager::u_float(kShaderUniform::Alpha, m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::StrokeAlpha, m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::Alpha, 1); ShaderManager::u_int(kShaderUniform::Lock, m_layers[layer_index].m_alpha_locked); ShaderManager::u_int(kShaderUniform::Mask, m_smask_active); ShaderManager::u_int(kShaderUniform::UseFragCoordUV2, false); @@ -804,9 +807,11 @@ void Canvas::stroke_commit() ShaderManager::use(kShader::CompErase); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::TexStroke, 1); - //ShaderManager::u_int(kShaderUniform::TexMask, 2); + ShaderManager::u_int(kShaderUniform::TexMask, 2); + ShaderManager::u_int(kShaderUniform::Mask, m_smask_active); ShaderManager::u_int(kShaderUniform::UseFragCoordUV2, false); - ShaderManager::u_float(kShaderUniform::Alpha, m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::StrokeAlpha, m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::Alpha, 1); ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); glActiveTexture(GL_TEXTURE0); @@ -831,7 +836,8 @@ void Canvas::stroke_commit() ShaderManager::u_int(kShaderUniform::TexStroke, 1); ShaderManager::u_int(kShaderUniform::TexMask, 2); //ShaderManager::u_vec2(kShaderUniform::Resolution, m_size); - ShaderManager::u_float(kShaderUniform::Alpha, m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::StrokeAlpha, m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::Alpha, 1); ShaderManager::u_int(kShaderUniform::Mask, m_smask_active); ShaderManager::u_int(kShaderUniform::UseFragCoordUV2, false); ShaderManager::u_int(kShaderUniform::BlendMode, m_current_stroke->m_brush.m_blend_mode); @@ -1009,6 +1015,7 @@ void Canvas::layer_merge(int source_idx, int dest_idx) // m_layer index ShaderManager::u_int(kShaderUniform::TexStroke, 1); // source //ShaderManager::u_vec2(kShaderUniform::Resolution, m_size); ShaderManager::u_int(kShaderUniform::UseFragCoordUV2, false); + ShaderManager::u_float(kShaderUniform::StrokeAlpha, 1); ShaderManager::u_float(kShaderUniform::Alpha, m_layers[source_idx].m_opacity); ShaderManager::u_int(kShaderUniform::Lock, false); ShaderManager::u_int(kShaderUniform::BlendMode, 0); // TODO: defaulted to normal, change to layer blend mode when implemented @@ -1284,6 +1291,8 @@ void Canvas::export_equirectangular_thread(std::string file_path) m_sampler_mask.bind(0); for (auto layer_index : m_order) { + if (!m_layers[layer_index].m_visible || m_layers[layer_index].m_opacity == 0.f) + continue; ShaderManager::u_float(kShaderUniform::Alpha, m_layers[layer_index].m_opacity); m_layers[layer_index].m_rtt[i].bindTexture(); m_plane.draw_fill(); diff --git a/src/canvas.h b/src/canvas.h index 42034c3..e6aa04e 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -20,6 +20,7 @@ public: bool m_alpha_locked = false; float m_opacity = 1.f; bool m_hightlight = false; + bool m_blend_mode = 0; std::string m_name; int w = 0; int h = 0; diff --git a/src/canvas_modes.cpp b/src/canvas_modes.cpp index 0df2fc1..7afc7d6 100644 --- a/src/canvas_modes.cpp +++ b/src/canvas_modes.cpp @@ -1112,6 +1112,7 @@ void CanvasModeTransform::leave() ShaderManager::u_int(kShaderUniform::TexStroke, 1); ShaderManager::u_int(kShaderUniform::TexMask, 2); ShaderManager::u_float(kShaderUniform::Alpha, 1); + ShaderManager::u_float(kShaderUniform::StrokeAlpha, 1); ShaderManager::u_int(kShaderUniform::Lock, false); ShaderManager::u_int(kShaderUniform::Mask, false); ShaderManager::u_int(kShaderUniform::UseFragCoordUV2, true); diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index 878585a..2351f3b 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -137,7 +137,7 @@ void NodeCanvas::draw() auto layer_index = m_canvas->m_order[i]; for (int plane_index = 0; plane_index < 6; plane_index++) { - if (m_canvas->m_layers[layer_index].m_opacity == .0f) + if (!m_canvas->m_layers[layer_index].m_visible || m_canvas->m_layers[layer_index].m_opacity == .0f) continue; int z = (int)(m_canvas->m_order.size() - i); @@ -153,11 +153,12 @@ void NodeCanvas::draw() ShaderManager::use(kShader::CompErase); ShaderManager::u_int(kShaderUniform::Tex, 0); ShaderManager::u_int(kShaderUniform::TexStroke, 1); - //ShaderManager::u_int(kShaderUniform::TexMask, 2); - ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_int(kShaderUniform::TexMask, 2); + ShaderManager::u_float(kShaderUniform::StrokeAlpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index].m_opacity); ShaderManager::u_int(kShaderUniform::UseFragCoordUV2, false); //ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[layer_index].m_alpha_locked); - //ShaderManager::u_int(kShaderUniform::Mask, m_canvas->m_smask_active); + ShaderManager::u_int(kShaderUniform::Mask, m_canvas->m_smask_active); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); glActiveTexture(GL_TEXTURE0); m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture(); @@ -182,7 +183,8 @@ void NodeCanvas::draw() ShaderManager::u_int(kShaderUniform::TexMask, 2); //ShaderManager::u_vec2(kShaderUniform::Resolution, zw(m_canvas->m_box) / zoom); //ShaderManager::u_int(kShaderUniform::TexStencil, 3); - ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::StrokeAlpha, m_canvas->m_current_stroke->m_brush.m_tip_opacity); + ShaderManager::u_float(kShaderUniform::Alpha, m_canvas->m_layers[layer_index].m_opacity); ShaderManager::u_int(kShaderUniform::Lock, m_canvas->m_layers[layer_index].m_alpha_locked); ShaderManager::u_int(kShaderUniform::Mask, m_canvas->m_smask_active); ShaderManager::u_int(kShaderUniform::UseFragCoordUV2, false); diff --git a/src/node_checkbox.cpp b/src/node_checkbox.cpp index b549c50..4e89700 100644 --- a/src/node_checkbox.cpp +++ b/src/node_checkbox.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "log.h" #include "node_checkbox.h" +#include "texture.h" Node* NodeCheckBox::clone_instantiate() const { @@ -13,7 +14,9 @@ void NodeCheckBox::clone_children(Node* dest) const NodeCheckBox* n = static_cast(dest); n->m_outer = (NodeBorder*)n->m_children[0].get(); n->m_inner = (NodeBorder*)n->m_outer->m_children[0].get(); + n->m_icon = n->m_inner->m_children.empty() ? nullptr : (NodeImage*)n->m_inner->m_children[0].get(); n->m_mouse_ignore = false; + n->m_icon_path = m_icon_path; } void NodeCheckBox::init() @@ -43,6 +46,8 @@ void NodeCheckBox::create() { m_outer->create(); m_inner->create(); + if (!m_icon_path.empty()) + set_icon(m_icon_path); } kEventResult NodeCheckBox::handle_event(Event* e) @@ -58,6 +63,7 @@ kEventResult NodeCheckBox::handle_event(Event* e) break; case kEventType::MouseUpL: checked = !checked; + update_icon(); if (on_value_changed) on_value_changed(this, checked); break; @@ -73,3 +79,42 @@ void NodeCheckBox::draw() m_inner->m_color = checked ? glm::vec4(.4, .4, .4, 1) : glm::vec4(.8, .8, .8, 1); Node::draw(); } + +void NodeCheckBox::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) +{ + switch (ka) + { + case kAttribute::Icon: + m_icon_path = attr->Value(); + break; + default: + Node::parse_attributes(ka, attr); + break; + } +} + +void NodeCheckBox::set_icon(const std::string& icon_path) +{ + if (icon_path.empty() || !TextureManager::load(icon_path.c_str())) + { + if (m_icon) + m_icon->destroy(); + m_icon = nullptr; + return; + } + m_icon_path = icon_path; + update_icon(); +} + +void NodeCheckBox::update_icon() +{ + auto& t = TextureManager::get(const_hash(m_icon_path.c_str())); + if (!m_icon) + m_icon = m_inner->add_child(); + auto region = checked ? glm::vec4(.5f, 0, 1, 1) : glm::vec4(0, 0, .5f, 1); + m_icon->m_region = region * glm::vec4(t.size(), t.size()); + m_icon->m_use_atlas = true; + m_icon->set_image(m_icon_path); + m_icon->SetWidthP(100); + m_icon->SetHeightP(100); +} diff --git a/src/node_checkbox.h b/src/node_checkbox.h index c7e5fd1..9b45b88 100644 --- a/src/node_checkbox.h +++ b/src/node_checkbox.h @@ -1,6 +1,7 @@ #pragma once #include "node.h" #include "node_border.h" +#include "node_image.h" class NodeCheckBox : public Node { @@ -8,6 +9,8 @@ public: std::function on_value_changed; NodeBorder* m_outer; NodeBorder* m_inner; + NodeImage* m_icon; + std::string m_icon_path; bool checked = false; virtual Node* clone_instantiate() const override; virtual void clone_children(Node* dest) const override; @@ -15,4 +18,8 @@ public: virtual void create() override; virtual kEventResult handle_event(Event* e) override; virtual void draw() override; + virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override; + void set_icon(const std::string& icon_path); + void set_value(bool checked) { this->checked = checked; update_icon(); } + void update_icon(); }; diff --git a/src/node_combobox.cpp b/src/node_combobox.cpp index 1995a1b..fefa424 100644 --- a/src/node_combobox.cpp +++ b/src/node_combobox.cpp @@ -13,7 +13,9 @@ void NodeComboBox::clone_copy(Node* dest) const NodeButton::clone_copy(dest); NodeComboBox* n = static_cast(dest); n->m_data = m_data; + n->m_items = m_items; n->m_current_index = m_current_index; + n->m_selected_child_index = m_selected_child_index; } void NodeComboBox::loaded() @@ -85,6 +87,9 @@ void NodeComboBox::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* case kAttribute::ComboList: { m_data = split(attr->Value(), ','); + m_items.clear(); + for (auto& i : m_data) + if (i != "-") m_items.push_back(i); break; } case kAttribute::Default: @@ -94,3 +99,12 @@ void NodeComboBox::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* break; } } + +void NodeComboBox::set_index(int index) +{ + m_current_index = index; + m_selected_child_index = index; + m_text->set_text(m_items[index].c_str()); + //if (on_select) + // on_select(this, index); +} diff --git a/src/node_combobox.h b/src/node_combobox.h index a505a90..72230b4 100644 --- a/src/node_combobox.h +++ b/src/node_combobox.h @@ -13,4 +13,5 @@ public: virtual void clone_copy(Node* dest) const override; virtual void loaded() override; virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override; + void set_index(int index); }; diff --git a/src/node_image.cpp b/src/node_image.cpp index 18e803f..1f68214 100644 --- a/src/node_image.cpp +++ b/src/node_image.cpp @@ -105,3 +105,19 @@ void NodeImage::draw() TextureManager::get(m_tex_id).unbind(); glDisable(GL_BLEND); } + +bool NodeImage::set_image(const std::string& path) +{ + m_path = path; + m_tex_id = const_hash(path.c_str()); + if (!m_path.empty() && TextureManager::load(m_path.c_str(), m_use_mipmaps)) + { + auto tex_sz = TextureManager::get(m_tex_id).size(); + m_off = xy(m_region) / tex_sz; + m_sz = (zw(m_region) - xy(m_region)) / tex_sz; + if (m_autosize) + SetAspectRatio(tex_sz.x / tex_sz.y); + return true; + } + return false; +} diff --git a/src/node_image.h b/src/node_image.h index 1280607..e4bdeaa 100644 --- a/src/node_image.h +++ b/src/node_image.h @@ -24,4 +24,5 @@ public: virtual void restore_context() override; virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override; virtual void draw() override; + bool set_image(const std::string& path); }; diff --git a/src/node_panel_layer.cpp b/src/node_panel_layer.cpp index 0c9532f..f58d40c 100644 --- a/src/node_panel_layer.cpp +++ b/src/node_panel_layer.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "log.h" #include "node_panel_layer.h" +#include "canvas.h" +#include "node_combobox.h" Node* NodeLayer::clone_instantiate() const { @@ -31,7 +33,6 @@ void NodeLayer::init() m_thinkness = m_template->m_thinkness; m_label = find("label"); m_visibility = find("cb"); - m_opacity = find("sl-opacity"); } void NodeLayer::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) @@ -54,10 +55,6 @@ void NodeLayer::loaded() NodeBorder::loaded(); if (!m_label_text.empty()) m_label->set_text(m_label_text.c_str()); - m_opacity->on_value_changed = [this](Node*, float value) { - if (on_opacity_changed) - on_opacity_changed(this, value); - }; m_visibility->on_value_changed = [this](Node*, bool checked) { if (on_visibility_changed) on_visibility_changed(this, checked); @@ -159,6 +156,18 @@ void NodePanelLayer::init() on_layer_order(this, old_idx, new_idx); } }; + m_opacity = find("opacity"); + m_opacity->on_value_changed = [this](Node*, float value) { + handle_layer_opacity(m_current_layer, value); + }; + m_alpha_lock = find("alpha-lock"); + m_alpha_lock->on_value_changed = [this](Node*, bool locked) { + handle_layer_alpha_lock(m_current_layer, locked); + }; + m_blend_mode = find("blend-mode"); + m_blend_mode->on_select = [this](Node*, int index) { + handle_layer_blend_mode(m_current_layer, index); + }; LOG("done init"); } @@ -170,8 +179,8 @@ NodeLayer* NodePanelLayer::add_layer(const char* name) l->create(); l->loaded(); l->set_name(name); + l->m_visibility->set_value(true); l->on_selected = std::bind(&NodePanelLayer::handle_layer_selected, this, std::placeholders::_1); - l->on_opacity_changed = std::bind(&NodePanelLayer::handle_layer_opacity, this, std::placeholders::_1, std::placeholders::_2); l->on_visibility_changed = std::bind(&NodePanelLayer::handle_layer_visibility, this, std::placeholders::_1, std::placeholders::_2); l->on_highlight = std::bind(&NodePanelLayer::handle_layer_highlight, this, std::placeholders::_1, std::placeholders::_2); if (m_current_layer) @@ -179,6 +188,7 @@ NodeLayer* NodePanelLayer::add_layer(const char* name) m_current_layer = l; m_current_layer->m_selected = true; m_layers.push_back(l); + update_attributes(); return l; } @@ -208,6 +218,7 @@ void NodePanelLayer::remove_layer(NodeLayer* layer) on_layer_delete(this, old_idx); if (on_layer_change) on_layer_change(this, -1, i); + update_attributes(); } void NodePanelLayer::handle_layer_opacity(NodeLayer* target, float value) @@ -228,6 +239,18 @@ void NodePanelLayer::handle_layer_visibility(NodeLayer* target, bool visible) on_layer_visibility_changed(this, m_layers_container->get_child_index(target), visible); } +void NodePanelLayer::handle_layer_alpha_lock(NodeLayer* target, bool locked) +{ + if (on_layer_alpha_lock_changed) + on_layer_alpha_lock_changed(this, m_layers_container->get_child_index(target), locked); +} + +void NodePanelLayer::handle_layer_blend_mode(NodeLayer* target, int mode) +{ + if (on_layer_blend_mode_changed) + on_layer_blend_mode_changed(this, m_layers_container->get_child_index(target), mode); +} + void NodePanelLayer::handle_layer_selected(NodeLayer* target) { if (m_current_layer) @@ -236,6 +259,7 @@ void NodePanelLayer::handle_layer_selected(NodeLayer* target) m_current_layer->m_selected = true; if (on_layer_change) on_layer_change(this, -1, m_layers_container->get_child_index(m_current_layer)); + update_attributes(); } void NodePanelLayer::clear() @@ -244,3 +268,11 @@ void NodePanelLayer::clear() m_layers.clear(); m_current_layer = nullptr; } + +void NodePanelLayer::update_attributes() +{ + auto& l = Canvas::I->m_layers[Canvas::I->m_current_layer_idx]; + m_opacity->set_value(l.m_opacity); + m_alpha_lock->set_value(l.m_alpha_locked); + m_blend_mode->set_index(l.m_blend_mode); +} diff --git a/src/node_panel_layer.h b/src/node_panel_layer.h index 93330f6..f069cf2 100644 --- a/src/node_panel_layer.h +++ b/src/node_panel_layer.h @@ -4,12 +4,12 @@ #include "node_checkbox.h" #include "node_slider.h" #include "node_button_custom.h" +#include "node_combobox.h" class NodeLayer : public NodeBorder { public: std::function on_selected; - std::function on_opacity_changed; std::function on_visibility_changed; std::function on_highlight; bool m_selected = false; @@ -19,7 +19,6 @@ public: std::string m_label_text; NodeText* m_label; NodeCheckBox* m_visibility; - NodeSliderH* m_opacity; virtual Node* clone_instantiate() const override; virtual void clone_children(Node* dest) const override; virtual void clone_copy(Node* dest) const override; @@ -42,13 +41,18 @@ public: std::function on_layer_change; std::function on_layer_opacity_changed; std::function on_layer_visibility_changed; + std::function on_layer_alpha_lock_changed; std::function on_layer_highlight_changed; + std::function on_layer_blend_mode_changed; std::function on_layer_delete; std::function on_layer_add; std::function on_layer_order; NodeLayer* m_current_layer = nullptr; std::vector m_layers; NodeBorder* m_layers_container; + NodeSliderH* m_opacity; + NodeCheckBox* m_alpha_lock; + NodeComboBox* m_blend_mode; virtual Node* clone_instantiate() const override; virtual void init() override; void add_layer(); @@ -57,7 +61,10 @@ public: void remove_layer(NodeLayer* layer); void handle_layer_opacity(NodeLayer* target, float value); void handle_layer_visibility(NodeLayer* target, bool visible); + void handle_layer_alpha_lock(NodeLayer* target, bool locked); void handle_layer_highlight(NodeLayer* target, bool highlight); + void handle_layer_blend_mode(NodeLayer* target, int mode); void handle_layer_selected(NodeLayer* target); void clear(); + void update_attributes(); }; diff --git a/src/node_slider.cpp b/src/node_slider.cpp index 16aad8e..b5ace62 100644 --- a/src/node_slider.cpp +++ b/src/node_slider.cpp @@ -42,8 +42,8 @@ void NodeSliderH::draw() void NodeSliderH::set_value(float value) { m_value = glm::vec2(value) * m_mask; - if (on_value_changed) - on_value_changed(this, glm::length(m_value)); + //if (on_value_changed) + // on_value_changed(this, glm::length(m_value)); } float NodeSliderH::get_value() diff --git a/src/shader.h b/src/shader.h index 2ca307b..d103acd 100644 --- a/src/shader.h +++ b/src/shader.h @@ -16,6 +16,7 @@ enum class kShaderUniform : uint16_t StencilOffset = const_hash("stencil_offset"), StencilAlpha = const_hash("stencil_alpha"), MixAlpha = const_hash("mix_alpha"), + StrokeAlpha = const_hash("stroke_alpha"), Wet = const_hash("wet"), Lock = const_hash("lock"), Col = const_hash("col"),