From a2cb0ecafe375b70201e78dea6088e1cc8c5c90c Mon Sep 17 00:00:00 2001 From: omigamedev Date: Fri, 24 Mar 2017 03:15:29 +0000 Subject: [PATCH] added render target and bezier classes, added stroke settings panel --- data/layout.xml | 63 ++++++- engine.vcxproj | 7 + engine.vcxproj.filters | 15 ++ engine/app.cpp | 42 +++-- engine/app.h | 6 +- engine/bezier.cpp | 39 ++++ engine/bezier.h | 66 +++++++ engine/layout.cpp | 14 +- engine/layout.h | 411 +++++++++++++++++++++++++++++++++++++---- engine/pch.h | 7 +- engine/rtt.cpp | 134 ++++++++++++++ engine/rtt.h | 28 +++ engine/shader.cpp | 8 + engine/shader.h | 3 + engine/util.cpp | 20 ++ engine/util.h | 2 + 16 files changed, 798 insertions(+), 67 deletions(-) create mode 100644 engine/bezier.cpp create mode 100644 engine/bezier.h create mode 100644 engine/rtt.cpp create mode 100644 engine/rtt.h 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);