diff --git a/data/layout.xml b/data/layout.xml
index abec4a7..9a7f666 100644
--- a/data/layout.xml
+++ b/data/layout.xml
@@ -35,8 +35,8 @@
-
-
+
+
@@ -118,8 +118,55 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -346,12 +393,14 @@
+
+
-
+
-
+
-
+
diff --git a/engine.vcxproj b/engine.vcxproj
index 86a22f3..89993c9 100644
--- a/engine.vcxproj
+++ b/engine.vcxproj
@@ -152,6 +152,7 @@
+
@@ -162,6 +163,7 @@
Create
Create
+
@@ -188,15 +190,20 @@
+
+
+
+
+
diff --git a/engine.vcxproj.filters b/engine.vcxproj.filters
index 7edc68e..20f7b40 100644
--- a/engine.vcxproj.filters
+++ b/engine.vcxproj.filters
@@ -57,6 +57,12 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
@@ -89,5 +95,14 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+
+
\ No newline at end of file
diff --git a/engine/app.cpp b/engine/app.cpp
index 4f38396..b376783 100644
--- a/engine/app.cpp
+++ b/engine/app.cpp
@@ -5,14 +5,6 @@ using namespace ui;
App App::I; // singleton
-#ifdef __APPLE__
-#define SHADER_VERSION "#version 300 es\n"
-#elif __ANDROID__
-#define SHADER_VERSION "#version 300 es\n"
-#elif _WIN32
-#define SHADER_VERSION "#version 150\n"
-#endif
-
void App::create()
{
width = 800;
@@ -192,15 +184,30 @@ void App::initLayout()
NodeBorder::static_init();
NodeImage::static_init();
NodeIcon::static_init();
+ NodeCanvas2D::static_init();
layout.on_loaded = [&] {
LOG("initializing layout updating after load");
-
layout[main_id]->update(width, height, zoom);
+
LOG("initializing layout components");
sidebar = layout[main_id]->find("sidebar");
- brushes = layout[main_id]->find("panel-brushes");
- layers = layout[main_id]->find("panel-layers");
+ brushes = layout[main_id]->find("panel-brush");
+ layers = layout[main_id]->find("panel-layer");
+ color = layout[main_id]->find("panel-color");
+ stroke = layout[main_id]->find("panel-stroke");
+
+ brushes->on_brush_changed = [this](Node* target, int index) {
+ auto tid = brushes->get_texture_id(index);
+ stroke->m_canvas->m_tex_id = tid;
+ stroke->m_canvas->draw_stroke();
+ };
+
+ color->on_color_changed = [this](Node* target, glm::vec4 color) {
+ stroke->m_canvas->m_tip_color = color;
+ stroke->m_canvas->draw_stroke();
+ };
+
if (auto* button = layout[main_id]->find("btn-close"))
{
button->on_click = [](Node*) { exit(0); };
@@ -271,7 +278,8 @@ void App::initLayout()
};
LOG("initializing layout xml");
#ifdef _WIN32
- layout.load("C:\\Users\\omar\\Desktop\\new_engine\\data\\layout.xml");
+ //layout.load("C:\\Users\\omar\\Desktop\\new_engine\\data\\layout.xml");
+ layout.load("data/layout.xml");
#else
layout.load("data/layout.xml");
#endif
@@ -307,8 +315,6 @@ void App::init()
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
- //glPointSize(5);
- glLineWidth(2);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
@@ -336,16 +342,16 @@ void App::update(float dt)
//glViewport(0, 0, (GLsizei)width, (GLsizei)height);
//glClear(GL_COLOR_BUFFER_BIT);
- layout.reload();
- if (auto* main = layout[main_id])
- main->update(width, height, zoom);
+// layout.reload();
+// if (auto* main = layout[main_id])
+// main->update(width, height, zoom);
auto observer = [this](Node* n)
{
if (n && n->m_display)
{
auto box = n->m_clip;
- glm::vec4 c = glm::vec4((int)box.x - 1, (int)(height / zoom - box.y - box.w) - 1, (int)box.z + 2, (int)box.w + 2) * zoom;
+ glm::ivec4 c = glm::vec4((int)box.x - 1, (int)(height / zoom - box.y - box.w) - 1, (int)box.z + 2, (int)box.w + 2) * zoom;
glScissor(c.x, c.y, c.z, c.w);
n->draw();
}
diff --git a/engine/app.h b/engine/app.h
index 9f23106..71d2a35 100644
--- a/engine/app.h
+++ b/engine/app.h
@@ -20,8 +20,10 @@ public:
NodePopupMenu* menu_edit = nullptr;
NodePopupMenu* menu_layers = nullptr;
NodeBorder* sidebar = nullptr;
- NodePanelBrushes* brushes;
- NodePanelLayers* layers;
+ NodePanelBrush* brushes;
+ NodePanelLayer* layers;
+ NodePanelColor* color;
+ NodePanelStroke* stroke;
const uint16_t main_id = const_hash("main");
float width;
float height;
diff --git a/engine/bezier.cpp b/engine/bezier.cpp
new file mode 100644
index 0000000..37d2979
--- /dev/null
+++ b/engine/bezier.cpp
@@ -0,0 +1,39 @@
+#include "pch.h"
+#include "bezier.h"
+
+
+double BezierCurve::FactorialLookup[] = {
+ 1.0,
+ 1.0,
+ 2.0,
+ 6.0,
+ 24.0,
+ 120.0,
+ 720.0,
+ 5040.0,
+ 40320.0,
+ 362880.0,
+ 3628800.0,
+ 39916800.0,
+ 479001600.0,
+ 6227020800.0,
+ 87178291200.0,
+ 1307674368000.0,
+ 20922789888000.0,
+ 355687428096000.0,
+ 6402373705728000.0,
+ 121645100408832000.0,
+ 2432902008176640000.0,
+ 51090942171709440000.0,
+ 1124000727777607680000.0,
+ 25852016738884976640000.0,
+ 620448401733239439360000.0,
+ 15511210043330985984000000.0,
+ 403291461126605635584000000.0,
+ 10888869450418352160768000000.0,
+ 304888344611713860501504000000.0,
+ 8841761993739701954543616000000.0,
+ 265252859812191058636308480000000.0,
+ 8222838654177922817725562880000000.0,
+ 263130836933693530167218012160000000.0,
+};
\ No newline at end of file
diff --git a/engine/bezier.h b/engine/bezier.h
new file mode 100644
index 0000000..bd0fc6a
--- /dev/null
+++ b/engine/bezier.h
@@ -0,0 +1,66 @@
+#pragma once
+
+
+class BezierCurve
+{
+ static double FactorialLookup[33];
+public:
+ // just check if n is appropriate, then return the result
+ static double factorial(int n)
+ {
+// if (n < 0) { throw new Exception("n is less than 0"); }
+// if (n > 32) { throw new Exception("n is greater than 32"); }
+ return FactorialLookup[n]; /* returns the value n! as a SUMORealing point number */
+ }
+
+ static double Ni(int n, int i)
+ {
+ double ni;
+ double a1 = factorial(n);
+ double a2 = factorial(i);
+ double a3 = factorial(n - i);
+ ni = a1 / (a2 * a3);
+ return ni;
+ }
+
+ // Calculate Bernstein basis
+ static double Bernstein(int n, int i, double t)
+ {
+ double basis;
+ double ti; /* t^i */
+ double tni; /* (1 - t)^i */
+
+ /* Prevent problems with pow */
+
+ if (t == 0.0 && i == 0)
+ ti = 1.0;
+ else
+ ti = pow(t, i);
+
+ if (n == i && t == 1.0)
+ tni = 1.0;
+ else
+ tni = pow((1 - t), (n - i));
+
+ //Bernstein basis
+ basis = Ni(n, i) * ti * tni;
+ return basis;
+ }
+
+ static glm::vec2 Bezier2D(const std::vector& b, double t)
+ {
+// if ((1.0 - t) < 5e-6)
+// t = 1.0;
+
+ double px = 0.0;
+ double py = 0.0;
+ const int npts = (int)b.size();
+ for (int i = 0; i < npts; i++)
+ {
+ double basis = Bernstein(npts - 1, i, t);
+ px += basis * b[i].x;
+ py += basis * b[i].y;
+ }
+ return { px, py };
+ }
+};
diff --git a/engine/layout.cpp b/engine/layout.cpp
index d951513..b0088fd 100644
--- a/engine/layout.cpp
+++ b/engine/layout.cpp
@@ -9,6 +9,7 @@ Plane NodeBorder::m_plane;
Plane NodeImage::m_plane;
Sampler NodeImage::m_sampler;
std::map NodeIcon::m_icons;
+ui::Shader NodeCanvas2D::m_shader;
kEventResult Node::on_event(Event* e)
{
@@ -177,6 +178,7 @@ void Node::update_internal(const glm::vec2& origin, const glm::mat4& proj)
float y = YGNodeLayoutGetTop(y_node);
float w = YGNodeLayoutGetWidth(y_node);
float h = YGNodeLayoutGetHeight(y_node);
+ auto old_size = m_size;
m_pos = glm::floor(origin + glm::vec2(x, y));
m_size = glm::floor(glm::vec2(w, h));
@@ -215,6 +217,9 @@ void Node::update_internal(const glm::vec2& origin, const glm::mat4& proj)
}
}
+ if (m_size != old_size)
+ handle_resize(old_size, m_size);
+
for (auto& c : m_children)
c->update_internal(m_pos, proj);
}
@@ -433,9 +438,12 @@ void Node::load_internal(const tinyxml2::XMLElement* x_node)
CASE(kWidget::Viewport, NodeViewport);
CASE(kWidget::CheckBox, NodeCheckBox);
CASE(kWidget::Layer, NodeLayer);
- CASE(kWidget::PanelLayers, NodePanelLayers);
- CASE(kWidget::PanelBrushes, NodePanelBrushes);
+ CASE(kWidget::PanelLayer, NodePanelLayer);
+ CASE(kWidget::PanelBrush, NodePanelBrush);
+ CASE(kWidget::PanelColor, NodePanelColor);
+ CASE(kWidget::PanelStroke, NodePanelStroke);
CASE(kWidget::ColorQuad, NodeColorQuad);
+ CASE(kWidget::Canvas2D, NodeCanvas2D);
#undef CASE
case kWidget::Ref:
{
@@ -496,7 +504,7 @@ void Node::clone_children(Node* dest) const
Node* cn = c->clone();
dest->m_children.emplace_back(cn);
cn->parent = dest;
- cn->m_manager = m_manager;
+ cn->m_manager = dest->m_manager;
YGNodeInsertChild(dest->y_node, cn->y_node, YGNodeGetChildCount(dest->y_node));
}
}
diff --git a/engine/layout.h b/engine/layout.h
index 1c92858..5e25c21 100644
--- a/engine/layout.h
+++ b/engine/layout.h
@@ -4,6 +4,8 @@
#include "shader.h"
#include "font.h"
#include "asset.h"
+#include "rtt.h"
+#include "bezier.h"
#include
#include
@@ -62,9 +64,12 @@ enum class kWidget : uint16_t
Ref = const_hash("ref"),
CheckBox = const_hash("checkbox"),
Layer = const_hash("layer"),
- PanelLayers = const_hash("panel-layers"),
- PanelBrushes = const_hash("panel-brushes"),
+ PanelLayer = const_hash("panel-layer"),
+ PanelBrush = const_hash("panel-brush"),
+ PanelColor = const_hash("panel-color"),
+ PanelStroke = const_hash("panel-stroke"),
ColorQuad = const_hash("color-quad"),
+ Canvas2D = const_hash("canvas2D"),
};
enum class kShapeType : uint16_t
@@ -273,6 +278,7 @@ public:
virtual kEventResult on_event(Event* e);
virtual kEventResult handle_event(Event* e) { return kEventResult::Available; }
+ virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size) { };
virtual void create() { }
virtual void init() { }
virtual void loaded() { }
@@ -698,6 +704,7 @@ public:
class NodePopupMenu : public Node
{
public:
+ std::function on_select;
virtual Node* clone_instantiate() const override { return new NodePopupMenu(); }
virtual void init() override
{
@@ -706,19 +713,34 @@ public:
SetWidth(100);
SetHeight(400);
SetPositioning(YGPositionTypeAbsolute);
+ m_mouse_ignore = false;
}
virtual kEventResult handle_event(Event* e) override
{
switch (e->m_type)
{
case kEventType::MouseDownL:
+ break;
+ case kEventType::MouseUpL:
if (!m_mouse_inside)
{
mouse_release();
- destroy();
}
- break;
- case kEventType::MouseUpL:
+ else
+ {
+ auto pos = ((MouseEvent*)e)->m_pos;
+ for (int i = 0; i < m_children.size(); i++)
+ {
+ if (m_children[i]->m_mouse_inside)
+ {
+ if (on_select)
+ on_select(this, i);
+ break;
+ }
+ }
+ mouse_release();
+ }
+ destroy();
break;
default:
break;
@@ -833,7 +855,7 @@ public:
static char name[256];
int x, y, w, h;
char* s = strtok(data, "\n");
- int i = strlen(s) + 1;
+ auto i = strlen(s) + 1;
while (i < size && sscanf(s, "%s %d %d %d %d", name, &w, &h, &x, &y) == 5)
{
m_icons[name] = glm::vec4(x, y, x + w, y + h);
@@ -899,7 +921,7 @@ public:
glClearColor(1, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
auto box = m_clip * root()->m_zoom;
- auto c = glm::vec4(box.x, (int)(vp[3] - box.y - box.w), box.z, box.w);
+ glm::ivec4 c = (glm::ivec4)glm::vec4(box.x, (int)(vp[3] - box.y - box.w), box.z, box.w);
glViewport(c.x, c.y, c.z, c.w);
TextureManager::get(m_tex_id).bind();
m_sampler->bind(0);
@@ -963,6 +985,8 @@ class NodeSliderCursor : public NodeButtonCustom
glm::vec2 old_pos;
public:
glm::vec2 m_mask{ 1, 0 };
+ glm::vec2 m_value;
+ std::function on_value_changed;
virtual Node* clone_instantiate() const override { return new NodeSliderCursor(); }
virtual void clone_copy(Node* dest) const override
{
@@ -992,6 +1016,9 @@ public:
drag_diff = old_pos + (((MouseEvent*)e)->m_pos - drag_start) * m_mask;
auto pos = glm::clamp(drag_diff, { 0, 0 }, sz);
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);
}
break;
default:
@@ -1005,15 +1032,29 @@ class NodeSliderH : public NodeBorder
{
public:
NodeSliderCursor* m_cursor;
+ std::function on_value_changed;
virtual Node* clone_instantiate() const override { return new NodeSliderH(); }
+ virtual void clone_finalize(Node* dest) const override
+ {
+ NodeSliderH* n = static_cast(dest);
+ n->init_controls();
+ }
virtual void init() override
{
const auto& m_template = (NodeBorder*)init_template("tpl-slider-h");
m_color = m_template->m_color;
m_border_color = m_template->m_border_color;
- m_thinkness = m_thinkness;
+ m_thinkness = m_template->m_thinkness;
+ init_controls();
+ }
+ void init_controls()
+ {
m_cursor = find("cursor");
m_cursor->m_mask = { 1, 0 };
+ m_cursor->on_value_changed = [this](Node*, glm::vec2 value) {
+ if (on_value_changed)
+ on_value_changed(this, value.x);
+ };
}
};
@@ -1021,15 +1062,29 @@ class NodeSliderV : public NodeBorder
{
public:
NodeSliderCursor* m_cursor;
+ std::function on_value_changed;
virtual Node* clone_instantiate() const override { return new NodeSliderV(); }
+ virtual void clone_finalize(Node* dest) const override
+ {
+ NodeSliderV* n = static_cast(dest);
+ n->init_controls();
+ }
virtual void init() override
{
const auto& m_template = (NodeBorder*)init_template("tpl-slider-v");
m_color = m_template->m_color;
m_border_color = m_template->m_border_color;
- m_thinkness = m_thinkness;
+ m_thinkness = m_template->m_thinkness;
+ init_controls();
+ }
+ void init_controls()
+ {
m_cursor = find("cursor");
m_cursor->m_mask = { 0, 1 };
+ m_cursor->on_value_changed = [this](Node*, glm::vec2 value) {
+ if (on_value_changed)
+ on_value_changed(this, value.y);
+ };
}
};
@@ -1037,25 +1092,34 @@ class NodeSliderHue : public NodeBorder
{
public:
NodeSliderCursor* m_cursor;
+ glm::vec4 m_color;
+ std::function on_value_changed;
virtual Node* clone_instantiate() const override { return new NodeSliderHue(); }
virtual void clone_finalize(Node* dest) const override
{
NodeSliderHue* n = static_cast(dest);
- n->m_cursor = n->find("cursor");
- n->m_cursor->m_color = glm::vec4(0);
- n->m_cursor->m_border_color = glm::vec4(0, 0, 0, 1);
+ n->init_controls();
}
virtual void init() override
{
const auto& m_template = (NodeBorder*)init_template("tpl-slider-hue");
m_color = m_template->m_color;
m_border_color = m_template->m_border_color;
- m_thinkness = m_thinkness;
+ m_thinkness = m_template->m_thinkness;
+ init_controls();
+ }
+ void init_controls()
+ {
m_cursor = find("cursor");
m_cursor->m_mask = { 0, 1 };
m_cursor->m_thinkness = 1;
m_cursor->m_color = glm::vec4(0);
m_cursor->m_border_color = glm::vec4(0, 0, 0, 1);
+ m_cursor->on_value_changed = [this](Node*, glm::vec2 value) {
+ m_color = glm::vec4(convert_hsv2rgb({ value.y, 1, 1 }), 1);
+ if (on_value_changed)
+ on_value_changed(this, m_color);
+ };
}
virtual void draw() override
{
@@ -1214,7 +1278,7 @@ public:
virtual void draw() override
{
auto c = m_selected ? m_color_selected : m_color_normal;
- m_thinkness = m_selected ? 1 : 0;
+ m_thinkness = m_selected ? 1.f : 0.f;
m_color = m_mouse_inside ? m_color_hover : c;
NodeBorder::draw();
}
@@ -1225,7 +1289,7 @@ public:
}
};
-class NodePanelLayers : public Node
+class NodePanelLayer : public Node
{
NodeButtonCustom* btn_add;
NodeButtonCustom* btn_remove;
@@ -1239,7 +1303,7 @@ public:
NodeLayer* m_current_layer = nullptr;
std::vector m_layers;
NodeBorder* m_layers_container;
- virtual Node* clone_instantiate() const override { return new NodePanelLayers(); }
+ virtual Node* clone_instantiate() const override { return new NodePanelLayer(); }
virtual void init() override
{
init_template("tpl-panel-layers");
@@ -1283,7 +1347,7 @@ public:
l->create();
l->loaded();
l->set_name(name);
- l->on_selected = std::bind(&NodePanelLayers::handle_layer_selected, this, std::placeholders::_1);
+ l->on_selected = std::bind(&NodePanelLayer::handle_layer_selected, this, std::placeholders::_1);
m_layers.push_back(l);
if (on_layer_add)
on_layer_add(this);
@@ -1294,11 +1358,11 @@ public:
auto i = m_layers_container->get_child_index(m_current_layer);
m_layers_container->remove_child(m_current_layer);
m_layers.erase(it);
- i = std::min(i, m_layers.size() - 1);
+ i = std::min(i, (int)m_layers.size() - 1);
m_current_layer = m_layers[i];
m_current_layer->m_selected = true;
if (on_layer_delete)
- on_layer_delete(this, std::distance(m_layers.begin(), it));
+ on_layer_delete(this, (int)std::distance(m_layers.begin(), it));
if (on_layer_change)
on_layer_change(this, -1, i);
}
@@ -1342,14 +1406,14 @@ public:
}
};
-class NodePanelBrushes : public Node
+class NodePanelBrush : 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 { return new NodePanelLayers(); }
+ virtual Node* clone_instantiate() const override { return new NodePanelLayer(); }
virtual void init() override
{
init_template("tpl-panel-brushes");
@@ -1369,7 +1433,7 @@ public:
brush->set_icon(path.c_str());
brush->m_brushID = count++;
m_brushes.push_back(brush);
- brush->on_click = std::bind(&NodePanelBrushes::handle_click, this, std::placeholders::_1);
+ brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1);
}
}
}
@@ -1402,32 +1466,307 @@ public:
}
return names;
}
+ uint16_t get_texture_id(int index) const
+ {
+ return m_brushes[index]->img->m_tex_id;
+ }
};
class NodeColorQuad : public NodeBorder
{
+ NodeBorder* m_picker;
+ bool dragging = false;
public:
+ glm::vec2 m_value;
+ std::function on_value_changed;
virtual Node* clone_instantiate() const override { return new NodeColorQuad(); }
+ virtual void clone_finalize(Node* dest) const override
+ {
+ auto n = (NodeColorQuad*)dest;
+ n->m_picker = (NodeBorder*)n->m_children[0].get();
+ }
+ virtual void init() override
+ {
+ m_picker = new NodeBorder;
+ m_picker->SetSize({ 20, 20 });
+ m_picker->SetPositioning(YGPositionTypeAbsolute);
+ m_picker->SetPosition(0, 0);
+ m_picker->m_thinkness = 1;
+ m_picker->m_color = glm::vec4(0);
+ add_child(m_picker);
+ }
+ virtual kEventResult handle_event(Event* e) override
+ {
+ NodeBorder::handle_event(e);
+ switch (e->m_type)
+ {
+ case kEventType::MouseDownL:
+ {
+ dragging = true;
+ mouse_capture();
+ auto sz = GetSize();
+ auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) - m_picker->GetSize() * .5f;
+ 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);
+ }
+ break;
+ case kEventType::MouseUpL:
+ mouse_release();
+ dragging = false;
+ break;
+ case kEventType::MouseMove:
+ if (dragging)
+ {
+ auto sz = GetSize();
+ auto pos = glm::clamp(((MouseEvent*)e)->m_pos - m_pos, { 0, 0 }, sz) - m_picker->GetSize() * .5f;
+ 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);
+ }
+ break;
+ default:
+ break;
+ }
+ return kEventResult::Consumed;
+ }
virtual void draw() override
{
+ m_picker->m_border_color = m_value.y > .5f ? glm::vec4(1) : glm::vec4(0, 0, 0, 1);
using namespace ui;
ui::ShaderManager::use(kShader::ColorQuad);
ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
-
-// if (m_color.a != 1.f)
-// glEnable(GL_BLEND);
-
ui::ShaderManager::u_vec4(kShaderUniform::Col, m_color);
m_plane.draw_fill();
-
-// if (m_thinkness > 0)
-// {
-// glLineWidth(m_thinkness);
-// ui::ShaderManager::u_vec4(kShaderUniform::Col, m_border_color);
-// m_plane.draw_stroke();
-// }
-
-// if (m_color.a != 1.f)
-// glDisable(GL_BLEND);
}
};
+
+class NodePanelColor : public Node
+{
+public:
+ NodeColorQuad* m_quad;
+ NodeSliderHue* m_hue;
+ glm::vec4 m_color;
+ std::function on_color_changed;
+ virtual Node* clone_instantiate() const override { return new NodePanelColor(); }
+ virtual void clone_finalize(Node* dest) const override
+ {
+ NodePanelColor* n = static_cast(dest);
+ n->init_controls();
+ }
+ virtual void init() override
+ {
+ const auto& m_template = (Node*)init_template("tpl-panel-color");
+ init_controls();
+ }
+ void init_controls()
+ {
+ m_quad = find("quad");
+ m_hue = find("hue");
+ m_hue->on_value_changed = [this](Node*, glm::vec4 color) {
+ m_color = m_quad->m_color = color;
+ if (on_color_changed)
+ on_color_changed(this, color);
+ };
+ m_quad->on_value_changed = [this](Node*, glm::vec2 pos)
+ {
+ auto x = glm::mix(m_color, glm::vec4(1, 1, 1, 1), pos.x);
+ auto color = glm::mix(x, glm::vec4(0, 0, 0, 1), pos.y);
+ if (on_color_changed)
+ on_color_changed(this, color);
+ };
+ }
+};
+
+class NodeCanvas2D : public NodeBorder
+{
+ RTT m_rtt;
+ Sampler m_sampler;
+ static ui::Shader m_shader;
+public:
+ uint16_t m_tex_id;
+ glm::vec4 m_tip_color;
+ float m_tip_size;
+ float m_tip_spacing;
+ float m_tip_flow;
+ float m_tip_angle;
+ float m_jitter_scale;
+ float m_jitter_angle;
+ float m_jitter_spread;
+ float m_jitter_flow;
+ static void static_init()
+ {
+ static const char* shader_v =
+ SHADER_VERSION
+ "uniform mat4 mvp;"
+ "in vec4 pos;"
+ "in vec2 uvs;"
+ "out vec3 uv;"
+ "void main(){"
+ " uv = vec3(uvs, pos.w);"
+ " gl_Position = mvp * vec4(pos.xyz, 1.f);"
+ "}";
+ static const char* shader_f =
+ SHADER_VERSION
+ "uniform sampler2D tex;"
+ "uniform vec4 col;"
+ "uniform float alpha;"
+ "in vec3 uv;"
+ "out vec4 frag;"
+ "void main(){"
+ " float a = (1 - texture(tex, uv.xy).r) * alpha;"
+ " frag = vec4(col.rgb, a);"
+ "}";
+ m_shader.create(shader_v, shader_f);
+ }
+ virtual Node* clone_instantiate() const override { return new NodeCanvas2D(); }
+ virtual void clone_copy(Node* dest) const override
+ {
+ NodeBorder::clone_copy(dest);
+ }
+ virtual void clone_children(Node* dest) const override
+ {
+ // stop children cloning
+ }
+ virtual void clone_finalize(Node* dest) const override
+ {
+ NodeCanvas2D* n = (NodeCanvas2D*)dest;
+ n->init_controls();
+ }
+ void init_controls()
+ {
+ m_sampler.create();
+ TextureManager::load("data/Icons/Round-Hard.png");
+ m_tex_id = const_hash("data/Icons/Round-Hard.png");
+ }
+ void draw_stroke()
+ {
+ m_rtt.bindFramebuffer();
+ {
+ using namespace ui;
+ GLint vp[4];
+ GLfloat cc[4];
+ glGetIntegerv(GL_VIEWPORT, vp);
+ glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
+
+ double w = (double)m_rtt.getWidth();
+ double h = (double)m_rtt.getHeight();
+ std::vector kp = { {30, 30}, {30, h-30}, {w-30, 30}, { w-30, h-30 } };
+
+ glClearColor(1, 1, 1, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight());
+ glEnable(GL_BLEND);
+ glm::mat4 proj = glm::ortho(0, (float)m_rtt.getWidth(), 0, (float)m_rtt.getHeight(), -1, 1);
+ auto& t = TextureManager::get(m_tex_id);
+ float alpha = 0;
+
+ std::minstd_rand prng;
+ m_shader.use();
+ m_shader.u_vec4(kShaderUniform::Col, m_tip_color);
+ m_shader.u_int(kShaderUniform::Tex, 0);
+ t.bind();
+ m_sampler.bind(0);
+ while (alpha < 1.f)
+ {
+ auto rnd_nor = [&] { return float((double)prng() / (double)prng.max()); }; // normalized [0, +1]
+ auto rnd_neg = [&] { return float((double)prng() / (double)prng.max() * 2.0 - 1.0); }; // normalized [-1, +1]
+ auto rnd_rad = [&] { return float((double)prng() / (double)prng.max() * M_PI * 2.0); }; // normalized [0, 2pi]
+ auto rnd_vec = [&] { float rad = rnd_rad(); return glm::vec2(cosf(rad), sinf(rad)); }; // normalized direction vector
+
+ float angle = (m_tip_angle + rnd_nor() * m_jitter_angle) * (float)(M_PI * 2.0);
+ glm::vec2 pos = BezierCurve::Bezier2D(kp, alpha) + (rnd_vec() * m_jitter_spread * 100.f);
+ float size = 100.f * m_tip_size * (1.f - rnd_nor() * m_jitter_scale);
+ float flow = m_tip_flow * (1.f - rnd_nor() * m_jitter_flow);
+
+ alpha += glm::max(m_tip_spacing * .2f, .01f);
+ auto mvp = proj *
+ //glm::translate(glm::vec3(i * 40 * m_tip_spacing, m_rtt.getHeight() / 2, 0)) *
+ glm::translate(glm::vec3(pos, 0)) *
+ glm::scale(glm::vec3(size, size, 1)) *
+ glm::eulerAngleZ(angle);
+ m_shader.u_mat4(kShaderUniform::MVP, mvp);
+ m_shader.u_float(kShaderUniform::Alpha, flow);
+ m_plane.draw_fill();
+ }
+ m_sampler.unbind();
+ t.unbind();
+ glDisable(GL_BLEND);
+
+ glViewport(vp[0], vp[1], vp[2], vp[3]);
+ glClearColor(cc[0], cc[1], cc[2], cc[3]);
+ }
+ m_rtt.unbindFramebuffer();
+ }
+ virtual void draw() override
+ {
+ using namespace ui;
+ ui::ShaderManager::use(kShader::Texture);
+ ui::ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp);
+ ui::ShaderManager::u_int(kShaderUniform::Tex, 0);
+ m_rtt.bindTexture();
+ m_sampler.bind(0);
+ m_plane.draw_fill();
+ m_sampler.unbind();
+ m_rtt.unbindTexture();
+ }
+ virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size) override
+ {
+ m_rtt.destroy();
+ m_rtt.create((int)new_size.x, (int)new_size.y);
+ draw_stroke();
+ }
+};
+
+class NodePanelStroke : public Node
+{
+public:
+ NodeCanvas2D* m_canvas;
+ NodeSliderH* m_tip_size;
+ NodeSliderH* m_tip_spacing;
+ NodeSliderH* m_tip_flow;
+ NodeSliderH* m_tip_angle;
+ NodeSliderH* m_jitter_scale;
+ NodeSliderH* m_jitter_angle;
+ NodeSliderH* m_jitter_spread;
+ NodeSliderH* m_jitter_flow;
+ virtual Node* clone_instantiate() const override { return new NodePanelStroke(); }
+ virtual void clone_finalize(Node* dest) const override
+ {
+ NodePanelColor* n = static_cast(dest);
+ n->init_controls();
+ }
+ virtual void init() override
+ {
+ const auto& m_template = (Node*)init_template("tpl-panel-stroke");
+ init_controls();
+ }
+ void init_controls()
+ {
+ m_canvas = find("canvas");
+
+ init_slider(m_tip_size, "tip-size", &NodeCanvas2D::m_tip_size);
+ init_slider(m_tip_spacing, "tip-spacing", &NodeCanvas2D::m_tip_spacing);
+ init_slider(m_tip_flow, "tip-flow", &NodeCanvas2D::m_tip_flow);
+ init_slider(m_tip_angle, "tip-angle", &NodeCanvas2D::m_tip_angle);
+ init_slider(m_jitter_scale, "jitter-scale", &NodeCanvas2D::m_jitter_scale);
+ init_slider(m_jitter_angle, "jitter-angle", &NodeCanvas2D::m_jitter_angle);
+ init_slider(m_jitter_spread, "jitter-spread", &NodeCanvas2D::m_jitter_spread);
+ init_slider(m_jitter_flow, "jitter-flow", &NodeCanvas2D::m_jitter_flow);
+ }
+ void init_slider(NodeSliderH*& slider, const char* id, float NodeCanvas2D::* prop)
+ {
+ slider = find(id);
+ slider->on_value_changed = std::bind(&NodePanelStroke::handle_slide,
+ this, prop, std::placeholders::_1, std::placeholders::_2);
+ }
+ void handle_slide(float NodeCanvas2D::* prop, Node* target, float value)
+ {
+ m_canvas->*prop = value;
+ m_canvas->draw_stroke();
+ }
+};
+
+
diff --git a/engine/pch.h b/engine/pch.h
index a760526..5aa7235 100644
--- a/engine/pch.h
+++ b/engine/pch.h
@@ -7,9 +7,11 @@
#include
#include
#define LOG printf
+ #define SHADER_VERSION "#version 300 es\n"
+
#elif __ANDROID__
#include
- #include
+ #include
#include
#include
@@ -17,6 +19,7 @@
#include
#define LOG(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-engine", __VA_ARGS__))
+#define SHADER_VERSION "#version 300 es\n"
#elif _WIN32
#define _USE_MATH_DEFINES
@@ -27,6 +30,7 @@
#include
#define LOG(M,...) printf(M"\n", ##__VA_ARGS__)
+#define SHADER_VERSION "#version 150\n"
#endif
#ifdef __cplusplus
@@ -37,6 +41,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/engine/rtt.cpp b/engine/rtt.cpp
new file mode 100644
index 0000000..b2e7a65
--- /dev/null
+++ b/engine/rtt.cpp
@@ -0,0 +1,134 @@
+#include "pch.h"
+#include "rtt.h"
+
+
+RTT::RTT()
+{
+ fboID = 0;
+ rboID = 0;
+ w = 0;
+ h = 0;
+}
+
+RTT::~RTT()
+{
+ //destroy();
+}
+
+void RTT::destroy()
+{
+ if (rboID)
+ {
+ glDeleteRenderbuffers(1, &rboID);
+ }
+ if (texID)
+ {
+ unbindTexture();
+ glDeleteTextures(1, &texID);
+ }
+ if (fboID)
+ {
+ unbindFramebuffer();
+ glDeleteFramebuffers(1, &fboID);
+ }
+ fboID = 0;
+ rboID = 0;
+ w = 0;
+ h = 0;
+}
+
+bool RTT::create(int width, int height, int tex/* = -1*/)
+{
+ // Destroy any previously created object
+ destroy();
+
+ w = width;
+ h = height;
+
+ if (tex == -1)
+ {
+ glGenTextures(1, &texID);
+ }
+ else
+ {
+ texID = tex;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, texID);
+
+ if (tex == -1)
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, 0);
+
+// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+// glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // Create a renderbuffer object to store depth info
+ glGenRenderbuffers(1, &rboID);
+ glBindRenderbuffer(GL_RENDERBUFFER, rboID);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+
+ // Create a framebuffer object
+ glGenFramebuffers(1, &fboID);
+ glBindFramebuffer(GL_FRAMEBUFFER, fboID);
+
+ // Attach the texture to FBO color attachment point
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texID, 0);
+
+ // Attach the renderbuffer to depth attachment point
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboID);
+
+ // Check FBO status
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ LOG("createColorBuffer failed");
+
+ // Switch back to window-system-provided framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ return status == GL_FRAMEBUFFER_COMPLETE;
+}
+
+void RTT::bindFramebuffer()
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, fboID);
+}
+
+void RTT::unbindFramebuffer()
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+void RTT::clear(glm::vec4 color)
+{
+ glClearColor(color.r, color.g, color.b, color.a);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+void RTT::readTextureData(uint8_t* buffer)
+{
+ bindTexture();
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
+ unbindTexture();
+}
+
+uint8_t* RTT::createBuffer()
+{
+ return new uint8_t[w * h * 3];
+}
+
+void RTT::bindTexture()
+{
+ glBindTexture(GL_TEXTURE_2D, texID);
+}
+
+void RTT::unbindTexture()
+{
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+
diff --git a/engine/rtt.h b/engine/rtt.h
new file mode 100644
index 0000000..0536838
--- /dev/null
+++ b/engine/rtt.h
@@ -0,0 +1,28 @@
+#pragma once
+
+class RTT
+{
+ GLuint fboID;
+ GLuint rboID;
+ GLuint texID;
+ int w;
+ int h;
+
+public:
+ RTT();
+ ~RTT();
+
+ void destroy();
+ bool create(int width, int height, int tex = -1);
+ void clear(glm::vec4 color = glm::vec4(0));
+ void readTextureData(uint8_t* buffer);
+ uint8_t* createBuffer();
+ void bindFramebuffer();
+ void unbindFramebuffer();
+ void bindTexture();
+ void unbindTexture();
+ GLuint getTextureID() { return texID; }
+ int getWidth() { return w; }
+ int getHeight() { return h; }
+ GLuint getFBO() { return fboID; }
+};
diff --git a/engine/shader.cpp b/engine/shader.cpp
index ebaf5c4..8fcfe6d 100644
--- a/engine/shader.cpp
+++ b/engine/shader.cpp
@@ -120,6 +120,10 @@ void Shader::u_int(kShaderUniform id, int i)
{
glUniform1i(m_umap[id], i);
}
+void Shader::u_float(kShaderUniform id, float f)
+{
+ glUniform1f(m_umap[id], f);
+}
bool ShaderManager::create(kShader id, const char* vertex, const char* fragment)
{
@@ -157,3 +161,7 @@ void ShaderManager::u_int(kShaderUniform id, int i)
{
m_current->u_int(id, i);
}
+void ui::ShaderManager::u_float(kShaderUniform id, float f)
+{
+ m_current->u_float(id, f);
+}
\ No newline at end of file
diff --git a/engine/shader.h b/engine/shader.h
index f2f78ba..2263547 100644
--- a/engine/shader.h
+++ b/engine/shader.h
@@ -10,6 +10,7 @@ enum class kShaderUniform : uint16_t
Col = const_hash("col"),
Tof = const_hash("tof"),
Tsz = const_hash("tsz"),
+ Alpha = const_hash("alpha"),
};
class Shader
@@ -23,6 +24,7 @@ public:
void u_vec2(kShaderUniform id, const glm::vec2& v);
void u_mat4(kShaderUniform id, const glm::mat4& m);
void u_int(kShaderUniform id, int i);
+ void u_float(kShaderUniform id, float f);
};
enum class kShader : uint16_t
@@ -48,6 +50,7 @@ public:
static void u_vec2(kShaderUniform id, const glm::vec2& v);
static void u_mat4(kShaderUniform id, const glm::mat4& m);
static void u_int(kShaderUniform id, int i);
+ static void u_float(kShaderUniform id, float f);
};
}
\ No newline at end of file
diff --git a/engine/util.cpp b/engine/util.cpp
index 846a3b7..f247e3e 100644
--- a/engine/util.cpp
+++ b/engine/util.cpp
@@ -13,3 +13,23 @@ glm::vec4 rand_color()
float b = (rand() % 256) / 256.f;
return { r, g, b, 1.f };
}
+
+glm::vec3 convert_hsv2rgb(const glm::vec3 c)
+{
+ glm::vec4 K = glm::vec4(1.0f, 2.0f / 3.0f, 1.0f / 3.0f, 3.0f);
+ glm::vec3 p = glm::abs(glm::fract(glm::vec3(c.xxx + K.xyz)) * 6.0f - K.www);
+ auto tmp = glm::clamp(glm::vec3(p - K.xxx), 0.0f, 1.0f);
+ return c.z * glm::mix(glm::vec3(K.xxx), tmp, c.y);
+}
+
+glm::vec3 convert_rgb2hsv(const glm::vec3 c)
+{
+ glm::vec4 K = glm::vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
+ //glm::vec4 p = mix(glm::vec4(c.bg, K.wz), glm::vec4(c.gb, K.xy), glm::step(c.b, c.g));
+ //glm::vec4 q = mix(glm::vec4(p.xyw, c.r), glm::vec4(c.r, p.yzx), glm::step(p.x, c.r));
+ glm::vec4 p = c.g < c.b ? glm::vec4(c.bg, K.wz) : glm::vec4(c.gb, K.xy);
+ glm::vec4 q = c.r < p.x ? glm::vec4(p.xyw, c.r) : glm::vec4(c.r, p.yzx);
+ float d = q.x - glm::min(q.w, q.y);
+ float e = 1.0e-10f;
+ return glm::vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
+}
diff --git a/engine/util.h b/engine/util.h
index 74740a1..5dc431c 100644
--- a/engine/util.h
+++ b/engine/util.h
@@ -9,3 +9,5 @@ uint16_t constexpr const_hash(const char* input)
bool point_in_rect(const glm::vec2& point, const glm::vec4& rect);
glm::vec4 rand_color();
+glm::vec3 convert_hsv2rgb(const glm::vec3 c);
+glm::vec3 convert_rgb2hsv(const glm::vec3 c);