diff --git a/data/layout.xml b/data/layout.xml
index 1460f08..5b7e1cf 100644
--- a/data/layout.xml
+++ b/data/layout.xml
@@ -81,10 +81,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine.xcodeproj/project.pbxproj b/engine.xcodeproj/project.pbxproj
index 68f421c..25f1edd 100644
--- a/engine.xcodeproj/project.pbxproj
+++ b/engine.xcodeproj/project.pbxproj
@@ -163,7 +163,6 @@
ADC6F4661F3E66FB004177FA /* app_dialogs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ADC6F4651F3E66FA004177FA /* app_dialogs.cpp */; };
ADC6F4671F3E66FB004177FA /* app_dialogs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ADC6F4651F3E66FA004177FA /* app_dialogs.cpp */; };
ADC6F4681F3E66FB004177FA /* app_dialogs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ADC6F4651F3E66FA004177FA /* app_dialogs.cpp */; };
- ADCB41B61F6FF2DD006D46FE /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD58E0521E107411006ACC15 /* main.cpp */; };
ADD7D26F1EBF9AE300D5A897 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = ADD7D26E1EBF9AE300D5A897 /* main.m */; };
ADD7D2721EBF9AE300D5A897 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = ADD7D2711EBF9AE300D5A897 /* AppDelegate.m */; };
ADD7D2791EBF9AE300D5A897 /* GameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = ADD7D2781EBF9AE300D5A897 /* GameViewController.m */; };
@@ -186,7 +185,6 @@
ADD7D29A1EBF9E1C00D5A897 /* pch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD95AEC51E41EDEC002DD03A /* pch.cpp */; };
ADD7D29B1EBF9E1C00D5A897 /* layout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD3B1EBE1E3B8B7600E918E3 /* layout.cpp */; };
ADD7D29C1EBF9E1C00D5A897 /* shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD58E0631E2A76FD006ACC15 /* shader.cpp */; };
- ADD7D29D1EBF9E1C00D5A897 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD58E0521E107411006ACC15 /* main.cpp */; };
ADD7D29E1EBF9E1C00D5A897 /* shape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD58E06D1E2A80BC006ACC15 /* shape.cpp */; };
ADD7D29F1EBF9E1C00D5A897 /* app.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD58E0701E2A90EF006ACC15 /* app.cpp */; };
ADD7D2A01EBF9E1C00D5A897 /* image.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AD58E0661E2A7741006ACC15 /* image.cpp */; };
@@ -802,7 +800,6 @@
files = (
AD58E0791E342205006ACC15 /* tinyxml2.cpp in Sources */,
AD10638B1EC7ADFA002A525F /* node_panel_color.cpp in Sources */,
- ADCB41B61F6FF2DD006D46FE /* main.cpp in Sources */,
AD10638C1EC7ADFA002A525F /* node_panel_layer.cpp in Sources */,
AD29CC621EA2B214008C8BFA /* action.cpp in Sources */,
AD1063891EC7ADFA002A525F /* node_message_box.cpp in Sources */,
@@ -878,7 +875,6 @@
AD10639E1EC7AE92002A525F /* node_icon.cpp in Sources */,
AD1063A71EC7AE92002A525F /* node_settings.cpp in Sources */,
AD0E119E1ECA215600CDA6BB /* app_shaders.cpp in Sources */,
- ADD7D29D1EBF9E1C00D5A897 /* main.cpp in Sources */,
ADD7D29F1EBF9E1C00D5A897 /* app.cpp in Sources */,
AD1063A41EC7AE92002A525F /* node_panel_layer.cpp in Sources */,
AD1063AA1EC7AE92002A525F /* node_text_input.cpp in Sources */,
diff --git a/engine/app.h b/engine/app.h
index 45f9a74..cf24bb9 100644
--- a/engine/app.h
+++ b/engine/app.h
@@ -41,6 +41,7 @@ public:
std::shared_ptr layers;
std::shared_ptr color;
std::shared_ptr stroke;
+ std::shared_ptr presets;
NodeCanvas* canvas;
Node* current_panel = nullptr;
NodeScroll* panels;
diff --git a/engine/app_layout.cpp b/engine/app_layout.cpp
index fe93052..a0c81e2 100644
--- a/engine/app_layout.cpp
+++ b/engine/app_layout.cpp
@@ -121,6 +121,12 @@ void App::init_sidebar()
stroke->create();
stroke->loaded();
+ presets = std::make_shared();
+ presets->m_manager = &layout;
+ presets->init();
+ presets->create();
+ presets->loaded();
+
if (canvas)
{
stroke->m_canvas->m_brush.m_tip_color = color->m_color;
@@ -136,6 +142,16 @@ void App::init_sidebar()
if (on_brush_select)
on_brush_select(index);
};
+ presets->on_brush_changed = [this](Node* target, int index) {
+ auto b = presets->get_brush(index);
+ // don't change some params
+ b.m_tip_size = stroke->m_canvas->m_brush.m_tip_size;
+ b.m_tip_color = stroke->m_canvas->m_brush.m_tip_color;
+ stroke->set_params(b);
+ canvas->m_brush = stroke->m_canvas->m_brush;
+ if (on_brush_select)
+ on_brush_select(index);
+ };
color->on_color_changed = [this](Node* target, glm::vec4 color) {
stroke->m_canvas->m_brush.m_tip_color = color;
@@ -196,6 +212,14 @@ void App::init_sidebar()
button->set_color(panels->get_child_index(brushes.get()) == -1 ? color_button_normal : color_button_hlight);
};
}
+ if (auto* button = layout[main_id]->find("btn-brush-preset"))
+ {
+ button->on_click = [this, button](Node*) {
+ panels->get_child_index(presets.get()) == -1 ? panels->add_child(presets) : panels->remove_child(presets.get());
+ panels->fix_scroll();
+ button->set_color(panels->get_child_index(presets.get()) == -1 ? color_button_normal : color_button_hlight);
+ };
+ }
if (auto* button = layout[main_id]->find("btn-color"))
{
button->on_click = [this, button](Node*) {
diff --git a/engine/node_panel_brush.cpp b/engine/node_panel_brush.cpp
index 86b48b0..03ecf8e 100644
--- a/engine/node_panel_brush.cpp
+++ b/engine/node_panel_brush.cpp
@@ -106,3 +106,105 @@ void NodePanelBrush::select_brush(int brush_id)
}
}
}
+
+// -----------------------------------------------------------------------
+
+
+Node* NodeBrushPresetItem::clone_instantiate() const
+{
+ return new NodeBrushPresetItem();
+}
+
+void NodeBrushPresetItem::init()
+{
+ init_template("tpl-brush-preset");
+ color_hover = glm::vec4(.7, .7, .7, 1);
+ color_normal = glm::vec4(.3, .3, .3, 1);
+ m_color = color_normal;
+ m_thumb = (NodeImage*)m_children[0].get();
+ m_preview = (NodeStrokePreview*)m_children[1].get();
+}
+
+void NodeBrushPresetItem::draw()
+{
+ m_color = m_mouse_inside ? color_hover : color_normal;
+ m_color = m_selected ? glm::vec4(.9, 0, 0, 1) : m_color;
+ NodeButtonCustom::draw();
+}
+
+//---
+
+Node* NodePanelBrushPreset::clone_instantiate() const
+{
+ return new NodePanelBrushPreset();
+}
+
+void NodePanelBrushPreset::init()
+{
+ init_template("tpl-panel-brush-preset");
+
+ static auto icons = Asset::list_files("data/thumbs", true, ".*\\.png$");
+
+ if ((m_container = find("brushes")))
+ {
+ int count = 0;
+ for (auto& i : icons)
+ {
+ std::string path = "data/thumbs/" + i;
+ std::string path_hi = "data/brushes/" + i;
+ NodeBrushPresetItem* brush = new NodeBrushPresetItem;
+ m_container->add_child(brush);
+ brush->init();
+ brush->create();
+ brush->loaded();
+// brush->set_icon(path.c_str());
+ brush->m_brushID = count++;
+ brush->high_path = path_hi;
+ brush->high_id = const_hash(path_hi.c_str());
+ brush->m_brush.m_tex_id = const_hash(path.c_str());
+ brush->m_brush.m_tip_size = .05;
+ brush->m_brush.m_tip_flow = .2;
+ brush->m_brush.m_tip_opacity = 1;
+ brush->m_brush.m_tip_spacing = 0.03;
+ brush->m_brush.m_jitter_spread = (rand() % 1000) * 0.0001;
+ brush->m_preview->m_brush = brush->m_brush;
+ brush->m_preview->draw_stroke();
+ brush->m_thumb->m_path = path;
+ brush->m_thumb->m_tex_id = const_hash(path.c_str());
+ brush->m_thumb->create();
+ m_brushes.push_back(brush);
+ brush->on_click = std::bind(&NodePanelBrushPreset::handle_click, this, std::placeholders::_1);
+ }
+ }
+}
+
+void NodePanelBrushPreset::handle_click(Node* target)
+{
+ if (target == m_current)
+ return;
+ if (m_current)
+ m_current->m_selected = false;
+ m_current = (NodeButtonBrush*)target;
+ m_current->m_selected = true;
+ if (on_brush_changed)
+ on_brush_changed(this, m_current->m_brushID);
+}
+
+ui::Brush NodePanelBrushPreset::get_brush(int index) const
+{
+ auto b = m_brushes[index]->m_brush;
+ TextureManager::load(m_brushes[index]->high_path.c_str(), true);
+ b.m_tex_id = m_brushes[index]->high_id;
+ return b;
+}
+
+uint16_t NodePanelBrushPreset::get_texture_id(int index) const
+{
+ TextureManager::load(m_brushes[index]->high_path.c_str(), true);
+ return m_brushes[index]->high_id;
+}
+
+int NodePanelBrushPreset::get_brush_id(int index) const
+{
+ return m_brushes[index]->m_brushID;
+}
diff --git a/engine/node_panel_brush.h b/engine/node_panel_brush.h
index a766615..6317b2c 100644
--- a/engine/node_panel_brush.h
+++ b/engine/node_panel_brush.h
@@ -2,6 +2,8 @@
#include "node.h"
#include "node_button_custom.h"
#include "node_image.h"
+#include "node_stroke_preview.h"
+#include "brush.h"
class NodeButtonBrush : public NodeButtonCustom
{
@@ -32,3 +34,38 @@ public:
int get_brush_id(int index) const;
void select_brush(int brush_id);
};
+
+// -----------------------------------------------------------------------
+
+class NodeBrushPresetItem : public NodeButtonCustom
+{
+public:
+ int m_brushID;
+ ui::Brush m_brush;
+ std::string high_path;
+ uint16_t high_id;
+ bool m_selected = false;
+ NodeStrokePreview* m_preview;
+ NodeImage* m_thumb;
+ virtual Node* clone_instantiate() const override;
+ virtual void init() override;
+ virtual void draw() override;
+};
+
+class NodePanelBrushPreset : public Node
+{
+ std::vector m_brushes;
+ NodeButtonBrush* m_current = nullptr;
+ Node* m_container;
+public:
+ std::function on_brush_changed;
+ virtual Node* clone_instantiate() const override;
+ virtual void init() override;
+ void handle_click(Node* target);
+ std::vector FindAllBrushes(const std::string& folder);
+ uint16_t get_texture_id(int index) const;
+ ui::Brush get_brush(int index) const;
+ int get_brush_id(int index) const;
+ void select_brush(int brush_id);
+};
+
diff --git a/engine/node_stroke_preview.cpp b/engine/node_stroke_preview.cpp
index b32123f..ee19145 100644
--- a/engine/node_stroke_preview.cpp
+++ b/engine/node_stroke_preview.cpp
@@ -31,9 +31,9 @@ void NodeStrokePreview::init_controls()
m_mesh.create();
m_sampler.create();
m_sampler_brush.create();
- m_sampler_brush.set_filter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR);
- TextureManager::load("data/Icons/Round-Hard.png");
- m_brush.m_tex_id = const_hash("data/Icons/Round-Hard.png");
+ m_sampler_brush.set_filter(GL_LINEAR, GL_LINEAR);
+ TextureManager::load("data/thumbs/Round-Hard.png");
+ m_brush.m_tex_id = const_hash("data/thumbs/Round-Hard.png");
}
void NodeStrokePreview::restore_context()