diff --git a/data/layout.xml b/data/layout.xml index 5a53e8c..71fdbd0 100644 --- a/data/layout.xml +++ b/data/layout.xml @@ -312,9 +312,6 @@ - - - @@ -328,18 +325,23 @@ - + + + + + + + + + + - - - - @@ -367,6 +369,12 @@ + + + + + + @@ -377,6 +385,9 @@ + + + diff --git a/src/app_layout.cpp b/src/app_layout.cpp index 8e7f4d7..a513b68 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -132,8 +132,12 @@ void App::init_sidebar() Canvas::I->m_current_brush->load_texture(path, thumb); stroke->m_preview->draw_stroke(); }; - stroke->on_stencil_changed = [this](Node*target, const std::string& path) { - Canvas::I->m_current_brush->load_stencil(path); + stroke->on_stencil_changed = [this](Node*target, const std::string& path, const std::string& thumb) { + Canvas::I->m_current_brush->load_stencil(path, thumb); + stroke->m_preview->draw_stroke(); + }; + stroke->on_dual_changed = [this](Node*target, const std::string& path, const std::string& thumb) { + Canvas::I->m_current_brush->load_dual(path, thumb); stroke->m_preview->draw_stroke(); }; diff --git a/src/brush.cpp b/src/brush.cpp index ce4cb5e..bbf2837 100644 --- a/src/brush.cpp +++ b/src/brush.cpp @@ -315,7 +315,7 @@ bool Brush::load_dual(const std::string& path, const std::string& thumb) return true; } -bool Brush::load_stencil(const std::string& path) +bool Brush::load_stencil(const std::string& path, const std::string& thumb) { m_stencil_texture = std::make_shared(); if (!m_stencil_texture->load(path)) @@ -323,6 +323,7 @@ bool Brush::load_stencil(const std::string& path) m_stencil_texture->create_mipmaps(); m_stencil_texture->auto_destroy = true; m_stencil_path = path; + m_stencil_thumb_path = thumb; return true; } diff --git a/src/brush.h b/src/brush.h index a703a1b..c8844e5 100644 --- a/src/brush.h +++ b/src/brush.h @@ -23,10 +23,10 @@ public: std::string m_stencil_thumb_path; glm::vec4 m_tip_color{0, 0, 0, 1}; - float m_tip_size = 0; - float m_tip_spacing = 0; - float m_tip_flow = 0; - float m_tip_opacity = 0; + float m_tip_size = .25f; + float m_tip_spacing = .25; + float m_tip_flow = 1; + float m_tip_opacity = 1; float m_tip_angle = 0; float m_tip_angle_delay = 0; float m_tip_mix = 0; @@ -55,8 +55,8 @@ public: bool m_dual_enabled = false; int m_dual_blend_mode = 0; bool m_dual_randflip = false; - float m_dual_size = 0; - float m_dual_spacing = 0; + float m_dual_size = .25; + float m_dual_spacing = .25; float m_dual_scatter = 0; bool m_dual_scatter_axis = false; bool m_dual_invert = false; @@ -64,11 +64,14 @@ public: bool m_dual_flipy = false; bool m_tip_randflipx = false; bool m_tip_randflipy = false; - float m_tip_aspect = 0.5; + float m_tip_aspect = 0.5f; + float m_dual_flow = .75f; + float m_dual_opacity = 1.f; + float m_dual_rotate = .25f; bool load_texture(const std::string& path, const std::string& thumb); bool load_dual(const std::string& path, const std::string& thumb); - bool load_stencil(const std::string& path); + bool load_stencil(const std::string& path, const std::string& thumb); bool load(); }; diff --git a/src/canvas.cpp b/src/canvas.cpp index b402961..f135153 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -282,7 +282,7 @@ std::array, 6> Canvas::stroke_draw_project(std::array, 6> ret; for (int i = 0; i < 6; i++) { - auto P = poly_intersect(std::data(B), std::data(B) + 4, m_plane_shape[i]); + auto P = poly_intersect(B.data(), B.data() + 4, m_plane_shape[i]); glm::mat4 plane_camera = glm::lookAt(m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i]); int intersections = 0; for (int j = 0; j < P.size(); j++) @@ -847,7 +847,8 @@ void Canvas::stroke_commit() void Canvas::stroke_update(glm::vec3 point, float pressure) { m_current_stroke->add_point(point, pressure); - m_dual_stroke->add_point(point, pressure); + if (m_dual_stroke) + m_dual_stroke->add_point(point, pressure); } void Canvas::stroke_start(glm::vec3 point, float pressure) { @@ -869,11 +870,29 @@ void Canvas::stroke_start(glm::vec3 point, float pressure) m_current_stroke->start(m_current_brush); m_current_stroke->add_point(point, pressure); - m_dual_stroke = std::make_unique(); - m_dual_stroke->m_camera.rot = m_cam_rot; - m_dual_stroke->m_camera.fov = m_cam_fov; - m_dual_stroke->start(m_dual_brush); - m_dual_stroke->add_point(point, pressure); + // Generate a brush for the dual-brush + if (m_current_brush->m_dual_enabled) + { + auto dual_brush = std::make_shared(); + dual_brush->m_tip_flow = m_current_brush->m_dual_flow; + dual_brush->m_tip_opacity = m_current_brush->m_dual_opacity; + dual_brush->m_tip_flipx = m_current_brush->m_dual_flipx; + dual_brush->m_tip_flipy = m_current_brush->m_dual_flipy; + dual_brush->m_tip_invert = m_current_brush->m_dual_invert; + dual_brush->m_blend_mode = m_current_brush->m_dual_blend_mode; + dual_brush->m_tip_randflipx = m_current_brush->m_dual_randflip; + dual_brush->m_tip_randflipy = m_current_brush->m_dual_randflip; + dual_brush->m_tip_size = m_current_brush->m_dual_size; + dual_brush->m_tip_spacing = m_current_brush->m_dual_spacing; + dual_brush->m_jitter_spread = m_current_brush->m_dual_scatter; + dual_brush->m_jitter_angle = m_current_brush->m_dual_rotate; + dual_brush->m_tip_texture = m_current_brush->m_dual_texture; + m_dual_stroke = std::make_unique(); + m_dual_stroke->m_camera.rot = m_cam_rot; + m_dual_stroke->m_camera.fov = m_cam_fov; + m_dual_stroke->start(dual_brush); + m_dual_stroke->add_point(point, pressure); + } for (int i = 0; i < 6; i++) { @@ -884,7 +903,7 @@ void Canvas::stroke_start(glm::vec3 point, float pressure) m_tmp[i].clear({ 0, 0, 0, 0 }); m_tmp[i].unbindFramebuffer(); - if (m_current_stroke->m_brush->m_dual_enabled) + if (m_current_brush->m_dual_enabled) { m_tmp_dual[i].bindFramebuffer(); m_tmp_dual[i].clear({ 0, 0, 0, 0 }); diff --git a/src/canvas.h b/src/canvas.h index f1e065d..9e54d6e 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -200,7 +200,6 @@ public: glm::vec2 m_cur_pos; std::shared_ptr m_current_brush; - std::shared_ptr m_dual_brush; static std::vector modes[]; std::vector* m_mode = nullptr; diff --git a/src/node_panel_brush.cpp b/src/node_panel_brush.cpp index ec7655b..59a9b0c 100644 --- a/src/node_panel_brush.cpp +++ b/src/node_panel_brush.cpp @@ -563,23 +563,26 @@ bool NodePanelBrushPreset::save() i.m_jitter_val = b->m_brush->m_jitter_val; i.m_blend_mode = b->m_brush->m_blend_mode; - i.m_tip_invert = b->m_brush->m_tip_invert; - i.m_tip_flipx = b->m_brush->m_tip_flipx; - i.m_tip_flipy = b->m_brush->m_tip_flipy; - i.m_tex_enabled = b->m_brush->m_tex_enabled; - i.m_dual_enabled = b->m_brush->m_dual_enabled; - i.m_dual_blend_mode = b->m_brush->m_dual_blend_mode; - i.m_dual_randflip = b->m_brush->m_dual_randflip; - i.m_dual_size = b->m_brush->m_dual_size; - i.m_dual_spacing = b->m_brush->m_dual_spacing; - i.m_dual_scatter = b->m_brush->m_dual_scatter; - i.m_dual_scatter_axis = b->m_brush->m_dual_scatter_axis; - i.m_dual_invert = b->m_brush->m_dual_invert; - i.m_dual_flipx = b->m_brush->m_dual_flipx; - i.m_dual_flipy = b->m_brush->m_dual_flipy; - i.m_tip_randflipx = b->m_brush->m_tip_randflipx; - i.m_tip_randflipy = b->m_brush->m_tip_randflipy; - i.m_tip_aspect = b->m_brush->m_tip_aspect; + i.m_tip_invert = b->m_brush->m_tip_invert; + i.m_tip_flipx = b->m_brush->m_tip_flipx; + i.m_tip_flipy = b->m_brush->m_tip_flipy; + i.m_tex_enabled = b->m_brush->m_tex_enabled; + i.m_dual_enabled = b->m_brush->m_dual_enabled; + i.m_dual_blend_mode = b->m_brush->m_dual_blend_mode; + i.m_dual_randflip = b->m_brush->m_dual_randflip; + i.m_dual_size = b->m_brush->m_dual_size; + i.m_dual_spacing = b->m_brush->m_dual_spacing; + i.m_dual_scatter = b->m_brush->m_dual_scatter; + i.m_dual_scatter_axis = b->m_brush->m_dual_scatter_axis; + i.m_dual_invert = b->m_brush->m_dual_invert; + i.m_dual_flipx = b->m_brush->m_dual_flipx; + i.m_dual_flipy = b->m_brush->m_dual_flipy; + i.m_tip_randflipx = b->m_brush->m_tip_randflipx; + i.m_tip_randflipy = b->m_brush->m_tip_randflipy; + i.m_tip_aspect = b->m_brush->m_tip_aspect; + i.m_dual_flow = b->m_brush->m_dual_flow; + i.m_dual_opacity = b->m_brush->m_dual_opacity; + i.m_dual_rotate = b->m_brush->m_dual_rotate; fwrite(&i, sizeof(i), 1, fp); fwrite(b->m_brush->m_name.c_str(), 1, b->m_brush->m_name.size(), fp); @@ -647,21 +650,24 @@ bool NodePanelBrushPreset::restore() b->m_jitter_val = i.m_jitter_val; b->m_blend_mode = i.m_blend_mode; - b->m_tip_invert = i.m_tip_invert; - b->m_tip_flipx = i.m_tip_flipx; - b->m_tip_flipy = i.m_tip_flipy; - b->m_tex_enabled = i.m_tex_enabled; - b->m_dual_enabled = i.m_dual_enabled; - b->m_dual_blend_mode = i.m_dual_blend_mode; - b->m_dual_randflip = i.m_dual_randflip; - b->m_dual_size = i.m_dual_size; - b->m_dual_spacing = i.m_dual_spacing; - b->m_dual_scatter = i.m_dual_scatter; - b->m_dual_scatter_axis = i.m_dual_scatter_axis; - b->m_dual_invert = i.m_dual_invert; - b->m_dual_flipx = i.m_dual_flipx; - b->m_dual_flipy = i.m_dual_flipy; - b->m_tip_aspect = i.m_tip_aspect; + b->m_tip_invert = i.m_tip_invert; + b->m_tip_flipx = i.m_tip_flipx; + b->m_tip_flipy = i.m_tip_flipy; + b->m_tex_enabled = i.m_tex_enabled; + b->m_dual_enabled = i.m_dual_enabled; + b->m_dual_blend_mode = i.m_dual_blend_mode; + b->m_dual_randflip = i.m_dual_randflip; + b->m_dual_size = i.m_dual_size; + b->m_dual_spacing = i.m_dual_spacing; + b->m_dual_scatter = i.m_dual_scatter; + b->m_dual_scatter_axis = i.m_dual_scatter_axis; + b->m_dual_invert = i.m_dual_invert; + b->m_dual_flipx = i.m_dual_flipx; + b->m_dual_flipy = i.m_dual_flipy; + b->m_tip_aspect = i.m_tip_aspect; + b->m_dual_flow = i.m_dual_flow; + b->m_dual_opacity = i.m_dual_opacity; + b->m_dual_rotate = i.m_dual_rotate; b->m_name.resize(i.m_name_len); b->m_brush_path.resize(i.m_brush_path_len); @@ -681,7 +687,7 @@ bool NodePanelBrushPreset::restore() if (b->load_texture(b->m_brush_path, b->m_brush_thumb_path)) { if (!b->m_stencil_path.empty()) - b->load_stencil(b->m_stencil_path); + b->load_stencil(b->m_stencil_path, b->m_stencil_thumb_path); NodeBrushPresetItem* brush = new NodeBrushPresetItem; m_container->add_child(brush); diff --git a/src/node_panel_brush.h b/src/node_panel_brush.h index 18420a3..63603ee 100644 --- a/src/node_panel_brush.h +++ b/src/node_panel_brush.h @@ -145,6 +145,9 @@ class NodePanelBrushPreset : public Node bool m_tip_randflipx = false; bool m_tip_randflipy = false; float m_tip_aspect = 0; + float m_dual_flow = .75f; + float m_dual_opacity = 1.f; + float m_dual_rotate = .25f; }; public: std::function& brush)> on_brush_changed; diff --git a/src/node_panel_stroke.cpp b/src/node_panel_stroke.cpp index ef9d66a..5de6603 100644 --- a/src/node_panel_stroke.cpp +++ b/src/node_panel_stroke.cpp @@ -58,10 +58,13 @@ void NodePanelStroke::update_controls() m_tip_randflipx->checked = b->m_tip_randflipx; m_tip_randflipy->checked = b->m_tip_randflipy; - m_dual_size->m_value.x = b->m_dual_size; - m_dual_spacing->m_value.x = b->m_dual_spacing; + m_dual_size->m_value.x = glm::pow(b->m_dual_size, 1.f / 3.f); + m_dual_spacing->m_value.x = glm::pow(b->m_dual_spacing, 1.f / 2.f); + m_dual_flow->m_value.x = glm::pow(b->m_dual_flow, 1.f / 2.f); m_dual_scatter->m_value.x = b->m_dual_scatter; m_tip_aspect->m_value.x = b->m_tip_aspect; + m_dual_opacity->m_value.x = b->m_dual_opacity; + m_dual_rotate->m_value.x = b->m_dual_rotate; m_blend_mode->set_index(b->m_blend_mode); m_dual_blend_mode->set_index(b->m_dual_blend_mode); @@ -98,19 +101,14 @@ void NodePanelStroke::init_controls() // init main brush auto b = std::make_shared(); b->load_texture(m_brush_popup->get_texture_path(br_idx), m_brush_popup->get_thumb_path(br_idx)); + b->load_dual(m_brush_popup->get_texture_path(br_idx), m_brush_popup->get_thumb_path(br_idx)); b->m_tip_size = .1f; b->m_tip_flow = .5f; b->m_tip_spacing = .1f; b->m_tip_opacity = 1.f; Canvas::I->m_current_brush = b; - // init dual brush - auto db = std::make_shared(); - db->load_texture(m_brush_popup->get_texture_path(br_idx), m_brush_popup->get_thumb_path(br_idx)); - db->m_tip_size = .3f; - db->m_tip_flow = .5f; - db->m_tip_spacing = .1f; - db->m_tip_opacity = 1.f; - Canvas::I->m_dual_brush = db; + + // BRUSH PRESETS m_preset_thumb = find("preset-thumb"); m_preset_thumb->m_use_mipmaps = true; @@ -156,55 +154,10 @@ void NodePanelStroke::init_controls() }; }; - m_dual_preset_thumb = find("dual-preset-thumb"); - m_dual_preset_thumb->m_use_mipmaps = true; - m_dual_preset_preview = find("dual-preset-preview"); - m_dual_preset_preview->m_brush = b; - m_dual_preset_preview->draw_stroke(); - m_dual_preset_button = find("dual-preset-button"); - m_dual_preset_button->on_click = [this](Node*) { - auto screen = root()->m_size; - glm::vec2 pos = m_dual_preset_button->m_pos + glm::vec2(m_dual_preset_button->m_size.x, 0); - root()->add_child(m_presets_popup); - auto tick = root()->add_child(); - tick->SetPositioning(YGPositionTypeAbsolute); - tick->SetSize(16, 32); - tick->SetPosition(pos.x, pos.y + (m_dual_preset_button->m_size.y - 32) * 0.5f); - tick->set_image("data/ui/popup-tick.png"); - root()->update(); - if ((pos.y + m_presets_popup->m_size.y) > screen.y) - pos.y = screen.y - m_presets_popup->m_size.y; - if (pos.y < 0) - pos.y = 0; - m_presets_popup->SetPosition(pos.x + 16, pos.y); - m_presets_popup->mouse_capture(); - root()->update(); - - m_presets_popup->on_popup_close = [this, tick](Node*) { - tick->destroy(); - }; - - m_presets_popup->on_brush_changed = [this](Node* target, std::shared_ptr& b) { - // don't change some params - b->m_tip_size = Canvas::I->m_current_brush->m_tip_size; - b->m_tip_color = { 0, 0, 0, 1 }; - *Canvas::I->m_dual_brush = *b; - m_preview->draw_stroke(); - m_dual_preset_preview->draw_stroke(); - m_dual_brush_thumb->set_image(b->m_brush_thumb_path); - m_dual_preset_thumb->set_image(b->m_brush_thumb_path); - update_controls(); - - //m_presets_popup->mouse_release(); - //m_presets_popup->parent->remove_child(m_presets_popup.get()); - }; - }; + // BRUSH TIP SHAPE m_brush_thumb = find("tip-change-thumb"); m_brush_thumb->set_image(m_brush_popup->get_thumb_path(br_idx)); - m_dual_brush_thumb = find("dual-change-thumb"); - m_dual_brush_thumb->set_image(m_brush_popup->get_thumb_path(br_idx)); - m_brush_button = find("tip-change"); m_brush_button->on_click = [this](Node*) { auto screen = root()->m_size; @@ -237,6 +190,42 @@ void NodePanelStroke::init_controls() }; }; + // DUAL BRUSH TIP SHAPE + + m_dual_brush_thumb = find("dual-change-thumb"); + m_dual_brush_thumb->set_image(m_brush_popup->get_thumb_path(br_idx)); + m_dual_brush_button = find("dual-change"); + m_dual_brush_button->on_click = [this](Node*) { + auto screen = root()->m_size; + glm::vec2 pos = m_dual_brush_button->m_pos + glm::vec2(m_dual_brush_button->m_size.x, 0); + root()->add_child(m_brush_popup); + auto tick = root()->add_child(); + tick->SetPositioning(YGPositionTypeAbsolute); + tick->SetSize(16, 32); + tick->SetPosition(pos.x, pos.y + (m_dual_brush_button->m_size.y - 32) * 0.5f); + tick->set_image("data/ui/popup-tick.png"); + root()->update(); + if ((pos.y + m_brush_popup->m_size.y) > screen.y) + pos.y = screen.y - m_brush_popup->m_size.y; + if (pos.y < 0) + pos.y = 0; + m_brush_popup->SetPosition(pos.x + 16, pos.y); + m_brush_popup->mouse_capture(); + root()->update(); + + m_brush_popup->on_popup_close = [this, tick](Node*) { + tick->destroy(); + }; + + m_brush_popup->on_brush_changed = [this](Node*, int index) { + if (on_dual_changed) + on_dual_changed(this, m_brush_popup->get_texture_path(index), m_brush_popup->get_thumb_path(index)); + m_dual_brush_thumb->set_image(m_brush_popup->get_thumb_path(index)); + //m_brush_popup->mouse_release(); + //m_brush_popup->parent->remove_child(m_brush_popup.get()); + }; + }; + m_preview = find("canvas"); m_blend_mode = find("blend-mode"); m_blend_mode->on_select = [this](Node*, int index) { @@ -266,9 +255,6 @@ void NodePanelStroke::init_controls() init_slider(m_jitter_hue, "jitter-hue", &Brush::m_jitter_hue); init_slider(m_jitter_sat, "jitter-sat", &Brush::m_jitter_sat); init_slider(m_jitter_val, "jitter-val", &Brush::m_jitter_val); - m_curves[m_tip_size] = [](float v){ return glm::pow(v, 3.f); }; - m_curves[m_tip_spacing] = [](float v){ return glm::pow(v, 2.f); }; - m_curves[m_tip_flow] = [](float v){ return glm::pow(v, 2.f); }; init_checkbox(m_tip_angle_follow, "tip-angle-follow", &Brush::m_tip_angle_follow); init_checkbox(m_tip_flow_pressure, "tip-flow-pressure", &Brush::m_tip_flow_pressure); @@ -291,6 +277,18 @@ void NodePanelStroke::init_controls() init_slider(m_dual_spacing, "dual-spacing", &Brush::m_dual_spacing); init_slider(m_dual_scatter, "dual-scatter", &Brush::m_dual_scatter); init_slider(m_tip_aspect, "tip-aspect", &Brush::m_tip_aspect); + init_slider(m_dual_opacity, "dual-opacity", &Brush::m_dual_opacity); + init_slider(m_dual_flow, "dual-flow", &Brush::m_dual_flow); + init_slider(m_dual_rotate, "dual-rotate", &Brush::m_dual_rotate); + + auto curve_cubic = [](float v) { return glm::pow(v, 3.f); }; + auto curve_quad = [](float v) { return glm::pow(v, 2.f); }; + m_curves[m_tip_size] = curve_cubic; + m_curves[m_tip_spacing] = curve_quad; + m_curves[m_tip_flow] = curve_quad; + m_curves[m_dual_size] = curve_cubic; + m_curves[m_dual_spacing] = curve_quad; + m_curves[m_dual_flow] = curve_quad; m_tip_aspect_reset = find("tip-aspect-reset"); m_tip_aspect_reset->on_click = [this](Node*) { @@ -319,7 +317,7 @@ void NodePanelStroke::init_controls() if (TextureManager::load(path.c_str())) { if (on_stencil_changed) - on_stencil_changed(this, path); + on_stencil_changed(this, path, ""); } App::I.async_redraw(); App::I.async_end(); diff --git a/src/node_panel_stroke.h b/src/node_panel_stroke.h index bcc4dca..4e43609 100644 --- a/src/node_panel_stroke.h +++ b/src/node_panel_stroke.h @@ -38,14 +38,12 @@ public: NodeCheckBox* m_tip_flow_pressure; NodeCheckBox* m_tip_size_pressure; NodeButtonCustom* m_brush_button; + NodeButtonCustom* m_dual_brush_button; NodeImage* m_brush_thumb; NodeImage* m_dual_brush_thumb; NodeImage* m_preset_thumb; - NodeImage* m_dual_preset_thumb; NodeButtonCustom* m_preset_button; - NodeButtonCustom* m_dual_preset_button; NodeStrokePreview* m_preset_preview; - NodeStrokePreview* m_dual_preset_preview; NodeCheckBox* m_tip_invert; NodeCheckBox* m_tip_flipx; @@ -63,14 +61,18 @@ public: NodeSliderH* m_dual_spacing; NodeSliderH* m_dual_scatter; NodeSliderH* m_tip_aspect; + NodeSliderH* m_dual_flow; + NodeSliderH* m_dual_opacity; + NodeSliderH* m_dual_rotate; NodeComboBox* m_dual_blend_mode; NodeButtonCustom* m_tip_aspect_reset; std::shared_ptr m_brush_popup; std::shared_ptr m_presets_popup; std::function on_stroke_change; - std::function on_stencil_changed; + std::function on_stencil_changed; std::function on_brush_changed; + std::function on_dual_changed; std::map> m_curves; virtual Node* clone_instantiate() const override;